thing-contract: blessed goldens (surface with all 13 arms, composition, invocation, one result per outcome and effect arm, asset catalog, surface.cc, refusal and tolerance payloads); the 64-deep chain runs on a 16 MiB thread
Blessed on Yggdrasil (Linux, debug). deep_tree's decode overflows a 2 MiB stack at depth 32. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c3991ec1e8
commit
1444e0c4f7
18 changed files with 42 additions and 14 deletions
|
|
@ -565,12 +565,12 @@ fn refusal_cases() {
|
|||
let payloads: std::collections::BTreeMap<_, _> = refusal_payloads().into_iter().collect();
|
||||
let committed = |name: &str| golden_bytes(&format!("{name}.bin"), &payloads[name]);
|
||||
|
||||
let cases: [(&str, &str, &str, &str); 3] = [
|
||||
("result-thing1-flat", "gamecult.thing.command_result", "thing:result:inv-1.1", "CommandResult"),
|
||||
("node-unknown-arm", "gamecult.thing.surface", "thing:surface:provider.a.main", "Node"),
|
||||
("outcome-unknown-arm", "gamecult.thing.command_result", "thing:result:inv-1.1", "Outcome"),
|
||||
let cases: [(&str, &str, &str); 3] = [
|
||||
("result-thing1-flat", "gamecult.thing.command_result", "thing:result:inv-1.1"),
|
||||
("node-unknown-arm", "gamecult.thing.surface", "thing:surface:provider.a.main"),
|
||||
("outcome-unknown-arm", "gamecult.thing.command_result", "thing:result:inv-1.1"),
|
||||
];
|
||||
for (name, kind, key, union) in cases {
|
||||
for (name, kind, key) in cases {
|
||||
let mut cache = CultCache::new();
|
||||
ThingContract.register_entries(&mut cache).unwrap();
|
||||
let bytes = committed(name);
|
||||
|
|
@ -582,25 +582,45 @@ fn refusal_cases() {
|
|||
let message = format!("{error:#}");
|
||||
assert!(message.contains(kind), "{name}: the error names the kind: {message}");
|
||||
assert!(message.contains(key), "{name}: the error names the record key: {message}");
|
||||
assert!(message.contains(union) || name == "result-thing1-flat", "{name}: {message}");
|
||||
assert!(!message.contains(CANARY), "{name}: the error echoed a value: {message}");
|
||||
}
|
||||
|
||||
// The unknown arm key is named; the payload never is.
|
||||
// Decoded directly, the refusal names the union and the arm key, and never the payload.
|
||||
for (name, union, key) in [("node-unknown-arm", "Node", "13"), ("outcome-unknown-arm", "Outcome", "4")] {
|
||||
let bytes = committed(name);
|
||||
let message = if union == "Node" {
|
||||
rmp_serde::from_slice::<SurfaceDocument>(&bytes).unwrap_err().to_string()
|
||||
} else {
|
||||
rmp_serde::from_slice::<CommandResult>(&bytes).unwrap_err().to_string()
|
||||
};
|
||||
assert!(message.contains(union) && message.contains(key), "{name}: {message}");
|
||||
assert!(!message.contains(CANARY), "{name}: {message}");
|
||||
}
|
||||
|
||||
// An extra trailing slot is read (CultLib's extra-trailing-slots ruling); a missing slot is not.
|
||||
let mut answered = invocation();
|
||||
answered.invocation_id = "inv-1".into();
|
||||
let extra: CommandResult = rmp_serde::from_slice(&committed("result-extra-slot")).unwrap();
|
||||
assert_eq!(extra, CommandResult::pending(&answered, 1, "working"));
|
||||
let missing = rmp_serde::from_slice::<CommandResult>(&committed("result-missing-slot")).unwrap_err();
|
||||
println!("missing slot: {missing}");
|
||||
let mut cache = CultCache::new();
|
||||
ThingContract.register_entries(&mut cache).unwrap();
|
||||
let node = cache
|
||||
.put_envelope::<SurfaceDocument>(envelope::<SurfaceDocument>("k", committed("node-unknown-arm")))
|
||||
let missing = cache
|
||||
.put_envelope::<CommandResult>(envelope::<CommandResult>("thing:result:inv-1.1", committed("result-missing-slot")))
|
||||
.unwrap_err();
|
||||
assert!(format!("{node:#}").contains("13"), "{node:#}");
|
||||
let outcome = cache
|
||||
.put_envelope::<CommandResult>(envelope::<CommandResult>("k", committed("outcome-unknown-arm")))
|
||||
.unwrap_err();
|
||||
assert!(format!("{outcome:#}").contains('4'), "{outcome:#}");
|
||||
let message = format!("{missing:#}");
|
||||
assert!(message.contains("gamecult.thing.command_result") && message.contains("thing:result:inv-1.1"), "{message}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deep_tree() {
|
||||
// Decoding a Node costs tens of KiB of debug stack per nesting level, so the chain runs on a
|
||||
// thread that has the room; the 2 MiB default does not.
|
||||
std::thread::Builder::new().stack_size(16 * 1024 * 1024).spawn(deep_tree_body).unwrap().join().unwrap();
|
||||
}
|
||||
|
||||
fn deep_tree_body() {
|
||||
// A 64-deep Box chain.
|
||||
let mut chain = text("leaf", "bottom");
|
||||
for depth in (0..64).rev() {
|
||||
|
|
|
|||
BIN
crates/thing-contract/tests/goldens/asset-catalog.bin
Normal file
BIN
crates/thing-contract/tests/goldens/asset-catalog.bin
Normal file
Binary file not shown.
BIN
crates/thing-contract/tests/goldens/composition-all-params.bin
Normal file
BIN
crates/thing-contract/tests/goldens/composition-all-params.bin
Normal file
Binary file not shown.
BIN
crates/thing-contract/tests/goldens/invocation-all-inputs.bin
Normal file
BIN
crates/thing-contract/tests/goldens/invocation-all-inputs.bin
Normal file
Binary file not shown.
1
crates/thing-contract/tests/goldens/node-unknown-arm.bin
Normal file
1
crates/thing-contract/tests/goldens/node-unknown-arm.bin
Normal file
|
|
@ -0,0 +1 @@
|
|||
–ªprovider.a¤main¯CANARY-7f3a91c2’
‘¯CANARY-7f3a91c2<63>
|
||||
|
|
@ -0,0 +1 @@
|
|||
“¥inv-1’‘¯CANARY-7f3a91c2
|
||||
Binary file not shown.
Binary file not shown.
1
crates/thing-contract/tests/goldens/result-completed.bin
Normal file
1
crates/thing-contract/tests/goldens/result-completed.bin
Normal file
|
|
@ -0,0 +1 @@
|
|||
“Ù$3f2b8c1e-5d44-4a6b-9a07-1c2d3e4f5a6b’“ÃÀ
|
||||
|
|
@ -0,0 +1 @@
|
|||
“Ù$3f2b8c1e-5d44-4a6b-9a07-1c2d3e4f5a6b’’«not allowedÀ
|
||||
1
crates/thing-contract/tests/goldens/result-denied.bin
Normal file
1
crates/thing-contract/tests/goldens/result-denied.bin
Normal file
|
|
@ -0,0 +1 @@
|
|||
“Ù$3f2b8c1e-5d44-4a6b-9a07-1c2d3e4f5a6b’’©not yours¢go
|
||||
BIN
crates/thing-contract/tests/goldens/result-extra-slot.bin
Normal file
BIN
crates/thing-contract/tests/goldens/result-extra-slot.bin
Normal file
Binary file not shown.
1
crates/thing-contract/tests/goldens/result-failed.bin
Normal file
1
crates/thing-contract/tests/goldens/result-failed.bin
Normal file
|
|
@ -0,0 +1 @@
|
|||
“Ù$3f2b8c1e-5d44-4a6b-9a07-1c2d3e4f5a6b’‘¥broke
|
||||
|
|
@ -0,0 +1 @@
|
|||
’¥inv-1
|
||||
BIN
crates/thing-contract/tests/goldens/result-pending.bin
Normal file
BIN
crates/thing-contract/tests/goldens/result-pending.bin
Normal file
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
<EFBFBD>§receipt†©commandId¥inv-1§command¢go¥state©completed§message¯CANARY-7f3a91c2sourceVersion«issuedAtUtc´2026-01-01T00:00:00Z
|
||||
BIN
crates/thing-contract/tests/goldens/surface-all-arms.bin
Normal file
BIN
crates/thing-contract/tests/goldens/surface-all-arms.bin
Normal file
Binary file not shown.
BIN
crates/thing-contract/tests/goldens/surface.cc
Normal file
BIN
crates/thing-contract/tests/goldens/surface.cc
Normal file
Binary file not shown.
Loading…
Reference in a new issue