From 4c2dfcf3e9d78642d4f3ca70a65c5c0b00e59001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sat, 22 Aug 2026 19:29:22 -0300 Subject: [PATCH] test(genesis): cover shared verification helpers --- .github/workflows/genesis-openembedded.yml | 2 + t/build_utils.t | 82 ++++++++++++++++++++++ t/genesis_openembedded_consumer.t | 4 +- t/genesis_openembedded_release.t | 30 ++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 t/build_utils.t diff --git a/.github/workflows/genesis-openembedded.yml b/.github/workflows/genesis-openembedded.yml index 25fc684..25171d1 100644 --- a/.github/workflows/genesis-openembedded.yml +++ b/.github/workflows/genesis-openembedded.yml @@ -45,6 +45,7 @@ jobs: lib/XCAT/BuildUtils.pm \ lib/XCAT/GenesisRelease.pm \ mockbuild-all.pl \ + t/build_utils.t \ t/genesis_openembedded_release.t \ t/genesis_openembedded_consumer.t \ t/lib/XCAT/GenesisReleaseTest.pm @@ -53,5 +54,6 @@ jobs: - name: Run package tests run: | + prove -v t/build_utils.t prove -v -It/lib t/genesis_openembedded_release.t sudo -E prove -v -It/lib t/genesis_openembedded_consumer.t diff --git a/t/build_utils.t b/t/build_utils.t new file mode 100644 index 0000000..af8aad8 --- /dev/null +++ b/t/build_utils.t @@ -0,0 +1,82 @@ +use strict; +use warnings; + +use File::Path qw(make_path); +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +use lib "$FindBin::Bin/../lib"; +use XCAT::BuildUtils qw( + capture_command + command_exists + digest_file + digest_manifest + display_quote + hashes_equal + read_binary + read_first_line + read_lines + relative_files + require_command + run_command + shell_quote + write_binary +); + +my $tmp = tempdir(CLEANUP => 1); +write_binary("$tmp/alpha", "abc"); +make_path("$tmp/nested"); +write_binary("$tmp/nested/lines", "first\r\nsecond\n"); + +is(read_binary("$tmp/alpha"), 'abc', 'binary files round trip'); +is_deeply( + [ read_lines("$tmp/nested/lines") ], + [ 'first', 'second' ], + 'line reader accepts CRLF and LF', +); +is(read_first_line("$tmp/nested/lines"), 'first', 'first line is returned'); +is_deeply( + [ relative_files($tmp) ], + [ 'alpha', 'nested/lines' ], + 'regular files are sorted and relative', +); + +my $sha256 = 'ba7816bf8f01cfea414140de5dae2223' + . 'b00361a396177a9cb410ff61f20015ad'; +is(digest_file("$tmp/alpha", 'sha256'), $sha256, 'SHA-256 matches a known value'); +is(digest_file("$tmp/alpha", 'md5'), '900150983cd24fb0d6963f7d28e17f72', + 'MD5 matches a known value'); +is( + digest_manifest($tmp, 'sha256', 'alpha'), + "$sha256 alpha\n", + 'digest manifest uses the release format', +); + +ok(command_exists($^X), 'current Perl interpreter is executable'); +is(capture_command($^X, '-e', 'print "captured\\n"'), 'captured', + 'command output is captured without a shell'); +ok(run_command($^X, '-e', 'exit 0'), 'successful command returns true'); + +eval { run_command($^X, '-e', 'exit 7'); 1 }; +like($@, qr/Command failed \(rc=7\)/, 'command failure reports its exit status'); +eval { require_command("xcat-missing-command-$$"); 1 }; +like($@, qr/Required command not found/, 'missing command is rejected'); +eval { digest_file("$tmp/alpha", 'unknown'); 1 }; +like($@, qr/Unsupported digest algorithm/, 'unknown digest is rejected'); + +is(display_quote('plain/value'), 'plain/value', 'simple display value is unquoted'); +is(display_quote('two words'), q{'two words'}, 'display value with spaces is quoted'); +is(shell_quote("it's"), q{'it'"'"'s'}, 'shell quote escapes apostrophes'); +ok(hashes_equal({ a => 1 }, { a => 1 }), 'equal hashes match'); +ok(!hashes_equal({ a => 1 }, { a => 2 }), 'different hashes do not match'); + +my $link = "$tmp/link"; +if (symlink('alpha', $link)) { + eval { relative_files($tmp); 1 }; + like($@, qr/Symbolic links are not allowed/, 'directory walk rejects symlinks'); +} else { + fail('test filesystem supports symbolic links'); +} + +done_testing(); diff --git a/t/genesis_openembedded_consumer.t b/t/genesis_openembedded_consumer.t index cdefb70..f25f346 100644 --- a/t/genesis_openembedded_consumer.t +++ b/t/genesis_openembedded_consumer.t @@ -59,7 +59,7 @@ SKIP: { } SKIP: { - skip 'APT repository tools are not installed', 12 + skip 'APT repository tools are not installed', 13 unless $^O eq 'linux' && command_exists('bash') && command_exists('dpkg-deb') @@ -198,6 +198,8 @@ sub test_deb_consumer { my $ppc64el = "$apt_root/dists/noble/main/binary-ppc64el/Packages"; is($status, 0, 'APT repository accepts a verified Genesis release'); + like(read_binary($log), qr/Verified copied Genesis package:/, + 'APT repository uses the shared copied-package verifier'); is(digest_file($pool_package), digest_file("$release_root/deb/$package"), 'pooled DEB matches the release'); ok(!-e "$apt_root/pool/main/noble/xcat-genesis-openembedded-old.deb", diff --git a/t/genesis_openembedded_release.t b/t/genesis_openembedded_release.t index 46dcab3..c2c0afa 100644 --- a/t/genesis_openembedded_release.t +++ b/t/genesis_openembedded_release.t @@ -138,6 +138,36 @@ dies_like( qr/Collected release file checksum mismatch/, 'release changes after verification are rejected', ); +copy("$release_dir/$verified_relative", $verified_copy) or die $!; +my $copied_file_log = "$tmp/copied-file.log"; +is( + run_capture( + $copied_file_log, + $verifier, + '--checksum-file', "$release_dir/SHA256SUMS", + '--relative-file', $verified_relative, + '--copied-file', $verified_copy, + ), + 0, + 'verifier accepts a copied release file', +); +write_binary($verified_copy, 'changed after verification'); +isnt( + run_capture( + $copied_file_log, + $verifier, + '--checksum-file', "$release_dir/SHA256SUMS", + '--relative-file', $verified_relative, + '--copied-file', $verified_copy, + ), + 0, + 'verifier rejects a changed copied file', +); +like( + read_binary($copied_file_log), + qr/Collected release file checksum mismatch/, + 'copied-file failure names the checksum mismatch', +); dies_like( sub { validate_complete_release($release_dir) }, qr/Genesis release is missing supported architectures/,