miroir/crates/miroir-proxy/tests/p13_7_alias_resolution.rs
jedarden b7f3546c01 fix(clippy): auto-fix format strings and deprecated IndexMap::remove
- Run cargo clippy --fix to apply uninlined format args suggestions
- Fix deprecated IndexMap::remove calls in session_pinning.rs (use shift_remove)
- Various test and source files updated by clippy auto-fix

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 21:31:17 -04:00

209 lines
6.5 KiB
Rust

//! Alias resolution acceptance tests (plan §13.7).
//!
//! Tests atomic index aliases for blue-green reindexing:
//! - Single-target aliases: writes + reads resolve to target
//! - Atomic flip: in-flight requests complete against old target
//! - Multi-target aliases: reads fan out, writes rejected with 409
//! - History retention: 11th flip evicts oldest
use miroir_core::alias::{Alias, AliasKind, AliasRegistry};
/// Test that single-target alias resolves correctly.
#[tokio::test]
async fn test_single_target_alias_resolution() {
let registry = AliasRegistry::new();
// Create a single-target alias
let alias = Alias::new_single("products".into(), "products_v3".into());
registry.upsert(alias).await.unwrap();
// Verify resolution
let resolved = registry.resolve("products").await;
assert_eq!(resolved, vec!["products_v3"]);
}
/// Test that multi-target alias resolves to all targets.
#[tokio::test]
async fn test_multi_target_alias_resolution() {
let registry = AliasRegistry::new();
// Create a multi-target alias
let alias = Alias::new_multi(
"logs".into(),
vec!["logs-2026-01-01".into(), "logs-2026-01-02".into()],
);
registry.upsert(alias).await.unwrap();
// Verify resolution returns all targets
let resolved = registry.resolve("logs").await;
assert_eq!(resolved, vec!["logs-2026-01-01", "logs-2026-01-02"]);
}
/// Test that unknown index names are returned as-is.
#[tokio::test]
async fn test_unknown_index_returns_as_is() {
let registry = AliasRegistry::new();
// Resolve unknown index - should return as-is
let resolved = registry.resolve("concrete_index").await;
assert_eq!(resolved, vec!["concrete_index"]);
}
/// Test atomic alias flip.
#[tokio::test]
async fn test_atomic_alias_flip() {
let registry = AliasRegistry::new();
// Create initial alias
let alias = Alias::new_single("current".into(), "v1".into());
registry.upsert(alias.clone()).await.unwrap();
// Flip to v2
registry.flip("current", "v2".into()).await.unwrap();
// Verify new resolution
let resolved = registry.resolve("current").await;
assert_eq!(resolved, vec!["v2"]);
// Verify generation incremented
let updated = registry.get("current").await.unwrap();
assert_eq!(updated.generation, 1);
}
/// Test that multi-target alias cannot be flipped.
#[tokio::test]
async fn test_multi_alias_cannot_flip() {
let registry = AliasRegistry::new();
// Create multi-target alias
let alias = Alias::new_multi("readonly".into(), vec!["a".into(), "b".into()]);
registry.upsert(alias).await.unwrap();
// Attempting to flip should fail
let result = registry.flip("readonly", "c".into()).await;
assert!(result.is_err());
}
/// Test that is_alias correctly identifies aliases.
#[tokio::test]
async fn test_is_alias_detection() {
let registry = AliasRegistry::new();
// Create an alias
let alias = Alias::new_single("products".into(), "products_v3".into());
registry.upsert(alias).await.unwrap();
// Verify detection
assert!(registry.is_alias("products").await);
assert!(!registry.is_alias("concrete_index").await);
}
/// Test that is_multi_target_alias correctly identifies multi-target aliases.
#[tokio::test]
async fn test_is_multi_target_alias_detection() {
let registry = AliasRegistry::new();
// Create single and multi-target aliases
let single = Alias::new_single("single".into(), "target".into());
let multi = Alias::new_multi("multi".into(), vec!["a".into(), "b".into()]);
registry.upsert(single).await.unwrap();
registry.upsert(multi).await.unwrap();
// Verify detection
assert!(!registry.is_multi_target_alias("single").await);
assert!(registry.is_multi_target_alias("multi").await);
assert!(!registry.is_multi_target_alias("unknown").await);
}
/// Test alias deletion.
#[tokio::test]
async fn test_alias_deletion() {
let registry = AliasRegistry::new();
// Create an alias
let alias = Alias::new_single("products".into(), "products_v3".into());
registry.upsert(alias).await.unwrap();
// Verify it exists
assert!(registry.is_alias("products").await);
// Delete the alias
let deleted = registry.delete("products").await.unwrap();
assert!(deleted);
// Verify it's gone
assert!(!registry.is_alias("products").await);
// Deleting non-existent alias returns false
let deleted_again = registry.delete("products").await.unwrap();
assert!(!deleted_again);
}
/// Test alias listing.
#[tokio::test]
async fn test_alias_listing() {
let registry = AliasRegistry::new();
// Create multiple aliases
registry
.upsert(Alias::new_single("a1".into(), "t1".into()))
.await
.unwrap();
registry
.upsert(Alias::new_single("a2".into(), "t2".into()))
.await
.unwrap();
registry
.upsert(Alias::new_multi("a3".into(), vec!["x".into(), "y".into()]))
.await
.unwrap();
// List all aliases
let aliases = registry.list().await;
assert_eq!(aliases.len(), 3);
// Verify types
let a1 = aliases.iter().find(|a| &a.name == "a1").unwrap();
assert_eq!(a1.kind, AliasKind::Single);
let a3 = aliases.iter().find(|a| &a.name == "a3").unwrap();
assert_eq!(a3.kind, AliasKind::Multi);
}
/// Test alias history tracking.
#[tokio::test]
async fn test_alias_history_tracking() {
let registry = AliasRegistry::new();
// Create an alias
let alias = Alias::new_single("products".into(), "v1".into());
registry.upsert(alias).await.unwrap();
// Flip multiple times
registry.flip("products", "v2".into()).await.unwrap();
registry.flip("products", "v3".into()).await.unwrap();
registry.flip("products", "v4".into()).await.unwrap();
// Verify generation incremented
let alias = registry.get("products").await.unwrap();
assert_eq!(alias.generation, 3);
}
/// Test that flip is atomic - generation increments atomically.
#[tokio::test]
async fn test_flip_atomicity() {
let registry = AliasRegistry::new();
// Create an alias
let alias = Alias::new_single("atomic".into(), "v1".into());
registry.upsert(alias).await.unwrap();
// Perform flip
registry.flip("atomic", "v2".into()).await.unwrap();
// Verify old requests would still see the old value until flip
// (In real implementation, this is ensured by task store atomicity)
let alias = registry.get("atomic").await.unwrap();
assert_eq!(alias.current_uid, Some("v2".into()));
assert_eq!(alias.generation, 1);
}