kcode_k1_daemon_lib_testkit/
lib.rs1#![forbid(unsafe_code)]
2
3const REQUIRED_LINES: &[&str] = &[
4 "kcode-k1-access = \"0.6.2\"",
5 "kcode-k1-access-audio-classifier = \"0.1.0\"",
6 "kcode-k1-access-full-audio = \"0.10.0\"",
7 "kcode-k1-access-launch-nodes = \"0.2.2\"",
8 "kcode-k1-access-persons = \"0.3.0\"",
9 "kcode-k1-access-profiles = \"0.6.2\"",
10 "kcode-k1-audio-classification = \"0.6.0\"",
11 "kcode-k1-audio-classifier = \"0.1.1\"",
12 "kcode-k1-authority-filters = \"0.2.0\"",
13 "kcode-k1-chat-service = \"0.10.9\"",
14 "kcode-k1-codex-adapter = \"0.5.3\"",
15 "kcode-k1-codex-websearch = \"0.2.0\"",
16 "kcode-k1-daemon-code-services = \"0.1.5\"",
17 "kcode-k1-daemon-http-boundary = \"0.1.1\"",
18 "kcode-k1-daemon-provider-config = \"0.1.6\"",
19 "kcode-k1-daemon-startup-error = \"0.1.1\"",
20 "kcode-k1-daemon-web-startup = \"0.1.1\"",
21 "kcode-k1-full-audio = \"0.4.0\"",
22 "kcode-k1-http-access-context = \"0.2.0\"",
23 "kcode-k1-http-access-profile-presentation = \"0.1.0\"",
24 "kcode-k1-http-audio = \"0.4.0\"",
25 "kcode-k1-http-chat = \"0.7.2\"",
26 "kcode-k1-http-kmap = \"0.1.0\"",
27 "kcode-k1-http-launch-nodes = \"0.3.0\"",
28 "kcode-k1-http-people = \"0.9.0\"",
29 "kcode-k1-http-persons = \"0.3.0\"",
30 "kcode-k1-ktool-set-launch-node = \"0.1.1\"",
31 "kcode-k1-ktool-social = \"0.1.1\"",
32 "kcode-k1-launch-nodes = \"0.4.0\"",
33 "kcode-k1-objects = \"0.2.2\"",
34 "kcode-k1-peering = \"0.3.1\"",
35 "kcode-k1-txn-ordering = \"0.5.1\"",
36 "kcode-speaker-v3-analysis = \"0.3.5\"",
37];
38
39const OBSOLETE_LINES: &[&str] = &[
40 "kcode-k1-access = \"0.6.1\"",
41 "kcode-k1-access-full-audio = \"0.9.1\"",
42 "kcode-k1-access-full-audio = \"0.9.4\"",
43 "kcode-k1-access-launch-nodes = \"0.2.0\"",
44 "kcode-k1-audio-classification = \"0.5.9\"",
45 "kcode-k1-audio-classification = \"0.5.10\"",
46 "kcode-k1-chat-service = \"0.3.0\"",
47 "kcode-k1-chat-service = \"0.6.0\"",
48 "kcode-k1-chat-service = \"0.7.0\"",
49 "kcode-k1-chat-service = \"0.8.0\"",
50 "kcode-k1-chat-service = \"0.9.0\"",
51 "kcode-k1-chat-service = \"0.10.0\"",
52 "kcode-k1-chat-service = \"0.10.1\"",
53 "kcode-k1-chat-service = \"0.10.2\"",
54 "kcode-k1-chat-service = \"0.10.3\"",
55 "kcode-k1-chat-service = \"0.10.4\"",
56 "kcode-k1-chat-service = \"0.10.5\"",
57 "kcode-k1-chat-service = \"0.10.6\"",
58 "kcode-k1-chat-service = \"0.10.7\"",
59 "kcode-k1-chat-service = \"0.10.8\"",
60 "kcode-k1-codex-adapter = \"0.5.0\"",
61 "kcode-k1-codex-adapter = \"0.5.1\"",
62 "kcode-k1-codex-websearch = \"0.1.0\"",
63 "kcode-k1-daemon-code-services = \"0.1.0\"",
64 "kcode-k1-daemon-code-services = \"0.1.1\"",
65 "kcode-k1-daemon-code-services = \"0.1.2\"",
66 "kcode-k1-daemon-code-services = \"0.1.3\"",
67 "kcode-k1-daemon-code-services = \"0.1.4\"",
68 "kcode-k1-daemon-http-boundary = \"0.1.0\"",
69 "kcode-k1-daemon-provider-config = \"0.1.4\"",
70 "kcode-k1-daemon-provider-config = \"0.1.5\"",
71 "kcode-k1-daemon-startup-error = \"0.1.0\"",
72 "kcode-k1-daemon-web-startup = \"0.1.0\"",
73 "kcode-k1-full-audio = \"0.3.7\"",
74 "kcode-k1-http-audio = \"0.3.0\"",
75 "kcode-k1-http-audio = \"0.3.1\"",
76 "kcode-k1-http-chat = \"0.2.0\"",
77 "kcode-k1-http-chat = \"0.5.0\"",
78 "kcode-k1-http-chat = \"0.6.0\"",
79 "kcode-k1-http-chat = \"0.6.1\"",
80 "kcode-k1-http-chat = \"0.7.0\"",
81 "kcode-k1-http-chat = \"0.7.1\"",
82 "kcode-k1-http-launch-nodes = \"0.2.0\"",
83 "kcode-k1-ktool-set-launch-node = \"0.1.0\"",
84 "kcode-k1-ktool-social = \"0.1.0\"",
85 "kcode-k1-launch-nodes = \"0.3.0\"",
86 "kcode-k1-objects = \"0.2.1\"",
87 "kcode-k1-peering = \"0.3.0\"",
88 "kcode-k1-web-code-ktool-service = \"0.1.1\"",
89 "kcode-k1-web-http = \"0.1.0\"",
90];
91
92const REQUIRED_SOURCE: &[&str] = &[
93 "AudioClassification::open(",
94 ".shutdown()",
95 "kcode_k1_http_audio::authenticated_routes",
96 "SocialKtools::new(",
97 "SetLaunchNodeKtool::new(",
98 "let web = open(",
99 "web.router()",
100 "web.projection()",
101 "CodeServices::open(",
102 ".into_parts()",
103 "K1ChatService::open_with_social_and_set_launch_node_and_rust_code_and_web_code(",
104 "with_cause(\"daemon files\")",
105 "with_cause(\"Code services\")",
106 "redacted_stage(\"Vault\")",
107 "redacted_stage(\"Gemini API key\")",
108 "redacted_stage(\"Gemini client\")",
109];
110
111const PROHIBITED_SOURCE: &[&str] = &[
112 "audio-classification-v2",
113 "http_audio_artifacts",
114 "retry_fragment",
115 "artifact_route",
116 "open_router(",
117 "RustCodeKtoolService::new(",
118 "K1WebCodeKtoolService::new(",
119 "let chat = K1ChatService::open(",
120 "K1ChatService::open_with_social(",
121 "K1ChatService::open_with_social_and_set_launch_node(",
122 ".map_err(stage(",
123 "StartupError::Stage(",
124];
125
126pub fn verify_manifest(manifest: &str) {
127 for required in REQUIRED_LINES {
128 assert!(
129 manifest.lines().any(|line| line == *required),
130 "missing required manifest line: {required}"
131 );
132 }
133 for obsolete in OBSOLETE_LINES {
134 assert!(
135 !manifest.lines().any(|line| line == *obsolete),
136 "obsolete manifest line: {obsolete}"
137 );
138 }
139 assert!(
140 !manifest.lines().any(|line| line
141 .trim_start()
142 .starts_with("kcode-k1-http-audio-artifacts")),
143 "artifact manifest selection"
144 );
145 assert!(
146 !manifest.lines().any(|line| line.contains(" = \"=")),
147 "manifest contains an exact internal pin"
148 );
149}
150
151pub fn verify_source(source: &str) {
152 for required in REQUIRED_SOURCE {
153 assert!(
154 source.contains(required),
155 "missing required source evidence: {required}"
156 );
157 }
158 for prohibited in PROHIBITED_SOURCE {
159 assert!(
160 !source.contains(prohibited),
161 "prohibited source token: {prohibited}"
162 );
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 fn good_manifest() -> String {
171 REQUIRED_LINES.join("\n")
172 }
173
174 fn good_source() -> &'static str {
175 "AudioClassification::open(\nservice.shutdown()\nkcode_k1_http_audio::authenticated_routes\nSocialKtools::new(\nSetLaunchNodeKtool::new(\nlet web = open(\nweb.router()\nweb.projection()\nCodeServices::open(\ncode_services.into_parts()\nK1ChatService::open_with_social_and_set_launch_node_and_rust_code_and_web_code(\nwith_cause(\"daemon files\")\nwith_cause(\"Code services\")\nredacted_stage(\"Vault\")\nredacted_stage(\"Gemini API key\")\nredacted_stage(\"Gemini client\")"
176 }
177
178 #[test]
179 fn accepts_current_inputs() {
180 verify_manifest(&good_manifest());
181 verify_source(good_source());
182 }
183
184 #[test]
185 fn rejects_missing_audio_requirements() {
186 for line in [
187 "kcode-k1-access-full-audio = \"0.10.0\"",
188 "kcode-k1-audio-classification = \"0.6.0\"",
189 "kcode-k1-full-audio = \"0.4.0\"",
190 "kcode-k1-http-audio = \"0.4.0\"",
191 "kcode-k1-peering = \"0.3.1\"",
192 "kcode-speaker-v3-analysis = \"0.3.5\"",
193 ] {
194 assert!(
195 std::panic::catch_unwind(|| verify_manifest(&good_manifest().replace(line, "")))
196 .is_err()
197 );
198 }
199 }
200
201 #[test]
202 fn rejects_missing_code_and_error_requirements() {
203 for line in [
204 "kcode-k1-chat-service = \"0.10.9\"",
205 "kcode-k1-daemon-code-services = \"0.1.5\"",
206 "kcode-k1-daemon-startup-error = \"0.1.1\"",
207 "kcode-k1-daemon-web-startup = \"0.1.1\"",
208 "kcode-k1-objects = \"0.2.2\"",
209 ] {
210 assert!(
211 std::panic::catch_unwind(|| verify_manifest(&good_manifest().replace(line, "")))
212 .is_err()
213 );
214 }
215 }
216
217 #[test]
218 fn rejects_missing_source_evidence() {
219 for evidence in REQUIRED_SOURCE {
220 assert!(
221 std::panic::catch_unwind(|| verify_source(&good_source().replace(evidence, "")))
222 .is_err()
223 );
224 }
225 }
226
227 #[test]
228 fn rejects_prohibited_source_tokens() {
229 for token in PROHIBITED_SOURCE {
230 assert!(
231 std::panic::catch_unwind(|| verify_source(&format!("{} {token}", good_source())))
232 .is_err()
233 );
234 }
235 }
236
237 #[test]
238 fn rejects_artifact_package_selection() {
239 assert!(
240 std::panic::catch_unwind(|| verify_manifest(&format!(
241 "{}\n kcode-k1-http-audio-artifacts = \"0.4.0\"",
242 good_manifest()
243 )))
244 .is_err()
245 );
246 }
247
248 #[test]
249 fn rejects_old_audio_and_displaced_composition_lines() {
250 for line in [
251 "kcode-k1-audio-classification = \"0.5.10\"",
252 "kcode-k1-full-audio = \"0.3.7\"",
253 "kcode-k1-access-full-audio = \"0.9.4\"",
254 "kcode-k1-http-audio = \"0.3.1\"",
255 "kcode-k1-peering = \"0.3.0\"",
256 "kcode-k1-chat-service = \"0.10.8\"",
257 "kcode-k1-daemon-code-services = \"0.1.4\"",
258 "kcode-k1-daemon-startup-error = \"0.1.0\"",
259 "kcode-k1-daemon-web-startup = \"0.1.0\"",
260 "kcode-k1-objects = \"0.2.1\"",
261 ] {
262 assert!(
263 std::panic::catch_unwind(|| verify_manifest(&format!(
264 "{}\n{line}",
265 good_manifest()
266 )))
267 .is_err()
268 );
269 }
270 }
271
272 #[test]
273 fn full_chat_composition_does_not_match_former_names() {
274 let current =
275 "K1ChatService::open_with_social_and_set_launch_node_and_rust_code_and_web_code(";
276 assert!(!current.contains("K1ChatService::open_with_social("));
277 assert!(!current.contains("K1ChatService::open_with_social_and_set_launch_node("));
278 }
279
280 #[test]
281 fn rejects_blanket_error_discarding() {
282 for token in [
283 ".map_err(stage(\"Objects\"))",
284 "StartupError::Stage(\"public origin\")",
285 ] {
286 assert!(
287 std::panic::catch_unwind(|| verify_source(&format!("{} {token}", good_source())))
288 .is_err()
289 );
290 }
291 }
292
293 #[test]
294 fn rejects_exact_internal_pin() {
295 assert!(
296 std::panic::catch_unwind(|| verify_manifest(&format!(
297 "{}\nkcode-k1-peering = \"=0.3.1\"",
298 good_manifest()
299 )))
300 .is_err()
301 );
302 }
303
304 #[test]
305 fn rejects_unrelated_obsolete_line() {
306 assert!(
307 std::panic::catch_unwind(|| verify_manifest(&format!(
308 "{}\nkcode-k1-http-chat = \"0.7.1\"",
309 good_manifest()
310 )))
311 .is_err()
312 );
313 }
314}