use shiplog_bundle::{write_bundle_manifest, write_zip};
use shiplog_ids::RunId;
use shiplog_schema::bundle::BundleProfile;
use std::fs::File;
#[test]
fn write_bundle_manifest_on_nonexistent_dir_errors() {
let temp = tempfile::tempdir().unwrap();
let nonexistent = temp.path().join("does").join("not").join("exist");
drop(temp);
let run_id = RunId::now("test");
let result = write_bundle_manifest(&nonexistent, &run_id, &BundleProfile::Internal);
assert!(result.is_err(), "should fail on nonexistent directory");
}
#[test]
fn write_bundle_manifest_on_empty_dir_succeeds() {
let dir = tempfile::tempdir().unwrap();
let run_id = RunId::now("test");
let result = write_bundle_manifest(dir.path(), &run_id, &BundleProfile::Internal);
assert!(result.is_ok(), "empty dir should succeed with zero files");
let manifest = result.unwrap();
assert!(
manifest.files.is_empty(),
"no files should be in the manifest"
);
}
#[test]
fn write_bundle_manifest_on_empty_dir_manager_profile_succeeds() {
let dir = tempfile::tempdir().unwrap();
let run_id = RunId::now("test");
let manifest = write_bundle_manifest(dir.path(), &run_id, &BundleProfile::Manager).unwrap();
assert!(manifest.files.is_empty());
assert_eq!(manifest.profile, BundleProfile::Manager);
}
#[test]
fn write_zip_to_invalid_path_errors() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("packet.md"), "# Test").unwrap();
let invalid_zip = dir.path().join("\0bad.zip");
let result = write_zip(dir.path(), &invalid_zip, &BundleProfile::Internal);
assert!(result.is_err(), "null byte path should fail");
}
#[test]
fn write_zip_on_nonexistent_source_dir_errors() {
let dir = tempfile::tempdir().unwrap();
let zip_path = dir.path().join("output.zip");
let temp2 = tempfile::tempdir().unwrap();
let nonexistent = temp2.path().join("no").join("such").join("dir");
drop(temp2);
let result = write_zip(&nonexistent, &zip_path, &BundleProfile::Internal);
assert!(result.is_err(), "non-existent source dir should fail");
}
#[test]
fn write_zip_empty_dir_produces_valid_archive() {
let src_dir = tempfile::tempdir().unwrap();
let out_dir = tempfile::tempdir().unwrap();
let zip_path = out_dir.path().join("empty.zip");
let result = write_zip(src_dir.path(), &zip_path, &BundleProfile::Internal);
assert!(result.is_ok(), "empty dir should produce valid zip");
let file = File::open(&zip_path).unwrap();
let archive = zip::ZipArchive::new(file).unwrap();
assert_eq!(archive.len(), 0, "zip should have 0 entries");
}
#[test]
fn manager_profile_on_dir_without_profiles_subdir_returns_empty() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("packet.md"), "# Top").unwrap();
std::fs::write(dir.path().join("coverage.manifest.json"), "{}").unwrap();
let run_id = RunId::now("test");
let manifest = write_bundle_manifest(dir.path(), &run_id, &BundleProfile::Manager).unwrap();
let paths: Vec<&str> = manifest.files.iter().map(|f| f.path.as_str()).collect();
assert!(
paths.contains(&"coverage.manifest.json"),
"coverage should be included"
);
assert!(
!paths.contains(&"packet.md"),
"top-level packet should not be in manager profile"
);
}
#[test]
fn public_profile_on_dir_without_profiles_subdir_returns_only_coverage() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("packet.md"), "# Top").unwrap();
std::fs::write(dir.path().join("coverage.manifest.json"), "{}").unwrap();
std::fs::write(dir.path().join("ledger.events.jsonl"), "").unwrap();
let run_id = RunId::now("test");
let manifest = write_bundle_manifest(dir.path(), &run_id, &BundleProfile::Public).unwrap();
let paths: Vec<&str> = manifest.files.iter().map(|f| f.path.as_str()).collect();
assert_eq!(paths.len(), 1, "only coverage should be included");
assert!(paths.contains(&"coverage.manifest.json"));
}
#[test]
fn manifest_checksums_are_deterministic() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("packet.md"), "hello world").unwrap();
let run_id = RunId::now("test");
let m1 = write_bundle_manifest(dir.path(), &run_id, &BundleProfile::Internal).unwrap();
std::fs::remove_file(dir.path().join("bundle.manifest.json")).unwrap();
let m2 = write_bundle_manifest(dir.path(), &run_id, &BundleProfile::Internal).unwrap();
assert_eq!(m1.files.len(), m2.files.len());
for (f1, f2) in m1.files.iter().zip(m2.files.iter()) {
assert_eq!(f1.sha256, f2.sha256, "checksums should be deterministic");
assert_eq!(f1.bytes, f2.bytes);
}
}
#[test]
fn write_zip_error_message_includes_path_context() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("packet.md"), "# Test").unwrap();
let temp3 = tempfile::tempdir().unwrap();
let invalid_zip = temp3.path().join("no").join("such").join("output.zip");
drop(temp3);
if let Err(e) = write_zip(dir.path(), &invalid_zip, &BundleProfile::Internal) {
let msg = format!("{e:#}");
assert!(
msg.contains("zip") || msg.contains("create"),
"zip error should have context about what was being done: {msg}"
);
}
}
#[test]
fn write_bundle_manifest_error_chain_has_io_cause() {
let temp4 = tempfile::tempdir().unwrap();
let nonexistent = temp4.path().join("absolutely").join("no").join("such");
drop(temp4);
let run_id = RunId::now("test");
let err = write_bundle_manifest(&nonexistent, &run_id, &BundleProfile::Internal).unwrap_err();
let chain: Vec<String> = err.chain().map(|e| e.to_string()).collect();
assert!(
!chain.is_empty(),
"error chain should contain at least one entry"
);
}
#[test]
fn write_zip_with_nested_directories_succeeds() {
let dir = tempfile::tempdir().unwrap();
let sub = dir.path().join("profiles").join("manager");
std::fs::create_dir_all(&sub).unwrap();
std::fs::write(sub.join("packet.md"), "# Manager").unwrap();
std::fs::write(dir.path().join("packet.md"), "# Top").unwrap();
let zip_path = dir.path().join("nested.zip");
let result = write_zip(dir.path(), &zip_path, &BundleProfile::Internal);
assert!(result.is_ok(), "nested directories should zip successfully");
let file = File::open(&zip_path).unwrap();
let archive = zip::ZipArchive::new(file).unwrap();
assert!(archive.len() >= 2, "zip should contain nested files");
}
#[test]
fn manifest_file_sizes_are_accurate() {
let dir = tempfile::tempdir().unwrap();
let content = "hello world, this is test content";
std::fs::write(dir.path().join("packet.md"), content).unwrap();
let run_id = RunId::now("test");
let manifest = write_bundle_manifest(dir.path(), &run_id, &BundleProfile::Internal).unwrap();
let packet_entry = manifest
.files
.iter()
.find(|f| f.path == "packet.md")
.expect("packet.md should be in manifest");
assert_eq!(
packet_entry.bytes,
content.len() as u64,
"file size should match actual content length"
);
}