OXYGENHUB
# 98a16830a3d3 spectra / tests / cli_project.rs
3.6 KiB · Rust · last changed Projects with relations from .oxymodules, presence and sessions; projections skip malformed payloads (#5) 2 days ago source blame history raw
1
//! Projects from the command line (spec §23.1): relations come from
2
//! `.oxymodules`, matched by remote, whichever is registered first; an agent
3
//! needs a registered project.
4
5
use spectra_core::keys::KeyPair;
6
use std::path::Path;
7
use std::process::{Command, Output};
8
9
fn hermetic(dir: &Path) -> Command {
10
    let mut c = Command::new(env!("CARGO_BIN_EXE_spectra"));
11
    c.env("SPECTRA_DATA", dir.join("data"))
12
        .env("OXY_HOME", dir.join("owner"))
13
        .env("HOME", dir)
14
        .env("XDG_DATA_HOME", dir.join("xdg"))
15
        .env("NO_COLOR", "1");
16
    c
17
}
18
19
fn run(dir: &Path, args: &[&str]) -> Output {
20
    hermetic(dir).args(args).output().unwrap()
21
}
22
23
fn text(o: &Output) -> String {
24
    format!("{}{}", String::from_utf8_lossy(&o.stdout), String::from_utf8_lossy(&o.stderr))
25
}
26
27
/// A checkout as `oxy` leaves one: an id, a remote, and the subrepos it pins.
28
fn checkout(at: &Path, remote: &str, pins: &[&str]) {
29
    std::fs::create_dir_all(at.join(".oxy")).unwrap();
30
    std::fs::write(at.join(".oxy/id"), "0123456789abcdef0123456789abcdef").unwrap();
31
    std::fs::write(at.join(".oxy/config"), format!("remote.origin.url = \"{remote}\"\n")).unwrap();
32
    let modules: String = pins.iter().enumerate().map(|(i, u)| format!("[deps/{i}]\nurl = {u}\nbookmark = main\n")).collect();
33
    std::fs::write(at.join(".oxymodules"), modules).unwrap();
34
}
35
36
fn setup() -> tempfile::TempDir {
37
    let dir = tempfile::tempdir().unwrap();
38
    KeyPair::create_home(&dir.path().join("owner"), "Andy McCall", "").unwrap();
39
    assert!(run(dir.path(), &["init"]).status.success());
40
    dir
41
}
42
43
fn projects(d: &Path) -> serde_json::Value {
44
    serde_json::from_slice(&run(d, &["project", "list", "--json"]).stdout).unwrap()
45
}
46
47
#[test]
48
fn relations_are_found_whichever_project_comes_first() {
49
    for hub_first in [false, true] {
50
        let dir = setup();
51
        let d = dir.path();
52
        checkout(&d.join("oxygen"), "https://hub.example/andy/oxygen", &[]);
53
        checkout(&d.join("oxygenhub"), "https://hub.example/andy/oxygenhub", &["https://hub.example/andy/oxygen"]);
54
        let order = if hub_first { ["oxygenhub", "oxygen"] } else { ["oxygen", "oxygenhub"] };
55
        for p in order {
56
            let o = run(d, &["project", "add", d.join(p).to_str().unwrap()]);
57
            assert!(o.status.success(), "{}", text(&o));
58
        }
59
        let list = projects(d);
60
        let hub = list.as_array().unwrap().iter().find(|p| p["name"] == "oxygenhub").unwrap();
61
        let oxy = list.as_array().unwrap().iter().find(|p| p["name"] == "oxygen").unwrap();
62
        assert_eq!(hub["depends_on"], serde_json::json!(["oxygen"]), "hub first: {hub_first}");
63
        assert_eq!(oxy["depended_on_by"], serde_json::json!(["oxygenhub"]));
64
        assert!(run(d, &["admin", "check"]).status.success());
65
        assert!(run(d, &["admin", "verify"]).status.success());
66
    }
67
}
68
69
#[test]
70
fn a_project_is_registered_once_and_an_agent_needs_one() {
71
    let dir = setup();
72
    let d = dir.path();
73
    checkout(&d.join("oxygen"), "https://hub.example/andy/oxygen", &[]);
74
    assert!(run(d, &["project", "add", d.join("oxygen").to_str().unwrap()]).status.success());
75
    let again = run(d, &["project", "add", d.join("oxygen").to_str().unwrap(), "--name", "other"]);
76
    assert!(!again.status.success() && text(&again).contains("already a project"), "{}", text(&again));
77
    let o = run(
78
        d,
79
        &["agent", "add", "kimi", "--kind", "kimi-code", "--project", "nowhere", "--home", d.join("kimi").to_str().unwrap(), "--wait", "0"],
80
    );
81
    assert!(!o.status.success() && text(&o).contains("no project named nowhere"), "{}", text(&o));
82
    assert!(!d.join("kimi").exists(), "no key is made for an agent that cannot be added");
83
}