KeiSeiKit-1.0/_primitives/_rust/kei-memory-sqlite/tests/sqlite_smoke.rs
Parfii-bot a4e667de10 KeiSeiKit-public — clean state
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.
2026-05-01 12:09:03 +08:00

164 lines
5.5 KiB
Rust

// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 <author org>
//!
//! End-to-end smoke tests for [`kei_memory_sqlite::SqliteBackend`].
//! All tests use `SqliteStore::from_memory()` so no tempfile is needed
//! and the suite has no external dependencies.
use kei_memory_sqlite::{SqliteBackend, SqliteStore};
use kei_runtime_core::dna::{Dna, DnaBuilder};
use kei_runtime_core::traits::memory::{MemoryBackend, MemoryItem, MemoryQuery};
use std::sync::Arc;
fn fresh_dna(role: &str) -> Dna {
DnaBuilder::new(role)
.caps(["PR", "AP", "SQ"])
.scope("keiseikit.dev/primitives/kei-memory-sqlite")
.body(b"sqlite-v3")
.build()
.expect("dna build")
}
fn fresh_backend() -> SqliteBackend {
let store = Arc::new(SqliteStore::from_memory().expect("open"));
SqliteBackend::new(fresh_dna("primitive"), None, store)
}
fn make_item(kind: &str, key: &str, ts: i64, tags: &[&str]) -> MemoryItem {
MemoryItem {
dna: fresh_dna("trace"),
parent_dna: None,
kind: kind.to_string(),
key: key.to_string(),
value: serde_json::json!({"k": key}).to_string(),
tags: tags.iter().map(|s| s.to_string()).collect(),
created_at_ms: ts,
}
}
#[tokio::test]
async fn store_and_query_roundtrip() {
let b = fresh_backend();
let item = make_item("trace", "session-1", 1000, &["sleep", "rem"]);
b.store(&item).await.expect("store");
let q = MemoryQuery::default();
let got = b.query(&q).await.expect("query");
assert_eq!(got.len(), 1, "single insert must return one row");
assert_eq!(got[0].key, "session-1");
assert_eq!(got[0].kind, "trace");
assert_eq!(got[0].tags, vec!["sleep".to_string(), "rem".to_string()]);
}
#[tokio::test]
async fn key_prefix_filter() {
let b = fresh_backend();
b.store(&make_item("trace", "alpha-1", 100, &[])).await.unwrap();
b.store(&make_item("trace", "alpha-2", 200, &[])).await.unwrap();
b.store(&make_item("trace", "beta-1", 300, &[])).await.unwrap();
let q = MemoryQuery {
key_prefix: Some("alpha-".into()),
..Default::default()
};
let got = b.query(&q).await.unwrap();
assert_eq!(got.len(), 2, "alpha- prefix selects 2 of 3");
// DESC by created_at_ms.
assert_eq!(got[0].key, "alpha-2");
assert_eq!(got[1].key, "alpha-1");
}
#[tokio::test]
async fn tag_any_exact_token_filter() {
let b = fresh_backend();
// "rem" must NOT match "remix" — exact-token boundary.
b.store(&make_item("trace", "a", 100, &["rem"])).await.unwrap();
b.store(&make_item("trace", "b", 200, &["remix"])).await.unwrap();
b.store(&make_item("trace", "c", 300, &["sleep", "rem"])).await.unwrap();
let q = MemoryQuery {
tag_any: vec!["rem".into()],
..Default::default()
};
let got = b.query(&q).await.unwrap();
assert_eq!(got.len(), 2, "tag 'rem' matches a and c, NOT b (remix)");
let keys: Vec<_> = got.iter().map(|i| i.key.clone()).collect();
assert!(keys.contains(&"a".to_string()));
assert!(keys.contains(&"c".to_string()));
assert!(!keys.contains(&"b".to_string()));
}
#[tokio::test]
async fn limit_clamps_result_count() {
let b = fresh_backend();
for i in 0..5 {
b.store(&make_item("trace", &format!("k-{i}"), 100 + i, &[]))
.await
.unwrap();
}
let q = MemoryQuery {
limit: Some(2),
..Default::default()
};
let got = b.query(&q).await.unwrap();
assert_eq!(got.len(), 2);
// DESC: newest first.
assert_eq!(got[0].key, "k-4");
assert_eq!(got[1].key, "k-3");
}
#[tokio::test]
async fn compact_returns_deleted_count() {
let b = fresh_backend();
b.store(&make_item("trace", "old-1", 100, &[])).await.unwrap();
b.store(&make_item("trace", "old-2", 200, &[])).await.unwrap();
b.store(&make_item("trace", "new-1", 1000, &[])).await.unwrap();
let n = b.compact(500).await.expect("compact");
assert_eq!(n, 2, "two items strictly older than 500 must be removed");
let remaining = b.query(&MemoryQuery::default()).await.unwrap();
assert_eq!(remaining.len(), 1);
assert_eq!(remaining[0].key, "new-1");
}
#[tokio::test]
async fn kind_filter_isolates_namespace() {
let b = fresh_backend();
b.store(&make_item("trace", "x", 100, &[])).await.unwrap();
b.store(&make_item("concept", "y", 200, &[])).await.unwrap();
b.store(&make_item("report", "z", 300, &[])).await.unwrap();
let q = MemoryQuery {
kind: Some("concept".into()),
..Default::default()
};
let got = b.query(&q).await.unwrap();
assert_eq!(got.len(), 1);
assert_eq!(got[0].kind, "concept");
assert_eq!(got[0].key, "y");
}
#[tokio::test]
async fn since_ms_filter_inclusive_lower_bound() {
let b = fresh_backend();
b.store(&make_item("trace", "old", 100, &[])).await.unwrap();
b.store(&make_item("trace", "boundary", 500, &[])).await.unwrap();
b.store(&make_item("trace", "new", 999, &[])).await.unwrap();
let q = MemoryQuery {
since_ms: Some(500),
..Default::default()
};
let got = b.query(&q).await.unwrap();
assert_eq!(got.len(), 2, "since_ms is inclusive lower bound");
let keys: Vec<_> = got.iter().map(|i| i.key.clone()).collect();
assert!(keys.contains(&"boundary".to_string()));
assert!(keys.contains(&"new".to_string()));
}
#[tokio::test]
async fn mirror_to_remote_returns_provider_error() {
let b = fresh_backend();
let r = b.mirror_to_remote("ssh://example/path.git").await;
assert!(r.is_err(), "mirror_to_remote must surface a Provider error");
}