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