view release on metacpan or search on metacpan
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
change `config-encoding` and `journal-encoding`.
Notes:
* Before switching `config-encoding` and `journal-encoding` you are responsible for transcoding file content of config and journal files manually.
* You are responsible for encoding compatibility. For example Don't try to work with UTF-8 journal with non-Cyrilic characters and KOI8-R (Cyrilic) filesystem.
* Don't try to use UTF-16 for *nix filesystem. It's not ASCII compatible and contains \x00 bytes, which can't be stored in filesystem.
* Don't use `UTF8` - it does not validate data, use `UTF-8` (one with a dash) instead.
* To get list of encodings installed with your Perl run:
perl -MEncode -e 'print join qq{\n}, Encode->encodings(q{:all})'
* Config file name (specified with `--config`) can be in any encoding (it's used as is) Of course it will work only if your terminal encoding match your
filesystem encoding or if your config file name consists of ASCII-7bit characters only.
* Additional information about encoding support in Perl programming language: [CPAN module Encode::Supported](http://search.cpan.org/perldoc?Encode%3A%3ASupported)
lib/App/MtAws/ConfigDefinition.pm view on Meta::CPAN
if (present('filename')) {
custom('data-type', 'filename'), mandatory('filename'), do {
if (present('set-rel-filename')) {
if (present('dir')) {
error('mutual', a => seen('set-rel-filename'), b => seen('dir'));
} else {
custom('name-type', 'rel-filename'), mandatory('set-rel-filename'), custom('relfilename', value('set-rel-filename'));
}
} elsif (present('dir')) {
custom('relfilename', do {
validate 'dir', 'filename';
if (valid('dir') && valid('filename')) {
my $b_dir = binary_abs_path(binaryfilename value('dir'));
my $b_file = binary_abs_path(binaryfilename value('filename'));
if (!defined $b_dir) {
error(message('cannot_resolve_dir',
'Directory specified with "%option a%" cannot be resolved to full path'),
a => 'dir'), undef
} elsif (!defined $b_file) {
lib/App/MtAws/ConfigDefinition.pm view on Meta::CPAN
/^[A-Za-z0-9\.\-_]{1,255}$/
};
{
my $fh = 'for-humans';
my $mt = 'mtmsg';
validation(option('format', default => $fh),
message(qq{%option a% must be "$fh" or "$mt"}), sub { /^(\Q$fh\E|\Q$mt\E)$/ });
}
command 'create-vault' => sub { validate(optional('config'), mandatory(@encodings), mandatory('vault-name'), mandatory(@config_opts), check_https)};
command 'delete-vault' => sub { validate(optional('config'), mandatory(@encodings), mandatory('vault-name'), mandatory(@config_opts), check_https)};
command 'list-vaults' => sub {
validate(optional('config'), mandatory(@encodings), optional('dry-run'), mandatory(@config_opts), check_https, mandatory('format'))
};
command 'sync' => sub {
validate(mandatory(
optional('config'), mandatory(@encodings), @config_opts, sync_opts, detect_opts, check_https,
qw/dir vault concurrency partsize/, writable_journal('journal'),
optional(qw/max-number-of-files leaf-optimization follow/),
filter_options, optional('dry-run')
))
};
command 'upload-file' => sub {
validate(mandatory( optional('config'), mandatory(@encodings), @config_opts, check_https, qw/vault concurrency/, writable_journal('journal'),
check_dir_or_relname, check_base_dir, mandatory('partsize'), check_max_size ))
};
command 'purge-vault' => sub {
validate(mandatory(
optional('config'), mandatory(@encodings), @config_opts, check_https, qw/vault concurrency/,
writable_journal(existing_journal('journal')),
deprecated('dir'), filter_options, optional('dry-run')
))
};
command 'restore' => sub {
validate(mandatory(
optional('config'), mandatory(@encodings), @config_opts, check_https, qw/dir vault max-number-of-files concurrency/,
writable_journal(existing_journal('journal')),
filter_options, optional('dry-run')
))
};
command 'restore-completed' => sub {
validate(mandatory(
optional('config'), mandatory(@encodings), @config_opts, check_https, qw/dir vault concurrency/, existing_journal('journal'),
filter_options, optional('dry-run'), http_download_options
))
};
command 'check-local-hash' => sub {
validate(mandatory(
optional('config'), mandatory(@encodings), @config_opts, check_https, qw/dir/, existing_journal('journal'), deprecated('vault'),
filter_options, optional('dry-run')
))
};
command 'retrieve-inventory' => sub {
validate(mandatory(optional('config'), mandatory(@encodings), 'request-inventory-format', @config_opts, check_https, qw/vault/))
};
command 'download-inventory' => sub {
validate(mandatory(optional('config'), mandatory(@encodings), @config_opts, check_https, 'vault', empty_journal('new-journal')))
};
});
return $c;
}
1;
__END__
lib/App/MtAws/ConfigEngine.pm view on Meta::CPAN
use List::Util qw/first/;
use strict;
use warnings;
use utf8;
use App::MtAws::Exceptions;
use App::MtAws::Utils;
use Exporter 'import';
our @EXPORT = qw/option options positional command validation message
mandatory optional seen deprecated validate scope
present valid value lists raw_option custom error warning impose explicit/;
our $context; # it's a not a global. always localized in code
# TODOS
#refactor messages %option a% vs %option option%
#options_encoding_error specify source of problem
sub message($;$%)
{
lib/App/MtAws/ConfigEngine.pm view on Meta::CPAN
assert_option;
my $opt = $context->{options}->{ seen() };
confess "positional options can't be deprecated" if $opt->{positional};
if (defined $opt->{value}) {
warning('option_deprecated_for_command', a => _real_option_name $opt) if $opt->{source} eq 'option';
undef $opt->{value};
}
$_;
} @_;
};
sub validate(@)
{
return map {
my $opt = $context->{options}->{seen()};
if (defined($opt->{value}) && !$opt->{validated}) {
$opt->{validated} = $opt->{valid} = 1;
VALIDATION: for my $v (@{ $opt->{validations} }) {
for ($opt->{value}) {
error ({ format => $v->{message}, a => _real_option_name $opt, value => $_}),
$opt->{valid} = 0,
$v->{stop} && last VALIDATION
unless $v->{cb}->();
}
}
};
$_;
lib/App/MtAws/ConfigEngine.pm view on Meta::CPAN
sub explicit(@) # TODO: test that it works with arrays
{
my $name = @_ ? shift : $_;
return present($name) && $context->{options}->{$name}->{source} eq 'option'
};
sub valid($)
{
my ($name) = @_;
assert_option for $name;
confess "validation not performed yet" unless $context->{options}->{$name}->{validated};
return $context->{options}->{$name}->{valid};
};
sub value($)
{
my ($name) = @_;
assert_option for $name;
confess "option not present" unless defined($context->{options}->{$name}->{value});
return $context->{options}->{$name}->{value};
};
lib/App/MtAws/GlacierRequest.pm view on Meta::CPAN
use App::MtAws::SHAHash qw/large_sha256_hex/;
use Carp;
sub new
{
my ($class, $options) = @_;
my $self = {};
bless $self, $class;
defined($self->{$_} = $options->{$_})||confess $_ for (qw/region key secret protocol timeout/);
defined($options->{$_}) and $self->{$_} = $options->{$_} for (qw/vault token/); # TODO: validate vault later
confess unless $self->{protocol} =~ /^https?$/; # we check external data here, even if it's verified in the beginning, especially if it's used to construct URL
$self->{service} ||= 'glacier';
$self->{account_id} = '-';
$self->{host} = "$self->{service}.$self->{region}.amazonaws.com";
$self->{headers} = [];
$self->add_header('Host', $self->{host});
$self->add_header('x-amz-glacier-version', '2012-06-01') if $self->{service} eq 'glacier';
t/integration/config_engine_definitions.t view on Meta::CPAN
no warnings 'redefine';
# validation
{
my $c = create_engine();
$c->define(sub {
option('myoption');
validation 'myoption', message('too_high', "%option a% should be less than 30"), sub { $_ < 30 };
command 'mycommand' => sub { validate(optional('myoption')), ok !valid('myoption'); };
});
my $res = $c->parse_options('mycommand', '-myoption', 31);
cmp_deeply $res->{error_texts}, [q{"--myoption" should be less than 30}], "validation should work";
cmp_deeply $res->{errors}, [{format => 'too_high', a => 'myoption', value => 31}], "validation should work";
}
{
my $c = create_engine();
$c->define(sub {
option('myoption', alias => 'old');
validation 'myoption', message('too_high', "%option a% should be less than 30"), sub { $_ < 30 };
command 'mycommand' => sub { validate optional('myoption') };
});
my $res = $c->parse_options('mycommand', '-old', 31);
cmp_deeply $res->{error_texts}, [q{"--old" should be less than 30}], "validation should work with alias";
cmp_deeply $res->{errors}, [{format => 'too_high', a => 'old', value => 31}], "validation should work with alias";
}
{
my $c = create_engine();
$c->define(sub {
option('myoption', deprecated => 'old');
validation 'myoption', message('too_high', "%option a% should be less than 30"), sub { $_ < 30 };
command 'mycommand' => sub { validate optional('myoption') };
});
my $res = $c->parse_options('mycommand', '-old', 31);
cmp_deeply $res->{error_texts}, [q{"--old" should be less than 30}], "validation should work with deprecated";
cmp_deeply $res->{errors}, [{format => 'too_high', a => 'old', value => 31}], "validation should work with deprecated";
}
{
my $c = create_engine();
$c->define(sub {
validation option('myoption'), message('too_high', "%option a% should be less than 30"), sub { $_ < 30 };
command 'mycommand' => sub { validate(optional('myoption')), ok !valid('myoption'); };
});
my $res = $c->parse_options('mycommand', '-myoption', 31);
cmp_deeply $res->{error_texts}, [q{"--myoption" should be less than 30}], "validation should work with option inline";
cmp_deeply $res->{errors}, [{format => 'too_high', a => 'myoption', value => 31}], "validation should work with option inline";
}
{
my $c = create_engine(override_validations => { myoption => undef });
$c->define(sub {
validation option('myoption'), message('too_high', "%option a% should be less than 30"), sub { $_ < 30 };
command 'mycommand' => sub { validate(optional('myoption')), ok valid('myoption'); };
});
my $res = $c->parse_options('mycommand', '-myoption', 31);
ok !defined($res->{errors} || $res->{error_texts});
}
{
my $c = create_engine();
$c->define(sub {
ok ! defined eval { validation 'myoption', message('too_high', "%option a% should be less than 30"), sub { $_ < 30 }; 1; },
"validation should die if option undeclared"
});
}
{
my $c = create_engine();
$c->define(sub {
validation option('myoption'), message('too_high', "%option a% should be less than 30"), stop => 1, sub { $_ < 30 };
validation 'myoption', message('way_too_high', "%option a% should be less than 100 for sure"), sub { $_ < 100 };
command 'mycommand' => sub { validate optional('myoption') };
});
my $res = $c->parse_options('mycommand', '-myoption', 200);
cmp_deeply $res->{error_texts}, [q{"--myoption" should be less than 30}], "should not perform two validations";
cmp_deeply $res->{errors}, [{format => 'too_high', a => 'myoption', value => 200}], "should not perform two validations";
}
{
my $c = create_engine();
$c->define(sub {
validation option('myoption'), message('too_high', "%option a% should be less than 30"), stop => 0, sub { $_ < 30 };
validation 'myoption', message('way_too_high', "%option a% should be less than 100 for sure"), sub { $_ < 100 };
command 'mycommand' => sub { validate optional('myoption') };
});
my $res = $c->parse_options('mycommand', '-myoption', 200);
cmp_deeply $res->{error_texts}, [q{"--myoption" should be less than 30}, q{"--myoption" should be less than 100 for sure}],
"should perform two validations";
cmp_deeply $res->{errors}, [{format => 'too_high', a => 'myoption', value => 200},
{format => 'way_too_high', a => 'myoption', value => 200}], "should perform two validations";
}
{
my $c = create_engine();
$c->define(sub {
validation option('myoption'), message('way_too_high', "%option a% should be less than 100 for sure"), sub { $_ < 100 };
validation 'myoption', message('too_high', "%option a% should be less than 30"), sub { $_ < 30 };
command 'mycommand' => sub { validate optional('myoption') };
});
my $res = $c->parse_options('mycommand', '-myoption', 42);
cmp_deeply $res->{error_texts}, [q{"--myoption" should be less than 30}], "should perform 2nd validation";
cmp_deeply $res->{errors}, [{format => 'too_high', a => 'myoption', value => 42}], "should perform 2nd validations";
}
# mandatory
t/lib/JobListEmulator.pm view on Meta::CPAN
use Carp;
use JSON::XS 1.00;
sub new
{
my $class = shift;
my $self = { seq => 0, markers => {} };
bless $self, $class;
}
sub _validate_fields
{
my ($self, $job) = (shift, shift);
my %j = %$job;
for (@_) {
confess unless defined delete $j{$_};
}
confess if keys %j;
}
sub add_page
{
my $self = shift;
for my $job (@_) {
confess unless $job->{Action};
if ($job->{Action} eq 'ArchiveRetrieval') {
$self->_validate_fields($job, qw/Action ArchiveId ArchiveSizeInBytes ArchiveSHA256TreeHash Completed CompletionDate CreationDate StatusCode JobId/);
} elsif ($job->{Action} eq 'InventoryRetrieval') {
$self->_validate_fields($job, qw/Action Completed CompletionDate CreationDate StatusCode JobId/);
}
}
push @{$self->{pages} }, [@_];
}
sub fetch_page
{
my ($self, $marker) = @_;
t/unit/config_engine_new.t view on Meta::CPAN
my @res = deprecated @options;
cmp_deeply [@res], [@options];
ok !defined Context->{errors};
ok Context->{options}->{myoption}->{seen};
ok Context->{options}->{myoption2}->{seen};
}
};
};
};
describe "validate" => sub {
it "should check option" => sub {
localize sub {
option 'myoption';
App::MtAws::ConfigEngine->expects("seen")->once()->returns(1);
validate('myoption2');
}
};
describe "validation is defined" => sub {
it "should work when validation passed" => sub {
localize sub {
local $_ = 'abc';
validation option('myoption'), 'myerror', sub { $_ > 10 };
Context->{options}->{myoption}->{value} = '123';
my ($res) = validate 'myoption';
ok $res eq 'myoption';
ok !defined Context->{errors};
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated} && Context->{options}->{myoption}->{valid};
ok $_ eq 'abc';
}
};
it "should work when validation failed" => sub {
localize sub {
validation option('myoption'), 'myerror', sub { $_ > 10 };
Context->{options}->{myoption}->{value} = '7';
my ($res) = validate 'myoption';
ok $res eq 'myoption';
cmp_deeply Context->{errors}, [ { format => 'myerror', a => 'myoption', value => 7 }];
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated};
ok !Context->{options}->{myoption}->{valid};
}
};
it "should work when validation failed with alias" => sub {
localize sub {
validation option('myoption', alias => 'old'), 'myerror', sub { $_ > 10 };
Context->{options}->{myoption}->{value} = '7';
Context->{options}->{myoption}->{original_option} = 'old';
my ($res) = validate 'myoption';
ok $res eq 'myoption';
cmp_deeply Context->{errors}, [ { format => 'myerror', a => 'old', value => 7 }];
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated};
ok !Context->{options}->{myoption}->{valid};
}
};
};
describe "validation is not defined" => sub {
it "should work" => sub {
localize sub {
option 'myoption';
Context->{options}->{myoption}->{value} = '123';
my ($res) = validate 'myoption';
ok $res eq 'myoption';
ok !defined Context->{errors};
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated} && Context->{options}->{myoption}->{valid};
}
};
};
describe "option is not present" => sub {
it "should work" => sub {
localize sub {
option 'myoption';
my ($res) = validate 'myoption';
ok $res eq 'myoption';
ok !defined Context->{errors};
ok Context->{options}->{myoption}->{seen};
ok ! (Context->{options}->{myoption}->{validated} || Context->{options}->{myoption}->{valid});
}
};
};
describe "several validations for one option" => sub {
it "should not perform second validation if stop is true and first failed" => sub {
localize sub {
validation option('myoption'), 'myerror', stop => 1, sub { $_ > 10 };
validation 'myoption', 'myerror2', sub { $_ > 9 };
Context->{options}->{myoption}->{value} = '1';
my (@res) = validate qw/myoption/;
cmp_deeply [@res], [qw/myoption/];
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated};
ok !Context->{options}->{myoption}->{valid};
cmp_deeply Context->{errors}, [ { format => 'myerror', a => 'myoption', value =>1 }];
}
};
it "should perform second validation if stop is false and first failed" => sub {
localize sub {
validation option('myoption'), 'myerror', sub { $_ > 10 };
validation 'myoption', 'myerror2', sub { $_ > 9 };
Context->{options}->{myoption}->{value} = '1';
my (@res) = validate qw/myoption/;
cmp_deeply [@res], [qw/myoption/];
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated};
ok !Context->{options}->{myoption}->{valid};
cmp_deeply Context->{errors}, [ { format => 'myerror', a => 'myoption', value => 1 }, { format => 'myerror2', a => 'myoption', value => 1 }];
}
};
it "should perform second validation if first passed" => sub {
localize sub {
validation option('myoption'), 'myerror', sub { $_ % 2 == 0 };
validation 'myoption', 'myerror2', sub { $_ > 9 };
Context->{options}->{myoption}->{value} = 6;
my (@res) = validate qw/myoption/;
cmp_deeply [@res], [qw/myoption/];
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated};
ok !Context->{options}->{myoption}->{valid};
cmp_deeply Context->{errors}, [ { format => 'myerror2', a => 'myoption', value => 6 }];
}
};
};
describe "several validations, max. one per option" => sub {
it "should check option" => sub {
localize sub {
options qw/myoption myoption2/;
App::MtAws::ConfigEngine->expects("seen")->exactly(2)->returns(1);
validate(qw/myoption2 myoption/);
}
};
it "should work when both failed" => sub {
localize sub {
validation option('myoption'), 'myerror', sub { $_ > 10 };
validation option('myoption2'), 'myerror2', sub { $_ > 9 };
Context->{options}->{myoption}->{value} = '1';
Context->{options}->{myoption2}->{value} = '2';
my (@res) = validate qw/myoption myoption2/;
cmp_deeply [@res], [qw/myoption myoption2/];
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated};
ok !Context->{options}->{myoption}->{valid};
ok Context->{options}->{myoption2}->{seen} && Context->{options}->{myoption2}->{validated};
ok !Context->{options}->{myoption2}->{valid};
cmp_deeply Context->{errors}, [ { format => 'myerror', a => 'myoption', value => 1 },
{ format => 'myerror2', a => 'myoption2', value => 2 }];
}
};
it "error order should match validation order" => sub {
localize sub {
validation option('myoption'), 'myerror', sub { $_ > 10 };
validation option('myoption2'), 'myerror2', sub { $_ > 9 };
Context->{options}->{myoption}->{value} = '1';
Context->{options}->{myoption2}->{value} = '2';
my (@res) = validate qw/myoption2 myoption/;
cmp_deeply Context->{errors}, [ { format => 'myerror2', a => 'myoption2', value => 2 },
{ format => 'myerror', a => 'myoption', value => 1 }];
}
};
it "should work when one failed" => sub {
localize sub {
validation option('myoption'), 'myerror', sub { $_ > 10 };
validation option('myoption2'), 'myerror2', sub { $_ > 9 };
Context->{options}->{myoption}->{value} = '11';
Context->{options}->{myoption2}->{value} = '2';
my (@res) = validate qw/myoption myoption2/;
cmp_deeply [@res], [qw/myoption myoption2/];
ok Context->{options}->{myoption}->{seen} && Context->{options}->{myoption}->{validated} && Context->{options}->{myoption}->{valid};
ok Context->{options}->{myoption2}->{seen} && Context->{options}->{myoption2}->{validated};
ok !Context->{options}->{myoption2}->{valid};
cmp_deeply Context->{errors}, [ { format => 'myerror2', a => 'myoption2', value => 2 }];
}
};
it "should work when one failed" => sub {
localize sub {
options qw/myoption/;
validation option('myoption2'), 'myerror2', sub { $_ > 9 };
Context->{options}->{myoption}->{value} = '2';
Context->{options}->{myoption2}->{value} = '2';
my (@res) = validate qw/myoption myoption2/;
cmp_deeply [@res], [qw/myoption myoption2/];
ok Context->{options}->{myoption}->{seen};
ok Context->{options}->{myoption2}->{seen};
cmp_deeply Context->{errors}, [ { format => 'myerror2', a => 'myoption2', value => 2 }];
}
};
};
};
describe "scope" => sub {
t/unit/config_engine_new.t view on Meta::CPAN
ok ! defined eval { value 'myoption'; 1 };
}
};
};
describe "valid" => sub {
it "should check option " => sub {
localize sub {
option 'myoption';
Context->{options}->{myoption}->{value} = 42;
Context->{options}->{myoption}->{validated} = 1;
Context->{options}->{myoption}->{valid} = 1;
App::MtAws::ConfigEngine->expects("assert_option")->once();
ok valid('myoption');
}
};
it "should work when option valid " => sub {
localize sub {
option 'myoption';
Context->{options}->{myoption}->{value} = 42;
Context->{options}->{myoption}->{validated} = 1;
Context->{options}->{myoption}->{valid} = 1;
ok valid('myoption');
}
};
it "should work when option not valid " => sub {
localize sub {
option 'myoption';
Context->{options}->{myoption}->{value} = 42;
Context->{options}->{myoption}->{validated} = 1;
Context->{options}->{myoption}->{valid} = 0;
ok !valid('myoption');
}
};
it "should die when option not validated " => sub {
localize sub {
option 'myoption';
Context->{options}->{myoption}->{value} = 42;
ok ! defined eval { ok valid('myoption'); 1 };
}
};
};
describe "custom" => sub {
it "should not redefine option" => sub {
t/unit/config_engine_parse.t view on Meta::CPAN
));
ok( $errors && $errors->[0] eq 'Extra argument in command line: arg2', "show throw error is there is extra positional argument" );
}
}
{
fake_config key=>'mykey', secret => 'mysecret', region => 'myregion', vault => 'newvault', sub {
my ($errors, $warnings, $command, $result) = config_create_and_parse(split(' ',
'create-vault --config=glacier.cfg my#vault'
));
ok( $errors && $errors->[0] eq 'Vault name should be 255 characters or less and consisting of a-z, A-Z, 0-9, ".", "-", and "_"', "should validate positional arguments" );
}
}
1;
t/unit/journal_add_delete.t view on Meta::CPAN
my $j = App::MtAws::Journal->new('journal_file' => '.');
$j->_add_archive({ relfilename => $f, archive_id => 'abc123' });
$j->_delete_archive('abc123', $f);
cmp_deeply $j->{archive_h}, {}, "_delete_archive should work";
}
}
{
my $j = App::MtAws::Journal->new('journal_file' => '.');
$j->_add_archive({ relfilename => 'file1', archive_id => 'abc123' });
ok ! eval { $j->_delete_archive('abc123'); 1 }, "_delete_archive should validate arguments";
ok ! eval { $j->_delete_archive(); 1 }, "_delete_archive should validate arguments";
}
{
my $j = App::MtAws::Journal->new('journal_file' => '.');
$j->_add_archive({ relfilename => 'file1', archive_id => 'abc123' });
$j->_add_archive({ relfilename => 'file1', archive_id => 'fff123' });
$j->_delete_archive('abc123', 'file1');
cmp_deeply $j->{archive_h}, { fff123 => { relfilename => 'file1', archive_id => 'fff123' }}, "_delete_archive should work with two archives";
}