use super::*;
use std::fs;
#[test]
fn test_migration_registry_has_legacy_step() {
let steps = migration_steps();
assert!(!steps.is_empty(), "registry must have at least one step");
let legacy = steps
.iter()
.find(|s| s.source_version() == LEGACY_FORMAT_VERSION);
assert!(legacy.is_some(), "registry must include a legacy v0 step");
}
#[test]
fn test_legacy_to_cas_step_versions() {
let step = LegacyToCasStep;
assert_eq!(step.source_version(), LEGACY_FORMAT_VERSION);
assert_eq!(step.target_version(), CURRENT_FORMAT_VERSION);
assert!(step.source_version() < step.target_version());
assert!(!step.name().is_empty());
assert!(step.name().contains("CAS"));
}
#[test]
fn test_every_step_covers_a_version_range() {
for step in migration_steps() {
assert!(
step.source_version() < step.target_version(),
"step '{}' must cover a forward range (from {} to {})",
step.name(),
step.source_version(),
step.target_version()
);
}
}
#[test]
fn test_detect_format_empty_store() {
let tmp = tempfile::tempdir().unwrap();
let detected = detect_format_version(tmp.path());
assert_eq!(detected, LEGACY_FORMAT_VERSION);
}
#[test]
fn test_detect_format_migrated_store() {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path().join("storage");
let gen_dir = root.join("generations").join("1");
fs::create_dir_all(&gen_dir).unwrap();
let manifest = crate::storage::generation::manifest::Manifest {
version: crate::storage::generation::manifest::MANIFEST_VERSION,
generation: 1,
model_identity: crate::storage::generation::manifest::ModelIdentity {
name: "test".to_string(),
digest: "sha256:test".to_string(),
dimensions: 384,
},
graph_fingerprint: [0u8; 32],
search_fingerprint: [0u8; 32],
layers: {
let mut m = std::collections::HashMap::new();
for kind in crate::storage::generation::manifest::ALL_LAYER_KINDS {
m.insert(kind, [0u8; 32]);
}
m
},
};
let bytes = manifest.to_bytes().unwrap();
fs::write(gen_dir.join("manifest"), &bytes).unwrap();
fs::write(root.join("CURRENT"), "1").unwrap();
let detected = detect_format_version(&root);
assert_eq!(detected, CURRENT_FORMAT_VERSION);
}
#[test]
fn test_migrate_to_current_on_empty_store_is_noop() {
let tmp = tempfile::tempdir().unwrap();
let cfg = MigrationConfig {
emit_backup_warning: false,
..Default::default()
};
let report = migrate_to_current(tmp.path(), &cfg).expect("migration succeeds");
assert!(!report.detected_legacy);
assert!(!report.migrated);
assert!(report.was_noop());
}
#[test]
fn test_needs_migration_false_for_new_store() {
let tmp = tempfile::tempdir().unwrap();
assert!(!needs_migration(tmp.path()));
}
struct DummyV1ToV2Step;
impl MigrationStep for DummyV1ToV2Step {
fn source_version(&self) -> u32 {
1
}
fn target_version(&self) -> u32 {
2
}
fn name(&self) -> &str {
"dummy v1→v2 test step"
}
fn migrate(
&self,
storage_root: &std::path::Path,
_config: &MigrationConfig,
) -> Result<MigrationReport, MigrationError> {
Ok(MigrationReport {
detected_legacy: false,
migrated: true,
current_generation: crate::storage::generation::lease::read_current_generation(
storage_root,
),
previous_generation: None,
generations_converted: 0,
generations_deleted: 0,
cas_reclaimed_bytes: 0,
cas_blob_count: 0,
cas_bytes: 0,
jobs_completed_deleted: 0,
jobs_byte_capped: 0,
job_bytes_reclaimed: 0,
job_bytes_remaining: 0,
artifact_bytes_reclaimed: 0,
total_bytes_before: 0,
total_bytes_after: 0,
blob_hashes: Vec::new(),
warnings: Vec::new(),
})
}
}
#[test]
fn test_custom_migration_step_satisfies_trait() {
let step = DummyV1ToV2Step;
assert_eq!(step.source_version(), 1);
assert_eq!(step.target_version(), 2);
assert_eq!(step.name(), "dummy v1→v2 test step");
}
#[test]
fn test_migration_steps_returned_are_boxed_dyn() {
let steps = migration_steps();
for step in &steps {
let _name = step.name();
let _from = step.source_version();
let _to = step.target_version();
}
}