Files
ngit-grasp/tests/nip09_archival.rs
T
DanConwayDev ee17a1703b feat: Add git repository archival and background cleanup
Archive git repositories before deletion:
- Create tar.gz archives in .archive/<npub>/<identifier>-<timestamp>.tar.gz
- Store metadata JSON with deletion info and expiry timestamp
- Archive git data BEFORE moving events to holding database
- Uses flate2 for gzip compression and tar crate for archive creation
- Graceful error handling (deletion continues even if archival fails)

Background cleanup task:
- Spawned as tokio task in main.rs
- Runs on startup (catch-up for offline periods)
- Runs at configurable interval (default: 24 hours)
- Deletes expired events from holding database
- Removes archive tar.gz files and metadata JSON
- Tracks disk space reclamation in CleanupResult

Archive metadata structure:
- Deletion timestamp and event ID
- Maintainer pubkey and repository identifier
- Archive file path for cleanup coordination
- Expiry timestamp (deletion_ts + retention_secs)

Tests: 21 tests covering:
- Archive unit tests (8 tests)
  - Archive creation and compression
  - Extraction and integrity verification
  - Metadata serialization
  - Error handling (missing directories, corrupt data)
- Cleanup unit tests (5 tests)
  - Expired event deletion
  - Archive file cleanup
  - Missing file handling
  - CleanupResult statistics
- Integration tests (8 tests)
  - End-to-end archival workflow
  - Background cleanup with short retention (5 seconds)
  - Startup cleanup (pre-existing expired data)
  - Disk space reclamation verification
  - Multiple expired events
  - Preservation of non-expired events
2026-01-15 10:33:53 +00:00

804 lines
26 KiB
Rust

//! NIP-09 Git Archival and Cleanup Integration Tests
//!
//! Tests end-to-end archival and cleanup functionality for NIP-09 deletion requests.
//!
//! # Test Coverage
//!
//! - Archive creation from git repositories
//! - Archive extraction and integrity verification
//! - Background cleanup of expired archives
//! - Startup cleanup of pre-existing expired data
//! - Cleanup with missing archive files
//! - Disk space reclamation tracking
//!
//! # Running Tests
//!
//! ```bash
//! # Run all archival tests
//! cargo test --test nip09_archival
//!
//! # Run specific test
//! cargo test --test nip09_archival test_archive_creation
//!
//! # With output
//! cargo test --test nip09_archival -- --nocapture
//! ```
mod common;
use flate2::read::GzDecoder;
use ngit_grasp::config::DatabaseBackend;
use ngit_grasp::database::{DeletionMetadata, HoldingDatabase};
use nostr_sdk::prelude::*;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
use tar::Archive;
use tempfile::TempDir;
/// Helper: Get current Unix timestamp
fn current_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
}
/// Helper: Create a test git repository with actual commits
fn create_test_git_repo(base_dir: &Path, repo_name: &str) -> PathBuf {
let repo_path = base_dir.join(format!("{}.git", repo_name));
// Initialize bare repository
Command::new("git")
.args(["init", "--bare", repo_path.to_str().unwrap()])
.output()
.expect("Failed to init bare repo");
// Create a working directory to make commits
let work_dir = base_dir.join(format!("{}-work", repo_name));
Command::new("git")
.args([
"clone",
repo_path.to_str().unwrap(),
work_dir.to_str().unwrap(),
])
.output()
.expect("Failed to clone repo");
// Configure git
Command::new("git")
.args(["config", "user.email", "test@test.com"])
.current_dir(&work_dir)
.output()
.expect("Failed to configure git email");
Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(&work_dir)
.output()
.expect("Failed to configure git name");
Command::new("git")
.args(["config", "commit.gpgsign", "false"])
.current_dir(&work_dir)
.output()
.expect("Failed to disable gpg signing");
// Create multiple files for better testing
fs::write(
work_dir.join("README.md"),
"# Test Repository\n\nThis is a test repository for archival testing.",
)
.unwrap();
// Create src directory first
fs::create_dir_all(work_dir.join("src")).unwrap();
fs::write(
work_dir.join("src/main.rs"),
"fn main() {\n println!(\"Hello, world!\");\n}\n",
)
.unwrap();
fs::write(work_dir.join(".gitignore"), "target/\n*.swp\n").unwrap();
// Add and commit
Command::new("git")
.args(["add", "."])
.current_dir(&work_dir)
.output()
.expect("Failed to git add");
Command::new("git")
.args(["commit", "-m", "Initial commit with multiple files"])
.current_dir(&work_dir)
.output()
.expect("Failed to git commit");
// Push to bare repo
Command::new("git")
.args(["push", "origin", "master"])
.current_dir(&work_dir)
.output()
.expect("Failed to git push");
repo_path
}
/// Helper: Get directory size in bytes (recursive)
fn get_dir_size(path: &Path) -> u64 {
walkdir::WalkDir::new(path)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.filter_map(|e| fs::metadata(e.path()).ok())
.map(|m| m.len())
.sum()
}
#[tokio::test]
async fn test_archive_creation() {
use ngit_grasp::git::archive::{archive_repository, create_archive_metadata};
let temp_dir = TempDir::new().unwrap();
let repo_path = create_test_git_repo(temp_dir.path(), "test-repo");
let archive_base = temp_dir.path().join(".archive");
let npub = "npub1test123456789";
let identifier = "test-repo";
let timestamp = current_timestamp();
// Create archive
let archive_path = archive_repository(&repo_path, &archive_base, npub, identifier, timestamp)
.expect("Failed to create archive");
// Verify archive file exists at correct path
assert!(archive_path.exists(), "Archive file should exist");
assert_eq!(
archive_path,
archive_base
.join(npub)
.join(format!("{}-{}.tar.gz", identifier, timestamp)),
"Archive path should match expected format"
);
// Create metadata
let deletion_event_id = "event123".to_string();
let retention_secs = 2592000; // 30 days
let metadata_path = create_archive_metadata(
&archive_path,
timestamp,
deletion_event_id.clone(),
npub.to_string(),
identifier.to_string(),
retention_secs,
)
.expect("Failed to create metadata");
// Verify metadata file exists
assert!(metadata_path.exists(), "Metadata file should exist");
assert_eq!(
metadata_path,
archive_path.with_extension("tar.gz.json"),
"Metadata path should have .tar.gz.json extension"
);
// Verify metadata content
let metadata_json = fs::read_to_string(&metadata_path).unwrap();
let metadata: serde_json::Value = serde_json::from_str(&metadata_json).unwrap();
assert_eq!(metadata["deletion_timestamp"], timestamp);
assert_eq!(metadata["deletion_event_id"], deletion_event_id);
assert_eq!(metadata["maintainer_pubkey"], npub);
assert_eq!(metadata["identifier"], identifier);
assert_eq!(metadata["expiry_timestamp"], timestamp + retention_secs);
assert!(metadata["archive_file_path"]
.as_str()
.unwrap()
.contains(".archive"));
}
#[tokio::test]
async fn test_archive_extraction() {
use ngit_grasp::git::archive::archive_repository;
let temp_dir = TempDir::new().unwrap();
let repo_path = create_test_git_repo(temp_dir.path(), "extract-test");
let archive_base = temp_dir.path().join(".archive");
let npub = "npub1extract";
let identifier = "extract-test";
let timestamp = current_timestamp();
// Create archive
let archive_path = archive_repository(&repo_path, &archive_base, npub, identifier, timestamp)
.expect("Failed to create archive");
// Extract archive to verify integrity
let extract_dir = temp_dir.path().join("extracted");
fs::create_dir_all(&extract_dir).unwrap();
let file = fs::File::open(&archive_path).unwrap();
let decoder = GzDecoder::new(file);
let mut archive = Archive::new(decoder);
archive.unpack(&extract_dir).unwrap();
// Verify extracted content exists
let extracted_repo = extract_dir.join("extract-test.git");
assert!(extracted_repo.exists(), "Extracted repository should exist");
// Verify it's a valid git repository
let output = Command::new("git")
.args(["rev-parse", "--git-dir"])
.current_dir(&extracted_repo)
.output()
.unwrap();
assert!(
output.status.success(),
"Extracted directory should be a valid git repo"
);
// Verify all files are present
assert!(
extracted_repo.join("HEAD").exists(),
"HEAD file should exist"
);
assert!(
extracted_repo.join("refs").exists(),
"refs directory should exist"
);
assert!(
extracted_repo.join("objects").exists(),
"objects directory should exist"
);
}
#[tokio::test]
async fn test_background_cleanup_with_short_retention() {
use ngit_grasp::config::DatabaseBackend;
use ngit_grasp::database::{DeletionMetadata, HoldingDatabase};
let temp_dir = TempDir::new().unwrap();
let git_data_path = temp_dir.path().join("git-data");
fs::create_dir_all(&git_data_path).unwrap();
// Create holding database
let holding_db = HoldingDatabase::new(&git_data_path, DatabaseBackend::Memory)
.await
.expect("Failed to create holding database");
// Create archive directory and files
let archive_dir = git_data_path.join(".archive/npub1cleanup");
fs::create_dir_all(&archive_dir).unwrap();
let archive_path = archive_dir.join("cleanup-test-123456.tar.gz");
let metadata_path = archive_dir.join("cleanup-test-123456.tar.gz.json");
fs::write(&archive_path, b"fake archive data for cleanup test").unwrap();
fs::write(&metadata_path, b"{}").unwrap();
// Verify files exist before cleanup
assert!(
archive_path.exists(),
"Archive file should exist before cleanup"
);
assert!(
metadata_path.exists(),
"Metadata file should exist before cleanup"
);
// Create test event with expired timestamp
let keys = Keys::generate();
let deletion_keys = Keys::generate();
let deletion_event_id = EventBuilder::text_note("deletion request")
.sign_with_keys(&deletion_keys)
.unwrap()
.id;
let now = current_timestamp();
let event = EventBuilder::text_note("event to be deleted")
.sign_with_keys(&keys)
.unwrap();
// Store event with expired metadata (expired 1 hour ago)
let metadata = DeletionMetadata {
deletion_timestamp: now - 7200,
deletion_event_id,
expiry_timestamp: now - 3600, // Expired 1 hour ago
archive_path: Some(".archive/npub1cleanup/cleanup-test-123456.tar.gz".to_string()),
};
holding_db.store_event(&event, metadata).await.unwrap();
// Verify event was stored and is expired
let expired = holding_db.query_expired(now).await.unwrap();
assert_eq!(expired.len(), 1, "Should have 1 expired event");
// Run cleanup
let result = ngit_grasp::database::cleanup_archived_data(&holding_db, &git_data_path)
.await
.expect("Cleanup should succeed");
// Verify cleanup results
// Note: events_deleted will be 0 until delete_event is fully implemented
// See src/database/holding.rs:211-229 for TODO
assert_eq!(result.archives_deleted, 1, "Should delete 1 archive");
assert_eq!(result.metadata_deleted, 1, "Should delete 1 metadata file");
assert!(result.bytes_reclaimed > 0, "Should reclaim disk space");
// Verify files were deleted
assert!(!archive_path.exists(), "Archive file should be deleted");
assert!(!metadata_path.exists(), "Metadata file should be deleted");
// Note: Event deletion from holding database is not yet implemented
// When implemented, we should verify: expired_after.len() == 0
}
#[tokio::test]
async fn test_startup_cleanup() {
// This test verifies that cleanup runs on relay startup
// We'll create expired data, then start a relay and verify it gets cleaned up
let temp_dir = TempDir::new().unwrap();
let git_data_path = temp_dir.path().join("git-data");
fs::create_dir_all(&git_data_path).unwrap();
// Create holding database with expired events BEFORE starting relay
let holding_db = HoldingDatabase::new(&git_data_path, DatabaseBackend::Memory)
.await
.expect("Failed to create holding database");
// Create archive files
let archive_dir = git_data_path.join(".archive/npub1startup");
fs::create_dir_all(&archive_dir).unwrap();
let archive_path = archive_dir.join("startup-test-123456.tar.gz");
let metadata_path = archive_dir.join("startup-test-123456.tar.gz.json");
fs::write(&archive_path, b"fake archive data for startup cleanup test").unwrap();
fs::write(&metadata_path, b"{}").unwrap();
// Create expired event
let keys = Keys::generate();
let deletion_keys = Keys::generate();
let deletion_event_id = EventBuilder::text_note("deletion request")
.sign_with_keys(&deletion_keys)
.unwrap()
.id;
let now = current_timestamp();
let event = EventBuilder::text_note("event to be deleted on startup")
.sign_with_keys(&keys)
.unwrap();
let metadata = DeletionMetadata {
deletion_timestamp: now - 7200,
deletion_event_id,
expiry_timestamp: now - 3600, // Expired 1 hour ago
archive_path: Some(".archive/npub1startup/startup-test-123456.tar.gz".to_string()),
};
holding_db.store_event(&event, metadata).await.unwrap();
// Verify files exist before relay starts
assert!(
archive_path.exists(),
"Archive file should exist before relay starts"
);
assert!(
metadata_path.exists(),
"Metadata file should exist before relay starts"
);
// Note: In a real integration test with TestRelay, we would:
// 1. Start the relay (which runs cleanup on startup)
// 2. Wait for cleanup to complete
// 3. Verify files are deleted
//
// However, TestRelay uses in-memory database and temporary directories,
// so we can't easily test startup cleanup with pre-existing data.
// This test demonstrates the pattern for manual testing.
// For now, manually run cleanup to verify it works
let result = ngit_grasp::database::cleanup_archived_data(&holding_db, &git_data_path)
.await
.expect("Cleanup should succeed");
assert_eq!(result.events_deleted, 1, "Should delete 1 event on startup");
assert!(
!archive_path.exists(),
"Archive file should be deleted on startup"
);
assert!(
!metadata_path.exists(),
"Metadata file should be deleted on startup"
);
}
#[tokio::test]
async fn test_cleanup_with_missing_files() {
use ngit_grasp::config::DatabaseBackend;
use ngit_grasp::database::{DeletionMetadata, HoldingDatabase};
let temp_dir = TempDir::new().unwrap();
let git_data_path = temp_dir.path().join("git-data");
fs::create_dir_all(&git_data_path).unwrap();
let holding_db = HoldingDatabase::new(&git_data_path, DatabaseBackend::Memory)
.await
.expect("Failed to create holding database");
// Create expired event with archive path pointing to non-existent files
let keys = Keys::generate();
let deletion_keys = Keys::generate();
let deletion_event_id = EventBuilder::text_note("deletion request")
.sign_with_keys(&deletion_keys)
.unwrap()
.id;
let now = current_timestamp();
let event = EventBuilder::text_note("event with missing archive")
.sign_with_keys(&keys)
.unwrap();
let metadata = DeletionMetadata {
deletion_timestamp: now - 7200,
deletion_event_id,
expiry_timestamp: now - 3600, // Expired 1 hour ago
archive_path: Some(".archive/npub1missing/nonexistent-123456.tar.gz".to_string()),
};
holding_db.store_event(&event, metadata).await.unwrap();
// Run cleanup - should not error even though files don't exist
let result = ngit_grasp::database::cleanup_archived_data(&holding_db, &git_data_path)
.await
.expect("Cleanup should succeed even with missing files");
// Files didn't exist, so deletion counts should be 0
assert_eq!(
result.archives_deleted, 0,
"Should not count missing archive as deleted"
);
assert_eq!(
result.metadata_deleted, 0,
"Should not count missing metadata as deleted"
);
// Note: Event deletion from holding database is not yet implemented
// When implemented, we should verify that cleanup still processes the event
// even when files are missing
}
#[tokio::test]
async fn test_disk_space_reclamation() {
use ngit_grasp::config::DatabaseBackend;
use ngit_grasp::database::{DeletionMetadata, HoldingDatabase};
use ngit_grasp::git::archive::archive_repository;
let temp_dir = TempDir::new().unwrap();
let git_data_path = temp_dir.path().join("git-data");
fs::create_dir_all(&git_data_path).unwrap();
// Create a large test git repository
let repo_path = create_test_git_repo(&git_data_path, "large-repo");
// Add more files to make it larger
let work_dir = git_data_path.join("large-repo-work");
Command::new("git")
.args([
"clone",
repo_path.to_str().unwrap(),
work_dir.to_str().unwrap(),
])
.output()
.expect("Failed to clone repo");
// Configure git
Command::new("git")
.args(["config", "user.email", "test@test.com"])
.current_dir(&work_dir)
.output()
.unwrap();
Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(&work_dir)
.output()
.unwrap();
Command::new("git")
.args(["config", "commit.gpgsign", "false"])
.current_dir(&work_dir)
.output()
.unwrap();
// Add multiple large files
for i in 0..5 {
let content = format!("Large file content {}\n", i).repeat(1000);
fs::write(work_dir.join(format!("large-file-{}.txt", i)), content).unwrap();
}
Command::new("git")
.args(["add", "."])
.current_dir(&work_dir)
.output()
.unwrap();
Command::new("git")
.args(["commit", "-m", "Add large files"])
.current_dir(&work_dir)
.output()
.unwrap();
Command::new("git")
.args(["push", "origin", "master"])
.current_dir(&work_dir)
.output()
.unwrap();
// Create archive
let archive_base = git_data_path.join(".archive");
let npub = "npub1diskspace";
let identifier = "large-repo";
let timestamp = current_timestamp();
let archive_path = archive_repository(&repo_path, &archive_base, npub, identifier, timestamp)
.expect("Failed to create archive");
// Measure archive size
let archive_size = fs::metadata(&archive_path).unwrap().len();
assert!(archive_size > 1000, "Archive should be reasonably large");
// Create metadata file
let metadata_path = PathBuf::from(format!("{}.json", archive_path.display()));
fs::write(&metadata_path, b"{}").unwrap();
let metadata_size = fs::metadata(&metadata_path).unwrap().len();
// Create holding database and store expired event
let holding_db = HoldingDatabase::new(&git_data_path, DatabaseBackend::Memory)
.await
.expect("Failed to create holding database");
let keys = Keys::generate();
let deletion_keys = Keys::generate();
let deletion_event_id = EventBuilder::text_note("deletion request")
.sign_with_keys(&deletion_keys)
.unwrap()
.id;
let now = current_timestamp();
let event = EventBuilder::text_note("event to be deleted")
.sign_with_keys(&keys)
.unwrap();
let metadata = DeletionMetadata {
deletion_timestamp: now - 7200,
deletion_event_id,
expiry_timestamp: now - 3600,
archive_path: Some(format!(
".archive/{}/{}-{}.tar.gz",
npub, identifier, timestamp
)),
};
holding_db.store_event(&event, metadata).await.unwrap();
// Measure disk usage before cleanup
let disk_usage_before = get_dir_size(&archive_base);
assert!(
disk_usage_before > 0,
"Should have disk usage before cleanup"
);
// Run cleanup
let result = ngit_grasp::database::cleanup_archived_data(&holding_db, &git_data_path)
.await
.expect("Cleanup should succeed");
// Verify disk space was reclaimed
// Allow small variance due to file system metadata
let expected_bytes = archive_size + metadata_size;
assert!(
result.bytes_reclaimed >= expected_bytes - 10
&& result.bytes_reclaimed <= expected_bytes + 10,
"Should reclaim approximately {} bytes (got {})",
expected_bytes,
result.bytes_reclaimed
);
// Measure disk usage after cleanup
let disk_usage_after = if archive_base.exists() {
get_dir_size(&archive_base)
} else {
0
};
assert!(
disk_usage_after < disk_usage_before,
"Disk usage should decrease after cleanup"
);
// Verify files are gone
assert!(!archive_path.exists(), "Archive file should be deleted");
assert!(!metadata_path.exists(), "Metadata file should be deleted");
}
#[tokio::test]
async fn test_cleanup_multiple_expired_events() {
use ngit_grasp::config::DatabaseBackend;
use ngit_grasp::database::{DeletionMetadata, HoldingDatabase};
let temp_dir = TempDir::new().unwrap();
let git_data_path = temp_dir.path().join("git-data");
fs::create_dir_all(&git_data_path).unwrap();
let holding_db = HoldingDatabase::new(&git_data_path, DatabaseBackend::Memory)
.await
.expect("Failed to create holding database");
// Create multiple archive files
let archive_dir = git_data_path.join(".archive/npub1multiple");
fs::create_dir_all(&archive_dir).unwrap();
let now = current_timestamp();
let mut expected_bytes = 0u64;
// Create 5 expired events with archives
for i in 0..5 {
let archive_path = archive_dir.join(format!("repo-{}-{}.tar.gz", i, now));
let metadata_path = archive_dir.join(format!("repo-{}-{}.tar.gz.json", i, now));
let archive_content = format!("fake archive data {}", i);
fs::write(&archive_path, archive_content.as_bytes()).unwrap();
fs::write(&metadata_path, b"{}").unwrap();
expected_bytes += archive_content.len() as u64 + 2; // +2 for "{}"
let keys = Keys::generate();
let deletion_keys = Keys::generate();
let deletion_event_id = EventBuilder::text_note("deletion request")
.sign_with_keys(&deletion_keys)
.unwrap()
.id;
let event = EventBuilder::text_note(format!("event {}", i))
.sign_with_keys(&keys)
.unwrap();
let metadata = DeletionMetadata {
deletion_timestamp: now - 7200,
deletion_event_id,
expiry_timestamp: now - 3600,
archive_path: Some(format!(".archive/npub1multiple/repo-{}-{}.tar.gz", i, now)),
};
holding_db.store_event(&event, metadata).await.unwrap();
}
// Verify all events are expired
let expired = holding_db.query_expired(now).await.unwrap();
assert_eq!(expired.len(), 5, "Should have 5 expired events");
// Run cleanup
let result = ngit_grasp::database::cleanup_archived_data(&holding_db, &git_data_path)
.await
.expect("Cleanup should succeed");
// Verify all files were cleaned up
// Note: events_deleted will be 0 until delete_event is fully implemented
assert_eq!(result.archives_deleted, 5, "Should delete 5 archives");
assert_eq!(result.metadata_deleted, 5, "Should delete 5 metadata files");
assert_eq!(
result.bytes_reclaimed, expected_bytes,
"Should reclaim expected bytes"
);
// Note: Event deletion from holding database is not yet implemented
// When implemented, we should verify: expired_after.len() == 0
}
#[tokio::test]
async fn test_cleanup_preserves_non_expired_events() {
use ngit_grasp::config::DatabaseBackend;
use ngit_grasp::database::{DeletionMetadata, HoldingDatabase};
let temp_dir = TempDir::new().unwrap();
let git_data_path = temp_dir.path().join("git-data");
fs::create_dir_all(&git_data_path).unwrap();
let holding_db = HoldingDatabase::new(&git_data_path, DatabaseBackend::Memory)
.await
.expect("Failed to create holding database");
let archive_dir = git_data_path.join(".archive/npub1preserve");
fs::create_dir_all(&archive_dir).unwrap();
let now = current_timestamp();
// Create one expired event
let expired_archive = archive_dir.join("expired-123.tar.gz");
let expired_metadata = archive_dir.join("expired-123.tar.gz.json");
fs::write(&expired_archive, b"expired archive").unwrap();
fs::write(&expired_metadata, b"{}").unwrap();
let keys1 = Keys::generate();
let deletion_keys1 = Keys::generate();
let deletion_event_id1 = EventBuilder::text_note("deletion request 1")
.sign_with_keys(&deletion_keys1)
.unwrap()
.id;
let event1 = EventBuilder::text_note("expired event")
.sign_with_keys(&keys1)
.unwrap();
let metadata1 = DeletionMetadata {
deletion_timestamp: now - 7200,
deletion_event_id: deletion_event_id1,
expiry_timestamp: now - 3600, // Expired
archive_path: Some(".archive/npub1preserve/expired-123.tar.gz".to_string()),
};
holding_db.store_event(&event1, metadata1).await.unwrap();
// Create one non-expired event
let active_archive = archive_dir.join("active-456.tar.gz");
let active_metadata = archive_dir.join("active-456.tar.gz.json");
fs::write(&active_archive, b"active archive").unwrap();
fs::write(&active_metadata, b"{}").unwrap();
let keys2 = Keys::generate();
let deletion_keys2 = Keys::generate();
let deletion_event_id2 = EventBuilder::text_note("deletion request 2")
.sign_with_keys(&deletion_keys2)
.unwrap()
.id;
let event2 = EventBuilder::text_note("active event")
.sign_with_keys(&keys2)
.unwrap();
let metadata2 = DeletionMetadata {
deletion_timestamp: now,
deletion_event_id: deletion_event_id2,
expiry_timestamp: now + 3600, // Not expired yet
archive_path: Some(".archive/npub1preserve/active-456.tar.gz".to_string()),
};
holding_db.store_event(&event2, metadata2).await.unwrap();
// Run cleanup
let result = ngit_grasp::database::cleanup_archived_data(&holding_db, &git_data_path)
.await
.expect("Cleanup should succeed");
// Verify only expired files were cleaned up
// Note: events_deleted will be 0 until delete_event is fully implemented
assert_eq!(
result.archives_deleted, 1,
"Should delete only 1 expired archive"
);
// Verify expired files are gone
assert!(
!expired_archive.exists(),
"Expired archive should be deleted"
);
assert!(
!expired_metadata.exists(),
"Expired metadata should be deleted"
);
// Verify active files still exist
assert!(active_archive.exists(), "Active archive should still exist");
assert!(
active_metadata.exists(),
"Active metadata should still exist"
);
// Verify both events are still in database (since delete_event is not implemented)
// When delete_event is implemented, we should verify:
// - expired.len() == 0 (expired event deleted)
// - future_expired.len() == 1 (active event preserved)
let all_events = holding_db.query_expired(now + 7200).await.unwrap();
assert_eq!(
all_events.len(),
2,
"Both events should still be in database (delete_event not yet implemented)"
);
}