Skip to content

Commit a1ce32a

Browse files
committed
feat(tests): refactoring
1 parent f334ede commit a1ce32a

76 files changed

Lines changed: 4201 additions & 549 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ serial_test = "3"
6767
futures = "0.3"
6868
mockito = "1"
6969

70+
[[test]]
71+
name = "unit_tests"
72+
path = "tests/unit/mod.rs"
73+
74+
[[test]]
75+
name = "integration_tests"
76+
path = "tests/integration/mod.rs"
77+
7078
[profile.release]
7179
opt-level = 3
7280
lto = true

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
[![License](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0)
33

44
A CLI-based security vulnerability scanner that combines static analysis, LLM-powered discovery, and ticket system cross-referencing (generated with Regolo.AI).
5-
[Example Report](example-report.html) generated with [Regolo.AI](https://regolo.ai/) models on [ins1gn1a/VulnServer-Linux](https://github.com/ins1gn1a/VulnServer-Linux).
5+
[Example Report](example-report.html) generated with [Regolo.AI](https://regolo.ai/) models on [ins1gn1a/VulnServer-Linux](https://github.com/ins1gn1a/VulnServer-Linux).
6+
Code coverage at [Codecov.io](https://app.codecov.io/gh/CodeAtCode/baco-scanner/tree/master).
67

78
## Features
89

codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ coverage:
1212
target: 80%
1313

1414
ignore:
15-
- "./tests/*.rs"
15+
- "tests/**/*.rs"
1616

1717
comment:
1818
layout: " diff, flags, files"

src/agent/executor.rs

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,303 @@ pub async fn execute_tool_calls(
172172

173173
(tools_used, test_source_path, compile_path, messages)
174174
}
175+
176+
#[cfg(test)]
177+
mod tests {
178+
use super::*;
179+
use crate::agent::sandbox::ToolSandbox;
180+
use crate::agent::tool_schema::ToolRegistry;
181+
use crate::agent::ToolCall;
182+
use std::sync::Arc;
183+
184+
#[test]
185+
fn test_create_empty_finding() {
186+
let finding = create_empty_finding("test.rs", 1, vec!["tool1".to_string()], Some("test-model".to_string()));
187+
188+
assert!(finding.finding.id.is_empty());
189+
assert!(finding.finding.title.is_empty());
190+
assert!(finding.finding.description.is_empty());
191+
assert_eq!(finding.finding.severity, Severity::Low);
192+
assert_eq!(finding.finding.confidence_score, 0.0);
193+
assert_eq!(finding.finding.file_path, "test.rs");
194+
assert!(finding.finding.line_number.is_none());
195+
assert_eq!(finding.agent_turns, 1);
196+
assert_eq!(finding.tools_used, vec!["tool1".to_string()]);
197+
assert_eq!(finding.finding.llm_model, Some("test-model".to_string()));
198+
assert!(finding.finding.agent_mode);
199+
}
200+
201+
#[test]
202+
fn test_create_empty_finding_no_model() {
203+
let finding = create_empty_finding("test.rs", 1, vec![], None);
204+
205+
assert!(finding.finding.llm_model.is_none());
206+
assert_eq!(finding.agent_turns, 1);
207+
}
208+
209+
#[test]
210+
fn test_create_audit_finding() {
211+
let reasoning = "No vulnerabilities found after thorough analysis";
212+
let finding = create_audit_finding("test.rs", 2, vec!["file_read".to_string()], reasoning.to_string(), Some("model".to_string()));
213+
214+
assert!(finding.finding.id.starts_with("agent-"));
215+
assert_eq!(finding.finding.title, "Security Audit - No Critical Vulnerabilities Detected");
216+
assert_eq!(finding.finding.description, reasoning);
217+
assert_eq!(finding.finding.severity, Severity::Medium);
218+
assert_eq!(finding.finding.confidence_score, 0.7);
219+
assert_eq!(finding.finding.file_path, "test.rs");
220+
assert_eq!(finding.agent_turns, 2);
221+
assert_eq!(finding.tools_used, vec!["file_read".to_string()]);
222+
}
223+
224+
#[test]
225+
fn test_create_audit_finding_no_model() {
226+
let finding = create_audit_finding("test.rs", 1, vec![], "reasoning".to_string(), None);
227+
228+
assert!(finding.finding.llm_model.is_none());
229+
}
230+
231+
#[tokio::test]
232+
async fn test_execute_tool_calls_single_tool() {
233+
let mut registry = ToolRegistry::new();
234+
registry.register(Box::new(crate::agent::tools::FileReadTool));
235+
236+
let tmpdir = tempfile::tempdir().unwrap();
237+
let path = tmpdir.path().join("test.txt");
238+
std::fs::write(&path, "hello").unwrap();
239+
240+
let sandbox = ToolSandbox::new(tmpdir.path().to_path_buf(), 30);
241+
let progress_cb: ProgressCallback = Arc::new(|_| {});
242+
243+
let response = ChatResponse {
244+
content: "".to_string(),
245+
tool_calls: vec![ToolCall {
246+
id: Some("call_1".to_string()),
247+
name: "file_read".to_string(),
248+
arguments: serde_json::json!({ "path": "test.txt" }),
249+
}],
250+
raw: serde_json::json!({}),
251+
model_used: "test".to_string(),
252+
};
253+
254+
let messages = vec![ChatMessage::user("test")];
255+
256+
let (tools_used, test_path, compile_path, messages) = execute_tool_calls(
257+
&registry,
258+
&sandbox,
259+
&response,
260+
messages,
261+
&progress_cb,
262+
tmpdir.path(),
263+
1,
264+
10,
265+
"Turn",
266+
).await;
267+
268+
assert_eq!(tools_used, vec!["file_read".to_string()]);
269+
assert!(test_path.is_none());
270+
assert!(compile_path.is_none());
271+
assert_eq!(messages.len(), 3); // original + assistant call + user result
272+
}
273+
274+
#[tokio::test]
275+
async fn test_execute_tool_calls_file_write_path_capture() {
276+
let mut registry = ToolRegistry::new();
277+
registry.register(Box::new(crate::agent::tools::FileWriteTool));
278+
279+
let tmpdir = tempfile::tempdir().unwrap();
280+
let sandbox = ToolSandbox::new(tmpdir.path().to_path_buf(), 30);
281+
let progress_cb: ProgressCallback = Arc::new(|_| {});
282+
283+
let response = ChatResponse {
284+
content: "".to_string(),
285+
tool_calls: vec![ToolCall {
286+
id: Some("call_1".to_string()),
287+
name: "file_write".to_string(),
288+
arguments: serde_json::json!({ "path": "test.rs", "content": "fn main() {}" }),
289+
}],
290+
raw: serde_json::json!({}),
291+
model_used: "test".to_string(),
292+
};
293+
294+
let messages = vec![ChatMessage::user("test")];
295+
296+
let (tools_used, test_path, compile_path, _) = execute_tool_calls(
297+
&registry,
298+
&sandbox,
299+
&response,
300+
messages,
301+
&progress_cb,
302+
tmpdir.path(),
303+
1,
304+
10,
305+
"Turn",
306+
).await;
307+
308+
assert_eq!(tools_used, vec!["file_write".to_string()]);
309+
assert!(test_path.is_some());
310+
assert!(compile_path.is_none());
311+
}
312+
313+
#[tokio::test]
314+
async fn test_execute_tool_calls_test_compile_path_capture() {
315+
let mut registry = ToolRegistry::new();
316+
registry.register(Box::new(crate::agent::tools::TestCompileTool));
317+
318+
let tmpdir = tempfile::tempdir().unwrap();
319+
let sandbox = ToolSandbox::new(tmpdir.path().to_path_buf(), 30);
320+
let progress_cb: ProgressCallback = Arc::new(|_| {});
321+
322+
let response = ChatResponse {
323+
content: "".to_string(),
324+
tool_calls: vec![ToolCall {
325+
id: Some("call_1".to_string()),
326+
name: "test_compile".to_string(),
327+
arguments: serde_json::json!({ "source_path": "test.rs", "language": "rust" }),
328+
}],
329+
raw: serde_json::json!({}),
330+
model_used: "test".to_string(),
331+
};
332+
333+
let messages = vec![ChatMessage::user("test")];
334+
335+
let (tools_used, test_path, compile_path, _) = execute_tool_calls(
336+
&registry,
337+
&sandbox,
338+
&response,
339+
messages,
340+
&progress_cb,
341+
tmpdir.path(),
342+
1,
343+
10,
344+
"Turn",
345+
).await;
346+
347+
assert_eq!(tools_used, vec!["test_compile".to_string()]);
348+
assert!(test_path.is_none());
349+
assert!(compile_path.is_some());
350+
}
351+
352+
#[tokio::test]
353+
async fn test_execute_tool_calls_unknown_tool() {
354+
let registry = ToolRegistry::new();
355+
let tmpdir = tempfile::tempdir().unwrap();
356+
let sandbox = ToolSandbox::new(tmpdir.path().to_path_buf(), 30);
357+
let progress_cb: ProgressCallback = Arc::new(|_| {});
358+
359+
let response = ChatResponse {
360+
content: "".to_string(),
361+
tool_calls: vec![ToolCall {
362+
id: Some("call_1".to_string()),
363+
name: "unknown_tool".to_string(),
364+
arguments: serde_json::json!({}),
365+
}],
366+
raw: serde_json::json!({}),
367+
model_used: "test".to_string(),
368+
};
369+
370+
let messages = vec![ChatMessage::user("test")];
371+
372+
let (tools_used, _, _, messages) = execute_tool_calls(
373+
&registry,
374+
&sandbox,
375+
&response,
376+
messages,
377+
&progress_cb,
378+
tmpdir.path(),
379+
1,
380+
10,
381+
"Turn",
382+
).await;
383+
384+
assert!(tools_used.is_empty());
385+
assert_eq!(messages.len(), 1); // No new messages since tool not found
386+
}
387+
388+
#[tokio::test]
389+
async fn test_execute_tool_calls_tool_error() {
390+
let mut registry = ToolRegistry::new();
391+
registry.register(Box::new(crate::agent::tools::FileReadTool));
392+
393+
let tmpdir = tempfile::tempdir().unwrap();
394+
let sandbox = ToolSandbox::new(tmpdir.path().to_path_buf(), 30);
395+
let progress_cb: ProgressCallback = Arc::new(|_| {});
396+
397+
let response = ChatResponse {
398+
content: "".to_string(),
399+
tool_calls: vec![ToolCall {
400+
id: Some("call_1".to_string()),
401+
name: "file_read".to_string(),
402+
arguments: serde_json::json!({ "path": "nonexistent.txt" }),
403+
}],
404+
raw: serde_json::json!({}),
405+
model_used: "test".to_string(),
406+
};
407+
408+
let messages = vec![ChatMessage::user("test")];
409+
410+
let (_, _, _, messages) = execute_tool_calls(
411+
&registry,
412+
&sandbox,
413+
&response,
414+
messages,
415+
&progress_cb,
416+
tmpdir.path(),
417+
1,
418+
10,
419+
"Turn",
420+
).await;
421+
422+
// Should have assistant call + user error message
423+
assert_eq!(messages.len(), 3);
424+
assert!(messages[2].content.contains("error"));
425+
}
426+
427+
#[tokio::test]
428+
async fn test_execute_tool_calls_multiple_tools() {
429+
let mut registry = ToolRegistry::new();
430+
registry.register(Box::new(crate::agent::tools::FileReadTool));
431+
registry.register(Box::new(crate::agent::tools::FileWriteTool));
432+
433+
let tmpdir = tempfile::tempdir().unwrap();
434+
let sandbox = ToolSandbox::new(tmpdir.path().to_path_buf(), 30);
435+
let progress_cb: ProgressCallback = Arc::new(|_| {});
436+
437+
let response = ChatResponse {
438+
content: "".to_string(),
439+
tool_calls: vec![
440+
ToolCall {
441+
id: Some("call_1".to_string()),
442+
name: "file_read".to_string(),
443+
arguments: serde_json::json!({ "path": "test.txt" }),
444+
},
445+
ToolCall {
446+
id: Some("call_2".to_string()),
447+
name: "file_write".to_string(),
448+
arguments: serde_json::json!({ "path": "out.txt", "content": "test" }),
449+
},
450+
],
451+
raw: serde_json::json!({}),
452+
model_used: "test".to_string(),
453+
};
454+
455+
let messages = vec![ChatMessage::user("test")];
456+
457+
let (tools_used, test_path, _, _) = execute_tool_calls(
458+
&registry,
459+
&sandbox,
460+
&response,
461+
messages,
462+
&progress_cb,
463+
tmpdir.path(),
464+
1,
465+
10,
466+
"Turn",
467+
).await;
468+
469+
assert_eq!(tools_used.len(), 2);
470+
assert!(tools_used.contains(&"file_read".to_string()));
471+
assert!(tools_used.contains(&"file_write".to_string()));
472+
assert!(test_path.is_some());
473+
}
474+
}

0 commit comments

Comments
 (0)