Return compact activation summaries instead of discovery schemas
ci / check (push) Failing after 2m40s

Card: SAU-158
This commit is contained in:
SAU-158 implementer
2026-10-26 22:24:43 +00:00
parent a14f051f97
commit 9a8c143856
3 changed files with 221 additions and 13 deletions
+106 -4
View File
@@ -43,6 +43,86 @@ fn fixture(version: usize) -> PathBuf {
})[version - 1]
.clone()
}
fn fixture_hash(version: usize) -> String {
use sha2::{Digest, Sha256};
format!(
"{:x}",
Sha256::digest(std::fs::read(fixture(version)).unwrap())
)
}
/// PROTOCOL 10.3: summaries omit even large schemas and report sorted main-method diffs.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn summarizes_method_changes() {
let mut old = document("unchanged");
old["methods"] = json!([
{"name":"z_removed"}, {"name":"a_removed"}, {"name":"direct", "params":[]},
{"name":"referenced", "result":{"schema":{"$ref":"#/components/schemas/Node"}}},
{"name":"unrelated", "result":{"schema":{"type":"string"}}}
]);
old["components"]["schemas"] = json!({
"Node":{"properties":{"next":{"$ref":"#/components/schemas/Node"}, "value":{"$ref":"#/components/schemas/Value"}}},
"Value":{"type":"string"}, "Unused":{"type":"string"}
});
let mut next = old.clone();
next["methods"].as_array_mut().unwrap().drain(..2);
next["methods"][0]["params"] = json!([{"name":"limit","schema":{"type":"integer"}}]);
next["methods"].as_array_mut().unwrap().extend(
json!([
{"name":"z_added"}, {"name":"a_added"}
])
.as_array()
.unwrap()
.iter()
.cloned(),
);
next["components"]["schemas"]["Value"]["type"] = json!("integer");
next["components"]["schemas"]["Unused"]["description"] = json!("x".repeat(220_000));
let world = TestWorld::new();
world.plugin("notes", old, next.clone());
let host = world.start().await;
let result = activate(&host, "notes").await.unwrap();
assert_eq!(result["notes"]["method_count"], 5);
assert_eq!(result["notes"]["added"], json!(["a_added", "z_added"]));
assert_eq!(
result["notes"]["removed"],
json!(["a_removed", "z_removed"])
);
assert_eq!(result["notes"]["changed"], json!(["direct", "referenced"]));
assert!(result.to_string().len() < 1024);
assert_eq!(call(&host, "notes.rpc.discover", json!({})).await, next);
assert_eq!(
call(
&host,
"notes.read",
json!({"path":"/proc/core/plugins/notes/discover.json"})
)
.await,
next
);
host.stop().await;
}
/// PROTOCOL 10.3: first installation has no previous build; a no-op has no method changes.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn summarizes_first_install_and_noop() {
let world = TestWorld::new();
world.plugin("notes", document("add"), document("add"));
std::fs::remove_file(world.0.join("plugins/notes/plugin.wasm")).unwrap();
let host = world.start().await;
let result = activate(&host, "notes").await.unwrap();
assert_eq!(result["notes"]["previous"], Value::Null);
assert_eq!(result["notes"]["added"], json!(["add"]));
assert_eq!(result["notes"]["hash"], fixture_hash(2));
let result = activate(&host, "notes").await.unwrap();
assert_eq!(result["notes"]["previous"], fixture_hash(2));
for field in ["added", "removed", "changed"] {
assert_eq!(result["notes"][field], json!([]));
}
assert!(!world.0.join("plugins/notes/plugin.prev.wasm").exists());
host.stop().await;
}
struct TestWorld(PathBuf);
impl TestWorld {
/// Creates an isolated world with two version documents per plugin.
@@ -481,7 +561,19 @@ async fn activates_copy() {
world.plugin("notes", document("add"), document("add"));
let host = world.start().await;
let result = activate(&host, "notes").await.unwrap();
assert!(result["notes"]["methods"].is_array());
assert_eq!(
result,
json!({"notes": {
"plugin":"notes", "wasm":"/plugins/notes/plugin.wasm",
"hash":fixture_hash(2), "previous":fixture_hash(1),
"method_count":1, "added":[], "removed":[], "changed":[],
"interface_check":"passed"
}})
);
assert_eq!(
call(&host, "notes.rpc.discover", json!({})).await,
document("add")
);
assert_eq!(
call(&host, "notes.version", json!({})).await["version"],
"2"
@@ -577,7 +669,12 @@ async fn activates_group() {
consumer("notes", document("other")),
);
let host = world.start().await;
call(&host,"core.activate",json!({"plugins":[{"plugin":"notes","wasm":"/state/notes/next.wasm"},{"plugin":"agent","wasm":"/state/agent/next.wasm"}]})).await;
let result = call(&host,"core.activate",json!({"plugins":[{"plugin":"notes","wasm":"/state/notes/next.wasm"},{"plugin":"agent","wasm":"/state/agent/next.wasm"}]})).await;
assert_eq!(result.as_object().unwrap().len(), 2);
assert_eq!(result["notes"]["added"], json!(["other"]));
assert_eq!(result["notes"]["removed"], json!(["add"]));
assert_eq!(result["agent"]["method_count"], 1);
assert_eq!(result["agent"]["interface_check"], "passed");
assert_eq!(
call(&host, "agent.version", json!({})).await["version"],
"2"
@@ -594,12 +691,13 @@ async fn force_skips_checks() {
consumer("absent", document("add")),
);
let host = world.start().await;
call(
let result = call(
&host,
"core.activate",
json!({"plugin":"notes","wasm":"/state/notes/next.wasm","force":true}),
)
.await;
assert_eq!(result["notes"]["interface_check"], "skipped");
host.stop().await;
}
/// PROTOCOL 10.3: revert activates the previous copy and preserves the replaced version.
@@ -609,7 +707,11 @@ async fn reverts_previous_version() {
world.plugin("notes", document("add"), document("add"));
let host = world.start().await;
activate(&host, "notes").await.unwrap();
call(&host, "core.revert", json!({"plugin":"notes"})).await;
let result = call(&host, "core.revert", json!({"plugin":"notes"})).await;
assert_eq!(result["notes"]["hash"], fixture_hash(1));
assert_eq!(result["notes"]["previous"], fixture_hash(2));
assert_eq!(result["notes"]["interface_check"], "passed");
assert!(result["notes"].get("methods").is_none());
assert_eq!(
call(&host, "notes.version", json!({})).await["version"],
"1"