knowledge-base-crud 0.1.0

Filesystem CRUD operations for a file-based knowledge base
Documentation
use knowledge_base_crud::{ApplyMode, ApplyStatementsOutcome, Error, KnowledgeBase, StatementBatch, StatementResultStatus};
use std::fs;
use std::path::{Path, PathBuf};

fn fixture() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR")).join("../..").join("fixtures/valid/minimal")
}

fn copied_fixture() -> tempfile::TempDir {
    let destination = tempfile::tempdir().unwrap();
    for directory in ["entities", "entity_types", "properties", "references", "entity_context"] {
        let source = fixture().join(directory);
        if !source.exists() {
            continue;
        }
        let target = destination.path().join(directory);
        fs::create_dir(&target).unwrap();
        for entry in fs::read_dir(source).unwrap() {
            let entry = entry.unwrap();
            fs::copy(entry.path(), target.join(entry.file_name())).unwrap();
        }
    }
    fs::copy(fixture().join("id_allocation.yaml"), destination.path().join("id_allocation.yaml")).unwrap();
    destination
}

fn batch() -> StatementBatch {
    serde_yaml::from_str("statements:\n  - entity: Q1\n    property: P1\n    value: { type: integer, value: 123456789 }\n    references: [R1]\n").unwrap()
}

#[test]
fn resource_service_previews_commits_and_rejects_repeated_batches() {
    let root = copied_fixture();
    let knowledge_base = KnowledgeBase::new(root.path());
    let entity_path = root.path().join("entities/Q1.yaml");
    let before = fs::read(&entity_path).unwrap();

    let preview = knowledge_base.entities().apply_statements(&batch(), ApplyMode::Preview).unwrap();
    assert!(matches!(preview, ApplyStatementsOutcome::Previewed(_)));
    assert_eq!(preview.results()[0].status, StatementResultStatus::WouldAdd);
    assert_eq!(fs::read(&entity_path).unwrap(), before);

    let applied = knowledge_base.entities().apply_statements(&batch(), ApplyMode::Commit).unwrap();
    assert!(applied.was_applied());
    assert_eq!(applied.results()[0].status, StatementResultStatus::Added);
    let after = fs::read(&entity_path).unwrap();
    assert_ne!(after, before);

    let repeated = knowledge_base.entities().apply_statements(&batch(), ApplyMode::Commit).unwrap();
    assert!(matches!(repeated, ApplyStatementsOutcome::NotApplied(_)));
    assert_eq!(repeated.results()[0].status, StatementResultStatus::AlreadyPresent);
    assert_eq!(fs::read(entity_path).unwrap(), after);
}

#[test]
fn invalid_repository_is_rejected_before_entity_files_change() {
    let root = copied_fixture();
    fs::write(root.path().join("references/R1.yaml"), "not: a reference\n").unwrap();
    let knowledge_base = KnowledgeBase::new(root.path());
    let entity_path = root.path().join("entities/Q1.yaml");
    let before = fs::read(&entity_path).unwrap();

    let error = knowledge_base.entities().apply_statements(&batch(), ApplyMode::Commit).unwrap_err();

    assert!(matches!(error, Error::Validation(_)));
    assert_eq!(fs::read(entity_path).unwrap(), before);
}