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.5\"",
14 "kcode-k1-codex-adapter = \"0.5.3\"",
15 "kcode-k1-codex-websearch = \"0.2.0\"",
16 "kcode-k1-daemon-http-boundary = \"0.1.1\"",
17 "kcode-k1-daemon-provider-config = \"0.1.6\"",
18 "kcode-k1-daemon-web-startup = \"0.1.0\"",
19 "kcode-k1-full-audio = \"0.4.0\"",
20 "kcode-k1-http-access-context = \"0.2.0\"",
21 "kcode-k1-http-access-profile-presentation = \"0.1.0\"",
22 "kcode-k1-http-audio = \"0.4.0\"",
23 "kcode-k1-http-chat = \"0.7.2\"",
24 "kcode-k1-http-kmap = \"0.1.0\"",
25 "kcode-k1-http-launch-nodes = \"0.3.0\"",
26 "kcode-k1-http-people = \"0.9.0\"",
27 "kcode-k1-http-persons = \"0.3.0\"",
28 "kcode-k1-ktool-set-launch-node = \"0.1.1\"",
29 "kcode-k1-ktool-social = \"0.1.1\"",
30 "kcode-k1-launch-nodes = \"0.4.0\"",
31 "kcode-k1-peering = \"0.3.1\"",
32 "kcode-k1-txn-ordering = \"0.5.1\"",
33 "kcode-speaker-v3-analysis = \"0.3.5\"",
34];
35
36const OBSOLETE_LINES: &[&str] = &[
37 "kcode-k1-access = \"0.6.1\"",
38 "kcode-k1-access-full-audio = \"0.9.1\"",
39 "kcode-k1-access-full-audio = \"0.9.4\"",
40 "kcode-k1-access-launch-nodes = \"0.2.0\"",
41 "kcode-k1-audio-classification = \"0.5.9\"",
42 "kcode-k1-audio-classification = \"0.5.10\"",
43 "kcode-k1-chat-service = \"0.3.0\"",
44 "kcode-k1-chat-service = \"0.6.0\"",
45 "kcode-k1-chat-service = \"0.7.0\"",
46 "kcode-k1-chat-service = \"0.8.0\"",
47 "kcode-k1-chat-service = \"0.9.0\"",
48 "kcode-k1-chat-service = \"0.10.0\"",
49 "kcode-k1-chat-service = \"0.10.1\"",
50 "kcode-k1-chat-service = \"0.10.2\"",
51 "kcode-k1-chat-service = \"0.10.3\"",
52 "kcode-k1-chat-service = \"0.10.4\"",
53 "kcode-k1-codex-adapter = \"0.5.0\"",
54 "kcode-k1-codex-adapter = \"0.5.1\"",
55 "kcode-k1-codex-websearch = \"0.1.0\"",
56 "kcode-k1-daemon-http-boundary = \"0.1.0\"",
57 "kcode-k1-daemon-provider-config = \"0.1.4\"",
58 "kcode-k1-daemon-provider-config = \"0.1.5\"",
59 "kcode-k1-full-audio = \"0.3.7\"",
60 "kcode-k1-http-audio = \"0.3.0\"",
61 "kcode-k1-http-audio = \"0.3.1\"",
62 "kcode-k1-http-chat = \"0.2.0\"",
63 "kcode-k1-http-chat = \"0.5.0\"",
64 "kcode-k1-http-chat = \"0.6.0\"",
65 "kcode-k1-http-chat = \"0.6.1\"",
66 "kcode-k1-http-chat = \"0.7.0\"",
67 "kcode-k1-http-chat = \"0.7.1\"",
68 "kcode-k1-http-launch-nodes = \"0.2.0\"",
69 "kcode-k1-ktool-set-launch-node = \"0.1.0\"",
70 "kcode-k1-ktool-social = \"0.1.0\"",
71 "kcode-k1-launch-nodes = \"0.3.0\"",
72 "kcode-k1-peering = \"0.3.0\"",
73 "kcode-k1-web-http = \"0.1.0\"",
74];
75
76const REQUIRED_SOURCE: &[&str] = &[
77 "AudioClassification::open(",
78 ".shutdown()",
79 "kcode_k1_http_audio::authenticated_routes",
80 "SocialKtools::new(",
81 "SetLaunchNodeKtool::new(",
82 "K1ChatService::open_with_social_and_set_launch_node(",
83];
84
85const PROHIBITED_SOURCE: &[&str] = &[
86 "audio-classification-v2",
87 "http_audio_artifacts",
88 "retry_fragment",
89 "artifact_route",
90 "let chat = K1ChatService::open(",
91 "K1ChatService::open_with_social(",
92];
93
94pub fn verify_manifest(manifest: &str) {
95 for required in REQUIRED_LINES {
96 assert!(
97 manifest.lines().any(|line| line == *required),
98 "missing required manifest line: {required}"
99 );
100 }
101 for obsolete in OBSOLETE_LINES {
102 assert!(
103 !manifest.lines().any(|line| line == *obsolete),
104 "obsolete manifest line: {obsolete}"
105 );
106 }
107 assert!(
108 !manifest.lines().any(|line| line
109 .trim_start()
110 .starts_with("kcode-k1-http-audio-artifacts")),
111 "artifact manifest selection"
112 );
113 assert!(
114 !manifest.lines().any(|line| line.contains(" = \"=")),
115 "manifest contains an exact internal pin"
116 );
117}
118
119pub fn verify_source(source: &str) {
120 for required in REQUIRED_SOURCE {
121 assert!(
122 source.contains(required),
123 "missing required source evidence: {required}"
124 );
125 }
126 for prohibited in PROHIBITED_SOURCE {
127 assert!(
128 !source.contains(prohibited),
129 "prohibited source token: {prohibited}"
130 );
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 fn good_manifest() -> String {
139 REQUIRED_LINES.join("\n")
140 }
141
142 fn good_source() -> &'static str {
143 "AudioClassification::open(\nservice.shutdown()\nkcode_k1_http_audio::authenticated_routes\nSocialKtools::new(\nSetLaunchNodeKtool::new(\nK1ChatService::open_with_social_and_set_launch_node("
144 }
145
146 #[test]
147 fn accepts_current_inputs() {
148 verify_manifest(&good_manifest());
149 verify_source(good_source());
150 }
151
152 #[test]
153 fn rejects_missing_audio_requirements() {
154 for line in [
155 "kcode-k1-access-full-audio = \"0.10.0\"",
156 "kcode-k1-audio-classification = \"0.6.0\"",
157 "kcode-k1-full-audio = \"0.4.0\"",
158 "kcode-k1-http-audio = \"0.4.0\"",
159 "kcode-k1-peering = \"0.3.1\"",
160 "kcode-speaker-v3-analysis = \"0.3.5\"",
161 ] {
162 assert!(
163 std::panic::catch_unwind(|| verify_manifest(&good_manifest().replace(line, "")))
164 .is_err()
165 );
166 }
167 }
168
169 #[test]
170 fn rejects_missing_composition_manifest_requirements() {
171 for line in [
172 "kcode-k1-chat-service = \"0.10.5\"",
173 "kcode-k1-ktool-set-launch-node = \"0.1.1\"",
174 "kcode-k1-ktool-social = \"0.1.1\"",
175 ] {
176 assert!(
177 std::panic::catch_unwind(|| verify_manifest(&good_manifest().replace(line, "")))
178 .is_err()
179 );
180 }
181 }
182
183 #[test]
184 fn rejects_missing_source_evidence() {
185 for evidence in REQUIRED_SOURCE {
186 assert!(
187 std::panic::catch_unwind(|| verify_source(&good_source().replace(evidence, "")))
188 .is_err()
189 );
190 }
191 }
192
193 #[test]
194 fn rejects_prohibited_source_tokens() {
195 for token in PROHIBITED_SOURCE {
196 assert!(
197 std::panic::catch_unwind(|| verify_source(&format!("{} {token}", good_source())))
198 .is_err()
199 );
200 }
201 }
202
203 #[test]
204 fn rejects_artifact_package_selection() {
205 assert!(
206 std::panic::catch_unwind(|| verify_manifest(&format!(
207 "{}\n kcode-k1-http-audio-artifacts = \"0.4.0\"",
208 good_manifest()
209 )))
210 .is_err()
211 );
212 }
213
214 #[test]
215 fn rejects_old_audio_and_displaced_composition_lines() {
216 for line in [
217 "kcode-k1-audio-classification = \"0.5.10\"",
218 "kcode-k1-full-audio = \"0.3.7\"",
219 "kcode-k1-access-full-audio = \"0.9.4\"",
220 "kcode-k1-http-audio = \"0.3.1\"",
221 "kcode-k1-peering = \"0.3.0\"",
222 "kcode-k1-chat-service = \"0.10.4\"",
223 "kcode-k1-ktool-set-launch-node = \"0.1.0\"",
224 "kcode-k1-ktool-social = \"0.1.0\"",
225 ] {
226 assert!(
227 std::panic::catch_unwind(|| verify_manifest(&format!(
228 "{}\n{line}",
229 good_manifest()
230 )))
231 .is_err()
232 );
233 }
234 }
235
236 #[test]
237 fn current_chat_composition_does_not_match_former_name() {
238 assert!(
239 !"K1ChatService::open_with_social_and_set_launch_node("
240 .contains("K1ChatService::open_with_social(")
241 );
242 }
243
244 #[test]
245 fn rejects_exact_internal_pin() {
246 assert!(
247 std::panic::catch_unwind(|| verify_manifest(&format!(
248 "{}\nkcode-k1-peering = \"=0.3.1\"",
249 good_manifest()
250 )))
251 .is_err()
252 );
253 }
254
255 #[test]
256 fn rejects_unrelated_obsolete_line() {
257 assert!(
258 std::panic::catch_unwind(|| verify_manifest(&format!(
259 "{}\nkcode-k1-http-chat = \"0.7.1\"",
260 good_manifest()
261 )))
262 .is_err()
263 );
264 }
265}