OXYGENHUB
◆ v0.1.0 spectra / crates / spectra-core / tests / common / mod.rs
3.5 KiB · Rust · last changed Spectra stands alone: its own identities, signatures and CBOR; projects of any kind of work (#26, #27) 1 day ago source blame history raw
1
//! A world for scenario tests: an owner, the instance, the default channels,
2
//! two projects with their channels, and agents the owner delegated to, all
3
//! recorded as `spectra init`, `project add` and `agent add` record them.
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
    /// Delegate to a new agent and add it, as `spectra agent add` does.
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
    /// An author signing from the command line, as the CLI does.
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
}