Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
166 lines
5.1 KiB
Rust
166 lines
5.1 KiB
Rust
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright 2026 <author org>
|
|
//!
|
|
//! Smoke tests for `SledBackend`. Each test gets its own `tempdir` so
|
|
//! sled file-locking doesn't cross-contaminate.
|
|
|
|
use kei_memory_sled::SledBackend;
|
|
use kei_runtime_core::traits::memory::{MemoryBackend, MemoryItem, MemoryQuery};
|
|
use kei_runtime_core::{DnaBuilder, HasDna};
|
|
use tempfile::tempdir;
|
|
|
|
fn make_item(kind: &str, key: &str, ts_ms: i64, tags: &[&str]) -> MemoryItem {
|
|
let dna = DnaBuilder::new("trace")
|
|
.cap("MM")
|
|
.scope("test")
|
|
.body(key.as_bytes())
|
|
.build()
|
|
.expect("dna");
|
|
MemoryItem {
|
|
dna,
|
|
parent_dna: None,
|
|
kind: kind.into(),
|
|
key: key.into(),
|
|
value: serde_json::json!({"k": key}).to_string(),
|
|
tags: tags.iter().map(|s| (*s).to_string()).collect(),
|
|
created_at_ms: ts_ms,
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn store_and_query_roundtrip() {
|
|
let dir = tempdir().unwrap();
|
|
let backend = SledBackend::from_path(dir.path(), None).unwrap();
|
|
|
|
let item = make_item("trace", "session-1", 1_000, &["alpha"]);
|
|
backend.store(&item).await.unwrap();
|
|
|
|
let got = backend.query(&MemoryQuery::default()).await.unwrap();
|
|
assert_eq!(got.len(), 1);
|
|
assert_eq!(got[0].key, "session-1");
|
|
assert_eq!(got[0].kind, "trace");
|
|
assert_eq!(got[0].tags, vec!["alpha"]);
|
|
assert_eq!(backend.backend_name(), "sled");
|
|
// DNA caps include SL marker.
|
|
assert!(backend.dna().caps().contains("SL"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn key_prefix_filter() {
|
|
let dir = tempdir().unwrap();
|
|
let backend = SledBackend::from_path(dir.path(), None).unwrap();
|
|
backend
|
|
.store(&make_item("trace", "alpha-1", 100, &[]))
|
|
.await
|
|
.unwrap();
|
|
backend
|
|
.store(&make_item("trace", "alpha-2", 200, &[]))
|
|
.await
|
|
.unwrap();
|
|
backend
|
|
.store(&make_item("trace", "beta-1", 300, &[]))
|
|
.await
|
|
.unwrap();
|
|
|
|
let q = MemoryQuery {
|
|
key_prefix: Some("alpha-".into()),
|
|
..Default::default()
|
|
};
|
|
let got = backend.query(&q).await.unwrap();
|
|
assert_eq!(got.len(), 2);
|
|
assert!(got.iter().all(|it| it.key.starts_with("alpha-")));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn tag_any_filter() {
|
|
let dir = tempdir().unwrap();
|
|
let backend = SledBackend::from_path(dir.path(), None).unwrap();
|
|
backend
|
|
.store(&make_item("note", "a", 10, &["red"]))
|
|
.await
|
|
.unwrap();
|
|
backend
|
|
.store(&make_item("note", "b", 20, &["green"]))
|
|
.await
|
|
.unwrap();
|
|
backend
|
|
.store(&make_item("note", "c", 30, &["red", "blue"]))
|
|
.await
|
|
.unwrap();
|
|
|
|
let q = MemoryQuery {
|
|
tag_any: vec!["red".into()],
|
|
..Default::default()
|
|
};
|
|
let got = backend.query(&q).await.unwrap();
|
|
assert_eq!(got.len(), 2);
|
|
let keys: Vec<&str> = got.iter().map(|it| it.key.as_str()).collect();
|
|
assert!(keys.contains(&"a"));
|
|
assert!(keys.contains(&"c"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn limit_truncates_desc() {
|
|
let dir = tempdir().unwrap();
|
|
let backend = SledBackend::from_path(dir.path(), None).unwrap();
|
|
for i in 0..5 {
|
|
backend
|
|
.store(&make_item("trace", &format!("k{i}"), 1000 + i, &[]))
|
|
.await
|
|
.unwrap();
|
|
}
|
|
let q = MemoryQuery {
|
|
limit: Some(2),
|
|
..Default::default()
|
|
};
|
|
let got = backend.query(&q).await.unwrap();
|
|
assert_eq!(got.len(), 2);
|
|
// DESC by ts → newest two are k4, k3.
|
|
assert_eq!(got[0].key, "k4");
|
|
assert_eq!(got[1].key, "k3");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn compact_returns_count_of_older() {
|
|
let dir = tempdir().unwrap();
|
|
let backend = SledBackend::from_path(dir.path(), None).unwrap();
|
|
backend.store(&make_item("trace", "old1", 100, &[])).await.unwrap();
|
|
backend.store(&make_item("trace", "old2", 200, &[])).await.unwrap();
|
|
backend.store(&make_item("trace", "new1", 1000, &[])).await.unwrap();
|
|
|
|
let n = backend.compact(500).await.unwrap();
|
|
assert_eq!(n, 2, "two items strictly older than 500 ms");
|
|
|
|
// No-op delete: items are still present.
|
|
let all = backend.query(&MemoryQuery::default()).await.unwrap();
|
|
assert_eq!(all.len(), 3);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn kind_filter_isolates_namespaces() {
|
|
let dir = tempdir().unwrap();
|
|
let backend = SledBackend::from_path(dir.path(), None).unwrap();
|
|
backend.store(&make_item("trace", "x", 1, &[])).await.unwrap();
|
|
backend.store(&make_item("concept", "x", 2, &[])).await.unwrap();
|
|
backend.store(&make_item("report", "x", 3, &[])).await.unwrap();
|
|
|
|
let q = MemoryQuery {
|
|
kind: Some("concept".into()),
|
|
..Default::default()
|
|
};
|
|
let got = backend.query(&q).await.unwrap();
|
|
assert_eq!(got.len(), 1);
|
|
assert_eq!(got[0].kind, "concept");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn mirror_to_remote_is_unsupported() {
|
|
let dir = tempdir().unwrap();
|
|
let backend = SledBackend::from_path(dir.path(), None).unwrap();
|
|
let err = backend
|
|
.mirror_to_remote("ssh://example/repo.git")
|
|
.await
|
|
.unwrap_err();
|
|
let msg = format!("{err}");
|
|
assert!(msg.contains("mirror_to_remote") || msg.contains("RULE 0.15"));
|
|
}
|