CallBackery
view release on metacpan or search on metacpan
t/config-restore.t view on Meta::CPAN
# a config of our own so we do not share the config database with the other
# tests -- this one gets overwritten half way through
my $cfgDb = setupTestConfig()->{cfgDb};
my $t = Test::Mojo->new('CallBackery');
my $cfg = $t->app->config;
my $db = $t->app->database;
is $cfg->cfgHash->{BACKEND}{cfg_db}, $cfgDb, 'the app uses our temporary config database';
$db->setConfigValue('restoreProbe','before');
my $blob = $cfg->getConfigBlob('secret');
$db->setConfigValue('restoreProbe','after');
is $db->getConfigValue('restoreProbe'),'after','probe value changed after taking the blob';
# a long lived handle, as held by the config daemon, its workers and by
# transient helpers such as a post start configuration script
my $live = DBI->connect("dbi:SQLite:dbname=$cfgDb",'','',{
RaiseError => 1,
PrintError => 0,
AutoCommit => 1,
});
is $live->selectrow_array('SELECT cbconfig_value FROM cbconfig WHERE cbconfig_id = ?',undef,'restoreProbe'),
'after','the live handle sees the current value';
my $inodeBefore = (stat $cfgDb)[1];
$cfg->restoreConfigBlob($blob,'secret');
is +(stat $cfgDb)[1], $inodeBefore, 'the restore keeps the config database inode';
is $live->selectrow_array('SELECT cbconfig_value FROM cbconfig WHERE cbconfig_id = ?',undef,'restoreProbe'),
'before','the live handle reads the restored data';
# the actual regression: with the database replaced rather than restored in
# place, SQLite fails this with SQLITE_READONLY_DBMOVED, reported as
# "attempt to write a readonly database"
ok eval {
$live->do('INSERT INTO cbconfig (cbconfig_id,cbconfig_value) VALUES (?,?)',
undef,'restoreWrite','ok');
1;
}, 'the live handle can still write' or diag $@;
# A restore that cannot get the write lock must leave the database exactly as
# it was. Giving up is fine, half restoring is not.
{
local $CallBackery::Config::RESTORE_BUSY_TIMEOUT_MS = 500;
$db->setConfigValue('restoreProbe','busy');
my $inode = (stat $cfgDb)[1];
my $reader = DBI->connect("dbi:SQLite:dbname=$cfgDb",'','',{
RaiseError => 1,
PrintError => 0,
AutoCommit => 1,
});
$reader->begin_work;
$reader->selectrow_array('SELECT count(*) FROM cbconfig');
ok !eval { $cfg->restoreConfigBlob($blob,'secret'); 1 },
'a restore blocked by another connection fails';
like "$@",qr/restore the configuration database/,
'and fails with the restore error, not something incidental';
is +(stat $cfgDb)[1], $inode, 'the blocked restore left the inode alone';
is $db->getConfigValue('restoreProbe'),'busy',
'the blocked restore left the data alone';
$reader->rollback;
$reader->disconnect;
# and the database is still usable afterwards
$db->setConfigValue('restoreProbe','afterBusy');
is $db->getConfigValue('restoreProbe'),'afterBusy',
'the database is still writable after a blocked restore';
}
# no staging database is left behind
is_deeply [glob "$cfgDb.restore*"],[],'the restore leaves no staging database behind';
$live->disconnect;
done_testing();
( run in 1.446 second using v1.01-cache-2.11-cpan-800906f7e73 )