use super::*;
fn remove_receipts(path: &Path, keys: &span>; 64) {
let db = LegacyDatabase::open(path).unwrap();
let transaction = db.begin_write().unwrap();
{
let mut requests = transaction.open_table(REQUESTS).unwrap();
for key in keys {
assert!(requests.remove(key.as_slice()).unwrap().is_some());
}
}
transaction.commit().unwrap();
}
fn assert_rejected_and_sealed(fixture: &Fixture) {
assert!(matches!(
ApplicationStore::migrate_redb(fixture.source(), fixture.destination()),
Err(Error::Corrupt)
));
assert!(guard_path(&fixture.destination()).is_file());
assert!(matches!(
ApplicationStore::open(fixture.destination()),
Err(Error::Corrupt)
));
assert_status(&fixture.destination(), b"copying");
assert!(LegacyDatabase::open(fixture.source()).is_ok());
}
fn assert_no_temporary_receipt_index(path: &Path) {
let keyspace = fjall::Config::new(path).open().unwrap();
let metadata = keyspace
.open_partition("bbg_import_v1", Default::default())
.unwrap();
assert_eq!(
metadata.get(b"status").unwrap().unwrap().as_ref(),
b"complete"
);
assert!(metadata.prefix(b"r").next().is_none());
}
#[test]
fn missing_all_or_one_receipt_prevents_migration_completion() {
for all in [true, false] {
let fixture = Fixture::new();
create_legacy(&fixture.source(), &[(1, 2), (2, 2)]);
let keys = if all {
vec![
request_key(1, 0),
request_key(1, 1),
request_key(2, 0),
request_key(2, 1),
]
} else {
vec![request_key(1, 0)]
};
remove_receipts(&fixture.source(), &keys);
assert_rejected_and_sealed(&fixture);
}
}
#[test]
fn duplicate_receipts_cannot_cover_another_missing_history_entry() {
for replace_existing in [true, false] {
let fixture = Fixture::new();
create_legacy(&fixture.source(), &[(1, 2), (2, 2)]);
let mut duplicate = [0; 72];
duplicate[..32].copy_from_slice(&id(0x80, 1, 1));
duplicate[32..].copy_from_slice(&encode_head(head(1, 0)));
let key = request_key(1, if replace_existing { 1 } else { 99 });
set_record(&fixture.source(), REQUESTS, &key, &duplicate);
assert_rejected_and_sealed(&fixture);
}
}
#[test]
fn an_empty_legacy_application_database_can_migrate_and_accept_its_first_write() {
let fixture = Fixture::new();
let source = LegacyDatabase::create(fixture.source()).unwrap();
let transaction = source.begin_write().unwrap();
for table in TABLES {
transaction.open_table(table).unwrap();
}
transaction.commit().unwrap();
drop(source);
ApplicationStore::migrate_redb(fixture.source(), fixture.destination()).unwrap();
assert!(!guard_path(&fixture.destination()).exists());
assert_no_temporary_receipt_index(&fixture.destination());
let migrated = ApplicationStore::open(fixture.destination()).unwrap();
assert_eq!(migrated.head(&[1; 32]).unwrap(), None);
assert!(migrated.history(&[1; 32], None, 4096).unwrap().is_empty());
assert_eq!(migrated.resolve(&[1; 32], &id(0x70, 1, 0)).unwrap(), None);
let write = Write {
namespace: [1; 32],
request: id(0x70, 1, 0),
fingerprint: id(0x80, 1, 0),
expected: None,
head: head(1, 0),
content: &[(head(1, 0).commit, content(1, 0))],
claims: &[],
};
assert_eq!(migrated.apply(&write).unwrap(), head(1, 0));
}
#[test]
fn successful_receipt_validation_removes_its_temporary_reverse_index() {
let fixture = Fixture::new();
create_legacy(&fixture.source(), &[(1, 2), (2, 2)]);
ApplicationStore::migrate_redb(fixture.source(), fixture.destination()).unwrap();
assert_no_temporary_receipt_index(&fixture.destination());
let migrated = ApplicationStore::open(fixture.destination()).unwrap();
for namespace in [1, 2] {
for index in [0, 1] {
assert_eq!(
migrated
.resolve(&[namespace; 32], &id(0x70, namespace, index))
.unwrap(),
Some((id(0x80, namespace, index), head(namespace, index)))
);
}
}
}