Resolves the 142 findings from tidal/docs/reviews/CODE_REVIEW_m0-m10.md across the engine, server, net, and CLI surfaces: - WAL/session-journal durability, checkpoint format, and crash-recovery hardening - Replication shipper/receiver, tenant isolation, and migration paths - Cluster scatter-gather, router, standalone server + health/offload endpoints - tidalctl refactored into command modules with JSON output and WAL-state tooling - Cohort, governance, signal-ledger, and vector-registry correctness fixes - Expanded UAT/integration/durability test coverage across all milestones
180 lines
5.8 KiB
Rust
180 lines
5.8 KiB
Rust
#![allow(
|
|
clippy::too_many_lines,
|
|
clippy::unwrap_used,
|
|
clippy::expect_used,
|
|
clippy::missing_panics_doc
|
|
)]
|
|
|
|
//! Integration tests for the sandboxed storage layout.
|
|
//!
|
|
//! Validates that [`Paths`] and [`TempTidalHome`] provide correct,
|
|
//! isolated filesystem sandboxes for tidalDB instances.
|
|
|
|
use tidaldb::{TidalDb, db::temp::TempTidalHome};
|
|
|
|
// =============================================================================
|
|
// TempTidalHome isolation
|
|
// =============================================================================
|
|
|
|
#[test]
|
|
fn two_temp_homes_have_non_overlapping_paths() {
|
|
let home1 = TempTidalHome::new().unwrap();
|
|
let home2 = TempTidalHome::new().unwrap();
|
|
|
|
assert_ne!(
|
|
home1.path(),
|
|
home2.path(),
|
|
"two TempTidalHome instances must have different base paths"
|
|
);
|
|
|
|
// Verify neither is a prefix of the other (true isolation).
|
|
assert!(
|
|
!home1.path().starts_with(home2.path()),
|
|
"home1 should not be a subdirectory of home2"
|
|
);
|
|
assert!(
|
|
!home2.path().starts_with(home1.path()),
|
|
"home2 should not be a subdirectory of home1"
|
|
);
|
|
}
|
|
|
|
// =============================================================================
|
|
// Drop removes directory
|
|
// =============================================================================
|
|
|
|
#[test]
|
|
fn drop_removes_directory_with_contents() {
|
|
let path = {
|
|
let home = TempTidalHome::new().unwrap();
|
|
let p = home.path().to_owned();
|
|
|
|
// Create a file inside so the directory is non-empty.
|
|
std::fs::write(p.join("sentinel.txt"), b"test data").unwrap();
|
|
assert!(p.exists(), "directory should exist before drop");
|
|
p
|
|
// home dropped here
|
|
};
|
|
assert!(!path.exists(), "directory should be removed after drop");
|
|
}
|
|
|
|
// =============================================================================
|
|
// with_preserve keeps directory
|
|
// =============================================================================
|
|
|
|
#[test]
|
|
fn with_preserve_keeps_directory_after_drop() {
|
|
let path = {
|
|
let home = TempTidalHome::with_preserve().unwrap();
|
|
let p = home.path().to_owned();
|
|
|
|
std::fs::write(p.join("sentinel.txt"), b"test data").unwrap();
|
|
p
|
|
// home dropped here, but preserve=true
|
|
};
|
|
assert!(
|
|
path.exists(),
|
|
"directory should still exist after drop with preserve"
|
|
);
|
|
|
|
// Manual cleanup.
|
|
std::fs::remove_dir_all(&path).unwrap();
|
|
}
|
|
|
|
// =============================================================================
|
|
// Paths::ensure_all creates expected subdirectories
|
|
// =============================================================================
|
|
|
|
#[test]
|
|
fn ensure_all_creates_all_subdirectories() {
|
|
let home = TempTidalHome::new().unwrap();
|
|
let paths = home.paths();
|
|
|
|
// Before ensure_all, subdirectories should not exist.
|
|
assert!(!paths.wal_dir().exists());
|
|
assert!(!paths.items_dir().exists());
|
|
assert!(!paths.users_dir().exists());
|
|
assert!(!paths.creators_dir().exists());
|
|
assert!(!paths.cache_dir().exists());
|
|
|
|
paths.ensure_all().unwrap();
|
|
|
|
// After ensure_all, all subdirectories should exist.
|
|
assert!(paths.wal_dir().is_dir(), "wal dir should exist");
|
|
assert!(paths.items_dir().is_dir(), "items dir should exist");
|
|
assert!(paths.users_dir().is_dir(), "users dir should exist");
|
|
assert!(paths.creators_dir().is_dir(), "creators dir should exist");
|
|
assert!(paths.cache_dir().is_dir(), "cache dir should exist");
|
|
}
|
|
|
|
// =============================================================================
|
|
// Two builders with different TempTidalHome roots never collide
|
|
// =============================================================================
|
|
|
|
#[test]
|
|
fn two_builders_with_different_homes_are_isolated() {
|
|
let home1 = TempTidalHome::new().unwrap();
|
|
let home2 = TempTidalHome::new().unwrap();
|
|
|
|
let paths1 = home1.paths();
|
|
let paths2 = home2.paths();
|
|
|
|
paths1.ensure_all().unwrap();
|
|
paths2.ensure_all().unwrap();
|
|
|
|
// Write a file into home1's wal directory.
|
|
let sentinel1 = paths1.wal_dir().join("segment-001");
|
|
std::fs::write(&sentinel1, b"home1 wal data").unwrap();
|
|
|
|
// Write a file into home2's wal directory.
|
|
let sentinel2 = paths2.wal_dir().join("segment-001");
|
|
std::fs::write(&sentinel2, b"home2 wal data").unwrap();
|
|
|
|
// Verify each sees only its own data.
|
|
let data1 = std::fs::read(&sentinel1).unwrap();
|
|
let data2 = std::fs::read(&sentinel2).unwrap();
|
|
|
|
assert_eq!(data1, b"home1 wal data");
|
|
assert_eq!(data2, b"home2 wal data");
|
|
|
|
// Verify the other home's directory does not contain the wrong data.
|
|
assert_ne!(data1, data2);
|
|
}
|
|
|
|
// =============================================================================
|
|
// Builder integrates with Paths via TempTidalHome
|
|
// =============================================================================
|
|
|
|
#[test]
|
|
fn builder_with_temp_home_opens_successfully() {
|
|
let home = TempTidalHome::new().unwrap();
|
|
|
|
// The builder validates data_dir exists (TempTidalHome creates it).
|
|
// Resolved defaults (wal_dir, cache_dir) are populated after validation
|
|
// so they don't need to pre-exist.
|
|
let db = TidalDb::builder().with_data_dir(home.path()).open();
|
|
assert!(db.is_ok(), "builder with TempTidalHome should succeed");
|
|
}
|
|
|
|
#[test]
|
|
fn ensure_all_is_idempotent() {
|
|
let home = TempTidalHome::new().unwrap();
|
|
let paths = home.paths();
|
|
|
|
paths.ensure_all().unwrap();
|
|
|
|
// Write a file to verify it survives a second ensure_all.
|
|
std::fs::write(paths.wal_dir().join("segment-001"), b"data").unwrap();
|
|
|
|
paths.ensure_all().unwrap();
|
|
|
|
// File should still exist.
|
|
assert!(paths.wal_dir().join("segment-001").exists());
|
|
}
|
|
|
|
#[test]
|
|
fn paths_base_matches_temp_home_path() {
|
|
let home = TempTidalHome::new().unwrap();
|
|
let paths = home.paths();
|
|
assert_eq!(paths.base(), home.path());
|
|
}
|