Merged 10 upstream commits (MemTable, read-your-writes tests, feed endpoint, security hardening, signed assertions, source registry, dashboard enhancements) and fixed all test failures across the full workspace (2656/2656 passing). Key fixes: - fix(cluster): DashMap deadlock in swim.rs suspect_node/fail_node/alive_node - DashMap::get_mut RefMut + iter() on same map = non-reentrant write lock deadlock - Fix: extract clone in scoped block to drop RefMut before calling update_node_gauges() - 6 previously-hanging SWIM tests now pass in <2s - fix(sim): replace background-task+polling ingestion with synchronous process_pending() - smoke_high_volume_simulation was CPU-starved under 2656 parallel tests - Removed ingestor.start() + wait_until_ingested() pattern throughout sim - All arena functions now call ingestor.process_pending() directly (deterministic) - fix(test): v2 signature helper used wrong hash (rkyv vs canonical compute_content_hash_v2) - fix(test): quota test signed "test" but v1 requires "subject:predicate" format - fix(test): http_validation now accepts 400 for valid-format-but-invalid-crypto hex - fix(test): scale_adaptive micro tier assertions updated (auto_promote upstream change) - config: add nextest.toml with slow-timeout for background-task-tests group Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
136 lines
4.7 KiB
Rust
136 lines
4.7 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::signing::compute_content_hash_v2;
|
|
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();
|
|
|
|
// Compute the canonical v2 content hash (subject:predicate:object:source_hash:...).
|
|
// Must use compute_content_hash_v2, NOT blake3::hash(serialize(&assertion)) —
|
|
// the verifier checks the canonical fields hash, not the rkyv serialization hash.
|
|
let content_hash = compute_content_hash_v2(&assertion);
|
|
|
|
// Sign the content hash (v2 enterprise format)
|
|
let signature = signing_key.sign(&content_hash);
|
|
|
|
// 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");
|
|
}
|