Skip to content

Commit 89b0fab

Browse files
committed
test: cover req id search json metadata
1 parent 56724ae commit 89b0fab

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

tests/json_format_tests.rs

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,76 @@ function validateTerm(term) {
9595
create_test_file(root_dir, "src/multi_term.js", multi_term_content);
9696
}
9797

98+
fn create_requirement_fixture(root_dir: &TempDir) {
99+
let service_ts = r#"export class PolicyService {
100+
// Implements: SYS-REQ-424
101+
async evaluatePolicy(input: string): Promise<boolean> {
102+
return input.length > 0 && input !== "deny";
103+
}
104+
}
105+
106+
// Implements: SYS-REQ-425
107+
export const normalizeDecision = (raw: string) => {
108+
return raw.trim().toLowerCase();
109+
};
110+
"#;
111+
create_test_file(root_dir, "web/src/service.ts", service_ts);
112+
113+
let service_test_ts = r#"import { describe, it, expect, test } from "vitest";
114+
115+
// Verifies: SYS-REQ-424 [boundary]
116+
test("accepts valid policy", () => {
117+
expect(true && true).toBe(true);
118+
});
119+
120+
// MCDC SYS-REQ-424: input_valid=T, not_denied=T => TRUE
121+
it("records witness row", () => {
122+
expect(true).toBe(true);
123+
});
124+
125+
describe("normalization", () => {
126+
// Verifies: SYS-REQ-425
127+
it("normalizes decisions", () => {
128+
expect(" ALLOW ".trim().toLowerCase()).toBe("allow");
129+
});
130+
});
131+
"#;
132+
create_test_file(
133+
root_dir,
134+
"web/src/__tests__/service.test.ts",
135+
service_test_ts,
136+
);
137+
138+
let demo_go = r#"package demo
139+
140+
// Implements: SYS-REQ-426
141+
func RunDemo(flag bool) bool {
142+
return flag
143+
}
144+
"#;
145+
create_test_file(root_dir, "pkg/demo/demo.go", demo_go);
146+
147+
let noise_ts = r#"export const literalOnly = "Implements: SYS-REQ-427";
148+
149+
export function unrelated() {
150+
return "SYS-REQ-428";
151+
}
152+
153+
// SYS-REQ-429 appears here without an annotation verb.
154+
export function looseComment() {
155+
return true;
156+
}
157+
"#;
158+
create_test_file(root_dir, "web/src/noise.ts", noise_ts);
159+
}
160+
161+
fn find_result<'a>(results: &'a [Value], predicate: impl Fn(&'a Value) -> bool) -> &'a Value {
162+
results
163+
.iter()
164+
.find(|result| predicate(result))
165+
.expect("expected search result was not found")
166+
}
167+
98168
#[test]
99169
fn test_json_output_format_basic() {
100170
let temp_dir = TempDir::new().expect("Failed to create temp dir");
@@ -190,6 +260,138 @@ fn test_json_output_format_basic() {
190260
);
191261
}
192262

263+
#[test]
264+
fn test_search_json_no_merge_reports_req_id_source_metadata() {
265+
let temp_dir = TempDir::new().expect("Failed to create temp dir");
266+
create_requirement_fixture(&temp_dir);
267+
268+
let output = Command::new("cargo")
269+
.args([
270+
"run",
271+
"--quiet",
272+
"--",
273+
"search",
274+
"--allow-tests",
275+
"--strict-elastic-syntax",
276+
"--max-results",
277+
"20",
278+
"--no-merge",
279+
"--format",
280+
"json",
281+
r#""SYS-REQ-424" OR "SYS-REQ-425""#,
282+
temp_dir.path().to_str().unwrap(),
283+
])
284+
.output()
285+
.expect("Failed to execute command");
286+
287+
assert!(
288+
output.status.success(),
289+
"search command failed: {}",
290+
String::from_utf8_lossy(&output.stderr)
291+
);
292+
293+
let stdout = String::from_utf8_lossy(&output.stdout);
294+
let json_str = extract_json_from_output(&stdout);
295+
let json_result: Value = serde_json::from_str(json_str).expect("Failed to parse JSON output");
296+
let results = json_result
297+
.get("results")
298+
.and_then(Value::as_array)
299+
.expect("results should be an array");
300+
301+
assert_eq!(
302+
results.len(),
303+
5,
304+
"expected one result per semantic-ish block"
305+
);
306+
307+
let method_result = find_result(results, |result| {
308+
result
309+
.get("code")
310+
.and_then(Value::as_str)
311+
.is_some_and(|code| code.contains("evaluatePolicy") && code.contains("SYS-REQ-424"))
312+
});
313+
assert_eq!(
314+
method_result.get("language").and_then(Value::as_str),
315+
Some("typescript")
316+
);
317+
assert_eq!(
318+
method_result.get("node_type").and_then(Value::as_str),
319+
Some("method_definition")
320+
);
321+
assert_eq!(
322+
method_result.get("owner_symbol").and_then(Value::as_str),
323+
Some("evaluatePolicy")
324+
);
325+
assert_eq!(
326+
method_result.get("scope").and_then(Value::as_str),
327+
Some("function")
328+
);
329+
330+
let arrow_result = find_result(results, |result| {
331+
result
332+
.get("code")
333+
.and_then(Value::as_str)
334+
.is_some_and(|code| code.contains("normalizeDecision") && code.contains("SYS-REQ-425"))
335+
});
336+
assert_eq!(
337+
arrow_result.get("node_type").and_then(Value::as_str),
338+
Some("export_statement")
339+
);
340+
assert_eq!(
341+
arrow_result.get("owner_symbol").and_then(Value::as_str),
342+
Some("normalizeDecision")
343+
);
344+
assert_eq!(
345+
arrow_result.get("scope").and_then(Value::as_str),
346+
Some("function")
347+
);
348+
349+
let callback_result = find_result(results, |result| {
350+
result
351+
.get("code")
352+
.and_then(Value::as_str)
353+
.is_some_and(|code| code.contains("test(\"accepts valid policy\""))
354+
});
355+
assert_eq!(
356+
callback_result.get("node_type").and_then(Value::as_str),
357+
Some("arrow_function")
358+
);
359+
assert_eq!(
360+
callback_result.get("scope").and_then(Value::as_str),
361+
Some("test")
362+
);
363+
assert_eq!(
364+
callback_result.get("is_test").and_then(Value::as_bool),
365+
Some(true)
366+
);
367+
368+
let leading_comments = callback_result
369+
.get("leading_comments")
370+
.and_then(Value::as_array)
371+
.expect("callback result should expose leading comments");
372+
assert_eq!(
373+
leading_comments[0].get("text").and_then(Value::as_str),
374+
Some("// Verifies: SYS-REQ-424 [boundary]")
375+
);
376+
377+
let matches = callback_result
378+
.get("matches")
379+
.and_then(Value::as_array)
380+
.expect("callback result should expose classified matches");
381+
assert_eq!(
382+
matches[0].get("text").and_then(Value::as_str),
383+
Some("SYS-REQ-424")
384+
);
385+
assert_eq!(
386+
matches[0].get("kind").and_then(Value::as_str),
387+
Some("comment")
388+
);
389+
assert_eq!(
390+
matches[0].get("comment_role").and_then(Value::as_str),
391+
Some("leading")
392+
);
393+
}
394+
193395
#[test]
194396
fn test_json_output_with_special_characters() {
195397
let temp_dir = TempDir::new().expect("Failed to create temp dir");

0 commit comments

Comments
 (0)