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