133 lines
5.6 KiB
Rust
133 lines
5.6 KiB
Rust
// By Nic Weyand!
|
|
//! Active coverage, delta masking, and audit-retention acceptance proof.
|
|
#[allow(dead_code)]
|
|
mod common;
|
|
|
|
use argand_site_registry::{
|
|
model::{CoverageKind, Format, Source, SourceCoverage},
|
|
query::Registry,
|
|
store,
|
|
};
|
|
use serde_json::json;
|
|
use std::fs;
|
|
|
|
#[test]
|
|
#[allow(clippy::too_many_lines)] // One scenario proves full, delta, tombstone, active, and audit behavior.
|
|
fn typed_full_and_delta_replace_active_records_but_preserve_audit_history() -> anyhow::Result<()> {
|
|
let root = tempfile::tempdir()?;
|
|
let mut db = common::fixture(root.path())?;
|
|
let original_bytes = serde_json::to_vec(&common::wikidata())?;
|
|
let original = common::manifest(Source::Wikidata, Format::WikidataEntities, &original_bytes)?;
|
|
|
|
let full_bytes = serde_json::to_vec(&common::wikidata())?;
|
|
let mut full = common::manifest(Source::Wikidata, Format::WikidataEntities, &full_bytes)?;
|
|
full.schema = "argand.site-source/v2".into();
|
|
full.snapshot = "synthetic-full-v2".into();
|
|
full.scope = "entities-full".into();
|
|
full.retrieved_at += chrono::Duration::hours(1);
|
|
full.coverage = Some(SourceCoverage {
|
|
collection: "entities".into(),
|
|
kind: CoverageKind::Full,
|
|
partition: None,
|
|
base: None,
|
|
sequence: None,
|
|
supersedes: vec![original.id()?],
|
|
});
|
|
let full_path = root.path().join("wikidata-full.json");
|
|
fs::write(&full_path, &full_bytes)?;
|
|
store::import(&mut db, &full, &full_path)?;
|
|
|
|
let delta_bytes = serde_json::to_vec(&json!({"entities":{"Q355":common::entity(
|
|
"Q355",
|
|
"Facebook",
|
|
&["Meta FB"],
|
|
&["https://facebook.com/"]
|
|
)}}))?;
|
|
let mut delta = common::manifest(Source::Wikidata, Format::WikidataEntities, &delta_bytes)?;
|
|
delta.schema = "argand.site-source/v2".into();
|
|
delta.snapshot = "synthetic-delta-v2".into();
|
|
delta.scope = "entities-delta-1".into();
|
|
delta.retrieved_at += chrono::Duration::hours(2);
|
|
delta.coverage = Some(SourceCoverage {
|
|
collection: "entities".into(),
|
|
kind: CoverageKind::Delta,
|
|
partition: None,
|
|
base: Some(full.id()?),
|
|
sequence: Some(1),
|
|
supersedes: vec![full.id()?],
|
|
});
|
|
let delta_path = root.path().join("wikidata-delta.json");
|
|
fs::write(&delta_path, &delta_bytes)?;
|
|
store::import(&mut db, &delta, &delta_path)?;
|
|
|
|
let generation = root.path().join("generation");
|
|
argand_site_registry::build::build_with_policy(
|
|
&db,
|
|
&generation,
|
|
&argand_site_registry::policy::ReviewPolicy::legacy_compatible(),
|
|
)?;
|
|
let pin = argand_site_registry::file_digest(&generation.join("COMPLETE.json"))?;
|
|
let registry = Registry::open(&generation, &pin)?;
|
|
assert_eq!(registry.lookup("FB", 20)?.total_entities, 0);
|
|
assert_eq!(registry.lookup("Meta FB", 20)?.total_entities, 1);
|
|
assert_eq!(registry.lookup("Atlas", 20)?.total_entities, 1);
|
|
let stats = registry.stats(common::timestamp()?)?;
|
|
assert_eq!(stats.selected_sources, 6);
|
|
assert_eq!(stats.superseded_source_snapshots, 1);
|
|
|
|
let active = root.path().join("active.jsonl");
|
|
let audit = root.path().join("audit.jsonl");
|
|
argand_site_registry::release::export(®istry, &active, false)?;
|
|
argand_site_registry::release::export_audit(®istry, &audit, false)?;
|
|
let active = fs::read_to_string(active)?;
|
|
let audit = fs::read_to_string(audit)?;
|
|
assert!(!active.contains("\"text\":\"FB\""));
|
|
assert!(active.contains("\"text\":\"Meta FB\""));
|
|
assert!(audit.contains("\"selection_state\":\"superseded\""));
|
|
assert!(audit.contains("\"text\":\"FB\""));
|
|
|
|
let tombstone_bytes = serde_json::to_vec(&json!({"entities":{"Q355":{
|
|
"id":"Q355",
|
|
"missing":""
|
|
}}}))?;
|
|
let mut tombstone =
|
|
common::manifest(Source::Wikidata, Format::WikidataEntities, &tombstone_bytes)?;
|
|
tombstone.schema = "argand.site-source/v2".into();
|
|
tombstone.snapshot = "synthetic-delta-v2-tombstone".into();
|
|
tombstone.scope = "entities-delta-2".into();
|
|
tombstone.retrieved_at += chrono::Duration::hours(3);
|
|
tombstone.coverage = Some(SourceCoverage {
|
|
collection: "entities".into(),
|
|
kind: CoverageKind::Delta,
|
|
partition: None,
|
|
base: Some(delta.id()?),
|
|
sequence: Some(2),
|
|
supersedes: vec![delta.id()?],
|
|
});
|
|
let tombstone_path = root.path().join("wikidata-tombstone.json");
|
|
fs::write(&tombstone_path, &tombstone_bytes)?;
|
|
store::import(&mut db, &tombstone, &tombstone_path)?;
|
|
let retired_path = root.path().join("retired");
|
|
argand_site_registry::build::build_with_policy(
|
|
&db,
|
|
&retired_path,
|
|
&argand_site_registry::policy::ReviewPolicy::legacy_compatible(),
|
|
)?;
|
|
let retired_pin = argand_site_registry::file_digest(&retired_path.join("COMPLETE.json"))?;
|
|
let retired = Registry::open(&retired_path, &retired_pin)?;
|
|
assert_eq!(retired.lookup("Facebook", 20)?.total_entities, 0);
|
|
assert_eq!(retired.lookup("Meta FB", 20)?.total_entities, 0);
|
|
let retired_active = root.path().join("retired-active.jsonl");
|
|
let retired_audit = root.path().join("retired-audit.jsonl");
|
|
argand_site_registry::release::export(&retired, &retired_active, false)?;
|
|
argand_site_registry::release::export_audit(&retired, &retired_audit, false)?;
|
|
assert!(
|
|
!fs::read_to_string(retired_active)?
|
|
.contains("\"subject\":\"argand:entity:wikidata:Q355\"")
|
|
);
|
|
let retired_audit = fs::read_to_string(retired_audit)?;
|
|
assert!(retired_audit.contains("\"type\":\"tombstone\""));
|
|
assert!(retired_audit.contains("\"selection_state\":\"tombstoned\""));
|
|
assert!(retired_audit.contains("\"source_identifier\":\"Q355\""));
|
|
Ok(())
|
|
}
|