Implements hierarchical subject identifiers with scheme-based source tier inference: - ConceptPath type with parse/wire_format, leaf/parent, prefix matching - SourceScheme registry mapping schemes to default SourceClass tiers: - rfc://, fda://, ietf:// → Regulatory (Tier 0) - peer://, pubmed:// → PeerReviewed (Tier 1) - code://, wiki:// → Expert (Tier 3) - blog://, anon:// → Anecdotal (Tier 5) - AliasStore for cross-scheme entity resolution (bidirectional indexing) - API endpoints for concept operations - Battery tests 8, 9 & 10 for concepts, aliases, and advanced signatures - Go SDK updates for concept types and signing Completes Phase 5, advancing to Phase 6 (Distributed Writes). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
134 lines
4.5 KiB
Rust
134 lines
4.5 KiB
Rust
//! Shared test utilities for battery tests.
|
|
|
|
#![allow(clippy::expect_used)] // Test code uses expect() for clear failure messages
|
|
|
|
// Re-export commonly used items for tests
|
|
pub use ed25519_dalek::{Signer, SigningKey};
|
|
pub use rand::rngs::OsRng;
|
|
pub use std::sync::Arc;
|
|
pub use stemedb_core::serde::serialize;
|
|
pub use stemedb_core::testing::AssertionBuilder;
|
|
pub use stemedb_core::types::{
|
|
Assertion, EscalationLevel, EscalationPolicy, LifecycleStage, ObjectValue, ResolutionStatus,
|
|
SignatureEntry, SourceClass,
|
|
};
|
|
pub use stemedb_ingest::worker::{serialize_assertion, IngestWorker};
|
|
pub use stemedb_lens::{
|
|
AsyncLens, LayeredConsensusLens, RecencyLens, SyncLensWrapper, TrustAwareAuthorityLens,
|
|
};
|
|
pub use stemedb_query::{
|
|
apply_source_class_decay, Materializer, Query, QueryEngine, SkepticResolver,
|
|
};
|
|
pub use stemedb_storage::{
|
|
key_codec, EscalationStore, GenericEscalationStore, GenericIndexStore, GenericTrustRankStore,
|
|
GenericVoteStore, HybridStore, IndexStore, KVStore,
|
|
};
|
|
pub use stemedb_wal::Journal;
|
|
pub use tempfile::tempdir;
|
|
pub use tokio::sync::Mutex;
|
|
|
|
/// Create a signed assertion with Ed25519 signature (v1), source class, confidence,
|
|
/// and arbitrary ObjectValue (text/bool, not just number).
|
|
///
|
|
/// The signature signs the message `"{subject}:{predicate}"` which matches
|
|
/// IngestWorker's v1 verification logic.
|
|
pub fn create_signed_assertion_with_source(
|
|
subject: &str,
|
|
predicate: &str,
|
|
object: ObjectValue,
|
|
source_class: SourceClass,
|
|
confidence: f32,
|
|
timestamp: u64,
|
|
) -> Assertion {
|
|
let mut csprng = OsRng;
|
|
let signing_key = SigningKey::generate(&mut csprng);
|
|
let verifying_key = signing_key.verifying_key();
|
|
|
|
let message = format!("{}:{}", subject, predicate);
|
|
let signature = signing_key.sign(message.as_bytes());
|
|
|
|
AssertionBuilder::new()
|
|
.subject(subject)
|
|
.predicate(predicate)
|
|
.object(object)
|
|
.source_class(source_class)
|
|
.confidence(confidence)
|
|
.lifecycle(LifecycleStage::Proposed)
|
|
.timestamp(timestamp)
|
|
.signatures(vec![SignatureEntry {
|
|
agent_id: verifying_key.to_bytes(),
|
|
signature: signature.to_bytes(),
|
|
timestamp,
|
|
version: 1,
|
|
}])
|
|
.build()
|
|
}
|
|
|
|
/// Create a signed assertion with Ed25519 signature (v2 enterprise), source class,
|
|
/// confidence, and arbitrary ObjectValue.
|
|
///
|
|
/// v2 signatures sign the BLAKE3 content hash of the full serialized assertion,
|
|
/// protecting ALL fields from tampering (confidence, object, timestamp, etc.).
|
|
pub fn create_signed_assertion_v2(
|
|
subject: &str,
|
|
predicate: &str,
|
|
object: ObjectValue,
|
|
source_class: SourceClass,
|
|
confidence: f32,
|
|
timestamp: u64,
|
|
) -> Assertion {
|
|
let mut csprng = OsRng;
|
|
let signing_key = SigningKey::generate(&mut csprng);
|
|
let verifying_key = signing_key.verifying_key();
|
|
|
|
// Build assertion WITHOUT signatures first (for hash computation)
|
|
let mut assertion = AssertionBuilder::new()
|
|
.subject(subject)
|
|
.predicate(predicate)
|
|
.object(object)
|
|
.source_class(source_class)
|
|
.confidence(confidence)
|
|
.lifecycle(LifecycleStage::Proposed)
|
|
.timestamp(timestamp)
|
|
.signatures(vec![])
|
|
.build();
|
|
|
|
// Serialize to get content hash
|
|
let bytes = serialize(&assertion).expect("serialize assertion for v2 signing");
|
|
let content_hash = blake3::hash(&bytes);
|
|
|
|
// Sign the content hash (v2 enterprise format)
|
|
let signature = signing_key.sign(content_hash.as_bytes());
|
|
|
|
// Add signature with version 2
|
|
assertion.signatures = vec![SignatureEntry {
|
|
agent_id: verifying_key.to_bytes(),
|
|
signature: signature.to_bytes(),
|
|
timestamp,
|
|
version: 2,
|
|
}];
|
|
|
|
assertion
|
|
}
|
|
|
|
/// Store an assertion directly into H: and SP: keys (bypassing WAL/Ingest).
|
|
///
|
|
/// Used for unit-style tests that don't need the full pipeline.
|
|
pub async fn store_assertion_direct(
|
|
store: &Arc<HybridStore>,
|
|
index_store: &GenericIndexStore<Arc<HybridStore>>,
|
|
assertion: &Assertion,
|
|
) {
|
|
let bytes = serialize(assertion).expect("serialize assertion");
|
|
let hash = blake3::hash(&bytes);
|
|
let hash_hex = hash.to_hex().to_string();
|
|
let key = key_codec::assertion_key(&assertion.subject, &hash_hex);
|
|
store.put(&key, &bytes).await.expect("put assertion");
|
|
|
|
let assertion_hash: [u8; 32] = *hash.as_bytes();
|
|
index_store
|
|
.add_to_indexes(&assertion.subject, &assertion.predicate, &assertion_hash)
|
|
.await
|
|
.expect("add to indexes");
|
|
}
|