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