| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
#![allow(dead_code)] |
| 5 |
|
| 6 |
use spectra_core::conversation::{default_channel_events, project_channel_event, Author, Signer}; |
| 7 |
use spectra_core::ids::now_ms; |
| 8 |
use spectra_core::keys::KeyPair; |
| 9 |
use spectra_core::ledger::Ledger; |
| 10 |
use spectra_core::principals::{identity_payload, signed_event, Principal, Principals}; |
| 11 |
use std::path::PathBuf; |
| 12 |
|
| 13 |
pub struct World { |
| 14 |
pub dir: tempfile::TempDir, |
| 15 |
pub owner_home: PathBuf, |
| 16 |
pub ledger: Ledger, |
| 17 |
pub owner: KeyPair, |
| 18 |
} |
| 19 |
|
| 20 |
impl World { |
| 21 |
pub async fn new(projects: &[&str]) -> World { |
| 22 |
let dir = tempfile::tempdir().unwrap(); |
| 23 |
let owner_home = dir.path().join("owner"); |
| 24 |
let owner = KeyPair::create_home(&owner_home, "Andy McCall", "[email protected]").unwrap(); |
| 25 |
let data = dir.path().join("data"); |
| 26 |
std::fs::create_dir_all(&data).unwrap(); |
| 27 |
let instance = KeyPair::instance(&data, "laptop").unwrap(); |
| 28 |
let ledger = Ledger::open(&data.join("spectra.db"), Some(instance.clone())).await.unwrap(); |
| 29 |
let mut first = vec![ |
| 30 |
signed_event(&owner, "human", "identity.owner", identity_payload(&owner.identity)), |
| 31 |
signed_event(&instance, "system", "identity.instance", identity_payload(&instance.identity)), |
| 32 |
]; |
| 33 |
first.extend(default_channel_events()); |
| 34 |
ledger.append(first).await.unwrap(); |
| 35 |
for p in projects { |
| 36 |
let payload = serde_json::json!({ "name": p, "path": format!("/src/{p}"), "modules": [] }); |
| 37 |
ledger.append(vec![signed_event(&owner, "human", "project.added", payload), project_channel_event(p)]).await.unwrap(); |
| 38 |
} |
| 39 |
World { dir, owner_home, ledger, owner } |
| 40 |
} |
| 41 |
|
| 42 |
|
| 43 |
pub async fn agent(&mut self, name: &str, project: &str) -> KeyPair { |
| 44 |
let agent = KeyPair::generate(name, ""); |
| 45 |
spectra_core::keys::delegate(&self.owner_home, agent.key, None).unwrap(); |
| 46 |
self.owner = KeyPair::load_home(&self.owner_home).unwrap(); |
| 47 |
let mut rotated = identity_payload(&self.owner.identity); |
| 48 |
rotated["chain"] = serde_json::json!([]); |
| 49 |
let mut p = identity_payload(&agent.identity); |
| 50 |
p["name"] = name.into(); |
| 51 |
p["kind"] = "claude-code".into(); |
| 52 |
p["project"] = project.into(); |
| 53 |
p["owner_name"] = "Andy McCall".into(); |
| 54 |
p["scopes"] = spectra_core::principals::DEFAULT_SCOPES.into(); |
| 55 |
self.ledger |
| 56 |
.append(vec![ |
| 57 |
signed_event(&self.owner, "human", "identity.rotated", rotated), |
| 58 |
signed_event(&self.owner, "human", "agent.added", p), |
| 59 |
]) |
| 60 |
.await |
| 61 |
.unwrap(); |
| 62 |
agent |
| 63 |
} |
| 64 |
|
| 65 |
pub async fn principals(&self) -> Principals { |
| 66 |
Principals::load(&self.ledger).await.unwrap() |
| 67 |
} |
| 68 |
|
| 69 |
|
| 70 |
pub async fn author<'a>(&self, pair: &'a KeyPair) -> Author<'a> { |
| 71 |
let principal: Principal = self.principals().await.principal(&pair.key, now_ms()).unwrap(); |
| 72 |
Author { principal, signer: Signer::Cli(pair) } |
| 73 |
} |
| 74 |
|
| 75 |
pub async fn verify_clean(&self) { |
| 76 |
let v = self.ledger.verify(&mut Principals::default()).await.unwrap(); |
| 77 |
assert!(v.clean(), "{:?}", v.findings); |
| 78 |
assert!(self.ledger.check().await.unwrap().is_empty(), "projections drifted"); |
| 79 |
} |
| 80 |
} |