App-FuguWeb
view release on metacpan or search on metacpan
t/fuguweb/rotate.t view on Meta::CPAN
like( $error, qr/0 current keys/, 'and the status rules say why' );
ok( !-e "$root/rel2.sec", 'and it generates no pair' );
};
subtest 'a trailing comment keeps its line' => sub {
my $root = _keyed();
my ($second) = _mint(
$root,
secret => "$root/rel2.sec",
signer => "$root/root1.sec"
);
ok( $second, 'the second mint succeeds' ) or return;
# Fugu::Config takes a comment behind a value, so the rewrite
# must take one too. A writer that refused it would refuse a
# description that the reader accepts.
my $rc = Fugu::File->read("$root/.fuguwebrc");
$rc =~ s/(key "fugubsd-1-release" \{\n\tstatus = current)/$1\t# the live key/
or die 'the fixture adds no comment';
Fugu::File->write( "$root/.fuguwebrc", $rc );
my ( $facts, $error ) = _promote(
$root,
signer => "$root/root1.sec",
retiring => "$root/rel1.sec"
);
ok( $facts, 'the promote succeeds' ) or diag($error);
return unless $facts;
$rc = Fugu::File->read("$root/.fuguwebrc");
like( $rc, qr/status = retired\t\# the live key/,
'the comment stands behind the new value' );
is_deeply( [ _problems($root) ], [], 'the reader reports no problem' );
};
subtest 'an organization word that no key name carries fails' => sub {
my $root = _site();
# Fugu::KeyDir dies on such a word, and a caller of the
# command reads a reason and an exit code.
my $reason;
my $config = App::FuguWeb::Config->load( root => $root,
error => \$reason )
or die "load: $reason\n";
my $rotate = App::FuguWeb::Rotate->new(
config => $config,
org => 'Fugu BSD',
bootstrap => 1,
);
ok( !$rotate->mint( purpose => 'root', secret => "$root/k.sec" ),
'the mint fails' );
like( $rotate->error, qr/the organization word/,
'and the reason names the word' );
};
subtest 'the key directory word names one directory' => sub {
my $root = _site();
# WEB-ROTATE-21. A word that held a solidus would write the
# key outside the source directory.
for my $dir ( '../escaped', 'a/b', '..', '.' ) {
my $reason;
my $config = App::FuguWeb::Config->load( root => $root,
error => \$reason )
or die "load: $reason\n";
my $rotate = App::FuguWeb::Rotate->new(
config => $config,
org => $ORG,
dir => $dir,
bootstrap => 1,
);
ok(
!$rotate->mint(
purpose => 'root',
secret => "$root/k.sec"
),
"the word $dir fails the mint"
);
like( $rotate->error, qr/is not one name/,
'and the reason says why' );
}
ok( !-e "$root/escaped", 'and no directory stands outside the source' );
};
# WEB-ROTATE-21. The directory word selects the key directory that a
# step writes. A description with one keys block needs none, and every
# subtest above reads that.
subtest 'a step names the key directory that it writes' => sub {
my $root = _keyed();
# The description holds no block of the second directory yet,
# so the mint takes the intent, the name, the organization word
# and the prefix, per WEB-ROTATE-15 and WEB-ROTATE-22.
my $reason;
my $config =
App::FuguWeb::Config->load( root => $root, error => \$reason )
or die "load: $reason\n";
my $second = App::FuguWeb::Rotate->new(
config => $config,
dir => 'other',
org => 'other',
url => 'https://www.example.net/other',
bootstrap => 1,
);
ok(
$second->mint(
purpose => 'root',
secret => "$root/other1.sec"
),
'the first root mint of a second directory succeeds'
) or diag( $second->error );
ok( -f "$root/web/other/other-1-root.pub",
'the key lands in the directory that the caller named' );
ok( -f "$root/web/other/SHA256", 'with a manifest of its own' );
ok( !-e "$root/web/keys/other-1-root.pub",
'and never in the first directory' );
my $both =
App::FuguWeb::Config->load( root => $root, error => \$reason )
or die "load: $reason\n";
is_deeply( [ $both->keys_dirs ],
[ 'keys', 'other' ], 'the description names both directories' );
# A description with several blocks names no one directory, so
# a step that names none refuses before it writes.
my $blind = App::FuguWeb::Rotate->new( config => $both );
ok(
!$blind->mint(
purpose => 'release',
secret => "$root/blind.sec",
signer => "$root/root1.sec"
),
'a step that names no directory fails'
);
like(
$blind->error,
qr{the description holds the key directories keys and other, so the step needs --dir},
'and the reason names each directory'
);
ok( !-e "$root/blind.sec", 'and the step writes no private half' );
t/fuguweb/rotate.t view on Meta::CPAN
'a mint of a directory that no keys block names fails'
);
like(
$typo->error,
qr{the description names no key directory keyz, and a mint or an import makes one with --bootstrap},
'and the reason names the word that makes one'
);
# The refusal comes before the first write, so a mistyped word
# leaves no directory and no key behind.
ok( !-e "$root/web/keyz", 'and the source tree holds no such'
. ' directory' );
ok( !-e "$root/typo.sec", 'and the step writes no private half' );
unlike( Fugu::File->read("$root/.fuguwebrc"),
qr/keyz/, 'and the description names no second block' );
# The organization word reaches Fugu::KeyDir before the step
# makes the directory, so a bootstrap that names none leaves no
# empty directory either.
my $nameless = App::FuguWeb::Rotate->new(
config => $config,
dir => 'other',
bootstrap => 1,
);
ok(
!$nameless->mint(
purpose => 'root',
secret => "$root/other.sec"
),
'a bootstrap with no organization word fails'
);
like( $nameless->error, qr/needs the organization word/,
'and the reason names the word' );
ok( !-e "$root/web/other",
'and the source tree holds no empty directory' );
# The same step with the intent and the word makes the
# directory.
my $asked = App::FuguWeb::Rotate->new(
config => $config,
dir => 'other',
org => 'other',
bootstrap => 1,
);
ok(
$asked->mint(
purpose => 'root',
secret => "$root/other.sec"
),
'a mint that states the intent succeeds'
) or diag( $asked->error );
ok( -f "$root/web/other/other-1-root.pub",
'and the key lands in the new directory' );
};
# WEB-ROTATE-21 and WEB-KEYS-29. Every verb holds the word to one
# directory below the source directory.
subtest 'a promote names one key directory' => sub {
my $root = _keyed();
for my $dir ( '../escaped', 'a/b', '..', '.' ) {
my $reason;
my $config = App::FuguWeb::Config->load( root => $root,
error => \$reason )
or die "load: $reason\n";
my $rotate = App::FuguWeb::Rotate->new(
config => $config,
dir => $dir,
);
ok(
!$rotate->promote(
purpose => 'release',
retiring => "$root/rel1.sec"
),
"the word $dir fails the promote"
);
like( $rotate->error, qr/is not one name/,
'and the reason says why' );
}
ok( !-e "$root/escaped", 'and no directory stands outside the source' );
};
subtest 'an absent signify takes the code of a missing tool' => sub {
my $root = _site();
# WEB-ROTATE-17. A caller tells a tool that it must install
# from a step that failed, as it does for a renderer.
my ( $exit, $out, $err ) = do {
local $ENV{PATH} = '/nonexistent';
_run(
'--project', $root, 'mint-key',
'--purpose', 'root',
'--secret', "$root/root1.sec",
'--org', $ORG,
'--bootstrap',
);
};
is( $exit, 6, 'the command takes the missing tool code' );
like( $err, qr/signify/, 'and the reason names the command' );
ok( !-e "$root/root1.sec", 'and it writes no private half' );
};
subtest 'the verbs guard their options and print their facts' => sub {
my $root = _site();
# WEB-ROTATE-1. Each verb names the options that it needs.
my %need = (
'mint-key' => [qw(purpose secret)],
'import-key' => [qw(purpose secret file)],
'promote-key' => [qw(purpose retiring)],
);
for my $verb ( sort keys %need ) {
for my $missing ( @{ $need{$verb} } ) {
my %opt = map { $_ => "$root/value" }
@{ $need{$verb} };
$opt{purpose} = 'release';
delete $opt{$missing};
my ( $exit, $out, $err ) =
_run( '--project', $root, $verb,
map { ( "--$_", $opt{$_} ) } sort keys %opt );
is( $exit, 2,
"$verb: an absent --$missing takes the"
. ' argument code' );
like( $err, qr/--\Q$missing\E is a necessary option/,
'and the reason names it' );
}
}
# WEB-ROTATE-13. One name=value line for each fact, so a
# caller appends the output to a file that its steps read.
my ( $exit, $out, $err ) = _run(
'--project', $root, 'mint-key',
'--purpose', 'root',
'--secret', "$root/root1.sec",
'--org', $ORG,
'--url', $URL,
'--bootstrap',
);
( run in 1.782 second using v1.01-cache-2.11-cpan-54e63673c56 )