1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! The envelope's three outcomes, and the policy rungs that are the
//! envelope's own. Each type's fields are tested beside that type.
use super::{Outcome, Read, Reply, read};
use serde_json::json;
/// The corpus replay — every frame in `corpus/`, through the real reader.
mod corpus;
/// The same frames as an older engine and a newer one would have written them:
/// the grows-only contract, replayed.
mod editions;
/// The answer path, end to end: a frame with a kind this build paints becomes
/// the typed value the window draws.
#[test]
fn a_kind_this_build_paints_becomes_the_typed_answer() {
assert_eq!(
read(&json!({"ok": true, "kind": "nudged"})),
Read::Answer(Reply::Nudged)
);
}
/// **The kind-less envelope is the refusal, and only a refusal may wear it.**
/// `ok` cannot be the discriminant — a captured run spells its own verdict
/// there — so a body carrying no kind must be the refusal shape, and one that
/// claims success while saying nothing is an answer that failed to say what it
/// answers.
#[test]
fn the_kindless_envelope_is_the_refusal_and_nothing_else() {
assert_eq!(
read(&json!({"ok": false, "error": "unknown workspace \"hoem\""})),
Read::Refusal("unknown workspace \"hoem\"".to_owned())
);
let Read::Unreadable(why) = read(&json!({"ok": true, "rows": []})) else {
panic!("an answer with no kind is not readable");
};
assert!(why.contains("an answer with no"), "{why}");
}
/// A refusal that does not say why, and one that does not say whether: both
/// are shapes rung 1 refuses by name, because a seat cannot paint a verdict it
/// was not given.
#[test]
fn a_refusal_missing_its_own_fields_is_unreadable_by_name() {
for (frame, named) in [
(json!({"ok": false}), "\"error\""),
(json!({"error": "no"}), "\"ok\""),
] {
let Read::Unreadable(why) = read(&frame) else {
panic!("{frame} is not readable");
};
assert!(why.contains(named), "{frame}: {why}");
}
}
/// **The captured run reads its verdict off the exit code and off nothing
/// else.** The `ok` beside it is the envelope's field, and a second copy of
/// one fact could only ever disagree with it — this is the frame where the two
/// deliberately do.
#[test]
fn a_captured_run_takes_its_verdict_from_the_exit_code() {
let frame = json!({"ok": true, "kind": "outcome", "exit": 1,
"stdout": "", "stderr": "the gate said no\n"});
let Read::Answer(Reply::Outcome(outcome)) = read(&frame) else {
panic!("a captured run");
};
assert!(!outcome.ok(), "exit 1 is not ok, whatever `ok` said");
assert_eq!(outcome.stderr, "the gate said no\n");
assert!(
Outcome {
exit: 0,
stdout: String::new(),
stderr: String::new(),
}
.ok()
);
}
/// An exit status no `i32` holds is an engine saying something this seat has
/// no way to paint — the one narrowing on this surface that is a real check.
#[test]
fn an_exit_status_outside_its_own_type_is_unreadable() {
let frame = json!({"ok": true, "kind": "outcome", "exit": 9_999_999_999_i64,
"stdout": "", "stderr": ""});
let Read::Unreadable(why) = read(&frame) else {
panic!("out of range");
};
assert!(why.contains("out of range"), "{why}");
}
/// **Rung 2**: a kind this build does not paint refuses, names itself, and
/// says what to do about it — the refusal is the upgrade prompt, exactly as
/// the version preface's mismatch is.
#[test]
fn a_kind_this_build_does_not_paint_refuses_by_name_with_a_remedy() {
let Read::Unreadable(why) = read(&json!({"ok": true, "kind": "invocations", "rows": []}))
else {
panic!("an unpainted kind");
};
assert!(why.contains("\"invocations\""), "{why}");
assert!(why.contains("upgrade"), "{why}");
}
/// **Rung 1**, at the envelope: bytes that are not a reply object at all, and
/// a discriminant that is not a word.
#[test]
fn a_frame_that_is_not_a_reply_object_is_unreadable() {
for (frame, said) in [
(json!(["workspaces", []]), "not a JSON object"),
(json!("workspaces"), "not a JSON object"),
(json!({"ok": true, "kind": 7}), "non-string field"),
] {
let Read::Unreadable(why) = read(&frame) else {
panic!("{frame} is not readable");
};
assert!(why.contains(said), "{frame}: {why}");
}
}
/// **Nothing that arrives is a panic path.** The reader is total: every frame,
/// however hostile, lands on one of the three arms. This is the property the
/// whole module is written for, so it is asserted over a table rather than
/// inferred from the arms above.
#[test]
fn every_frame_lands_on_one_of_the_three_arms() {
for frame in [
json!(null),
json!(0),
json!({}),
json!({"ok": "yes"}),
json!({"kind": "workspaces"}),
json!({"ok": true, "kind": "workspaces"}),
json!({"ok": true, "kind": "workspaces", "rows": {}}),
json!({"ok": true, "kind": "workspaces", "rows": [7]}),
json!({"ok": true, "kind": "transcript", "rows": [{}]}),
json!({"ok": true, "kind": "follow"}),
json!({"ok": true, "kind": "conversations", "rows": [[]]}),
] {
let answered = read(&frame);
assert!(
matches!(
answered,
Read::Answer(_) | Read::Refusal(_) | Read::Unreadable(_)
),
"{frame}: {answered:?}"
);
}
}