OXYGENHUB
◆ main spectra / tests / mentions.rs
2.5 KiB · Rust · last changed @channel and @all mentions 1 day ago source blame history raw
1
//! Mentions by part of a name (spec §10.3): `@kimi` reaches the one agent
2
//! with that part in its name; a part two agents share reaches nobody.
3
//! `@channel` reaches the agents who belong in the channel, `@all` every
4
//! agent.
5
6
mod common;
7
8
use common::{run, setup, text, Serving};
9
use serde_json::{json, Value};
10
use spectra_core::keys::KeyPair;
11
use spectra_mcp::client::{rpc_result, Client};
12
13
async fn call(c: &Client, name: &str, args: Value) -> Value {
14
    let req = json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": name, "arguments": args } });
15
    rpc_result(&c.mcp("2025-11-25", &req).await.unwrap()).unwrap()["result"].clone()
16
}
17
18
#[tokio::test]
19
async fn a_unique_part_of_a_name_is_a_mention() {
20
    let dir = setup();
21
    let d = dir.path();
22
    for p in ["book", "web"] {
23
        assert!(run(d, &["project", "add", p]).status.success());
24
    }
25
    let mut keys = std::collections::HashMap::new();
26
    for (n, kind, p) in [("kimi-book", "kimi-code", "book"), ("claude-book", "claude-code", "book"), ("claude-web", "claude-code", "web")] {
27
        let home = d.join("agents").join(n);
28
        let o = run(d, &["agent", "add", n, "--kind", kind, "--project", p, "--home", home.to_str().unwrap(), "--yes"]);
29
        assert!(o.status.success(), "{}", text(&o));
30
        keys.insert(n, KeyPair::load_home(&home).unwrap());
31
    }
32
    let srv = Serving::start(d);
33
    let c = |n: &str| Client::new(&srv.socket, Some(keys[n].clone()));
34
35
    call(&c("claude-web"), "say", json!({ "body": "@Kimi and @WEB, and @book: hello", "channel": "general" })).await;
36
    let unread = |r: Value| r["structuredContent"]["items"].as_array().map(|a| a.len()).unwrap_or(0);
37
    assert_eq!(unread(call(&c("kimi-book"), "inbox", json!({})).await), 1, "@Kimi reaches kimi-book");
38
    assert_eq!(unread(call(&c("claude-book"), "inbox", json!({})).await), 0, "@book is two agents: nobody");
39
40
    // @channel: the agents who belong in #book; @all: everyone.
41
    assert!(run(d, &["agent", "allow", "claude-web", "--channel", "book"]).status.success());
42
    call(&c("claude-web"), "say", json!({ "body": "@channel the index moved", "channel": "book" })).await;
43
    assert_eq!(unread(call(&c("claude-book"), "inbox", json!({})).await), 1, "@channel in #book reaches claude-book");
44
    assert_eq!(unread(call(&c("kimi-book"), "inbox", json!({})).await), 2, "and kimi-book");
45
    call(&c("kimi-book"), "say", json!({ "body": "@all heads up", "channel": "general" })).await;
46
    assert_eq!(unread(call(&c("claude-web"), "inbox", json!({})).await), 1, "@all reaches claude-web too");
47
    srv.stop();
48
}