summaryrefslogtreecommitdiff
path: root/tests/compare.rs
blob: b11d288ac7df40dc11d0462722dbd058692eda3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use alt_controller_image::compare::{Change, SemanticDifference, compare};
use alt_controller_image::archive::NativeTarWriter;
use alt_controller_image::manifest::{
    ArchiveMemberRecord, ArtifactManifest, FileRecord, InitrdRecord, PackageRecord, ServiceRecord,
};
use std::fs;
use std::os::unix::fs::symlink;
use tempfile::tempdir;

fn baseline() -> ArtifactManifest {
    ArtifactManifest::new(
        vec![PackageRecord::new("kernel-image-rt", "1.0")],
        vec![FileRecord::file("etc/controller.conf", "aaa")],
        Some(InitrdRecord::new("boot/initrd-rt.img", "old-initrd")),
        vec![ServiceRecord::new("controller.service", true)],
        vec![ArchiveMemberRecord::new("etc/controller.conf", "file")],
    )
    .expect("valid baseline manifest")
}

#[test]
fn compares_added_removed_and_changed_semantic_records() {
    let left = baseline();
    let right = ArtifactManifest::new(
        vec![
            PackageRecord::new("kernel-image-rt", "2.0"),
            PackageRecord::new("controller-agent", "1.0"),
        ],
        vec![FileRecord::symlink("etc/controller.conf", "controller.conf.real")],
        Some(InitrdRecord::new("boot/initrd-rt.img", "new-initrd")),
        vec![ServiceRecord::new("controller.service", false)],
        vec![ArchiveMemberRecord::new("usr/bin/controller", "file")],
    )
    .expect("valid comparison manifest");

    let report = compare(&left, &right);

    assert_eq!(
        report.differences(),
        &[
            SemanticDifference::Package { name: "controller-agent".into(), change: Change::Added },
            SemanticDifference::Package { name: "kernel-image-rt".into(), change: Change::Changed },
            SemanticDifference::File { path: "etc/controller.conf".into(), change: Change::Changed },
            SemanticDifference::Initrd { change: Change::Changed },
            SemanticDifference::Service { name: "controller.service".into(), change: Change::Changed },
            SemanticDifference::ArchiveMember { path: "etc/controller.conf".into(), change: Change::Removed },
            SemanticDifference::ArchiveMember { path: "usr/bin/controller".into(), change: Change::Added },
        ]
    );
    assert!(report.render().contains("changed initrd"));
}

#[test]
fn reads_and_writes_a_toml_manifest_beside_an_artifact() {
    let fixture = tempdir().expect("temporary directory");
    let artifact = fixture.path().join("controller.tar");
    let manifest = baseline();

    let manifest_path = manifest.write_beside(&artifact).expect("write manifest");
    assert_eq!(manifest_path, fixture.path().join("artifact.manifest.toml"));
    assert_eq!(ArtifactManifest::load(&manifest_path).expect("load manifest"), manifest);
}

#[test]
fn rejects_duplicate_semantic_keys() {
    let error = ArtifactManifest::new(
        vec![PackageRecord::new("controller", "1"), PackageRecord::new("controller", "2")],
        vec![],
        None,
        vec![],
        vec![],
    )
    .expect_err("duplicate package names must fail");

    assert!(error.to_string().contains("duplicate package record"));
}

#[test]
fn collects_semantic_facts_from_native_rootfs_and_tar_artifact() {
    let fixture = tempdir().expect("temporary directory");
    let rootfs = fixture.path().join("rootfs");
    fs::create_dir_all(rootfs.join("etc/systemd/system/multi-user.target.wants"))
        .expect("create service state directory");
    fs::write(rootfs.join("etc/controller.conf"), "controller\n").expect("write file");
    symlink("controller.conf", rootfs.join("etc/controller-link")).expect("write symlink");
    symlink(
        "/usr/lib/systemd/system/controller.service",
        rootfs.join("etc/systemd/system/multi-user.target.wants/controller.service"),
    )
    .expect("enable service");
    let artifact = fixture.path().join("controller.tar");
    NativeTarWriter::new()
        .write(&rootfs, &artifact)
        .expect("write native tar");

    let manifest = ArtifactManifest::collect(
        &rootfs,
        vec![PackageRecord::new("controller", "1.0")],
        Some(InitrdRecord::new("boot/initrd-rt.img", "digest")),
        &artifact,
    )
    .expect("collect semantic facts");

    assert_eq!(manifest.packages, vec![PackageRecord::new("controller", "1.0")]);
    assert!(manifest.files.iter().any(|record| record == &FileRecord::file(
        "etc/controller.conf",
        "2d5c759b2b539229e09d362e8dbe0ae410ff8c9ece6038624458724520683f5b",
    )));
    assert!(manifest
        .files
        .iter()
        .any(|record| record == &FileRecord::symlink("etc/controller-link", "controller.conf")));
    assert_eq!(manifest.services, vec![ServiceRecord::new("controller.service", true)]);
    assert!(manifest
        .archive_members
        .iter()
        .any(|record| record == &ArchiveMemberRecord::new("etc/controller.conf", "file")));
    assert!(manifest
        .archive_members
        .iter()
        .any(|record| record == &ArchiveMemberRecord::new("etc/controller-link", "symlink")));
}