Skip to content

Commit f1b1eca

Browse files
committed
✅ test(cli): integration tests for search + system
1 parent 84568a2 commit f1b1eca

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
use assert_cmd::Command;
2+
use serde_json::json;
3+
use std::fs;
4+
use tempfile::TempDir;
5+
use wiremock::matchers::{method, path};
6+
use wiremock::{Mock, MockServer, ResponseTemplate};
7+
8+
fn write_config(cfg_dir: &std::path::Path, server_url: &str) {
9+
let cfg_path = cfg_dir.join("config.toml");
10+
fs::write(
11+
&cfg_path,
12+
format!(
13+
"[current]\nprofile = \"default\"\n\n\
14+
[profiles.default]\nserver = \"{server_url}\"\ntoken = \"plain-token\"\n"
15+
),
16+
)
17+
.unwrap();
18+
}
19+
20+
#[tokio::test]
21+
async fn search_renders_results() {
22+
let server = MockServer::start().await;
23+
Mock::given(method("GET"))
24+
.and(path("/api/search"))
25+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
26+
"mode_used": "lexical",
27+
"results": [
28+
{
29+
"type": "document",
30+
"id": "doc-1",
31+
"title": "Auth Endpoints",
32+
"snippet": "POST /auth/login authenticates a user",
33+
"score": 0.85
34+
},
35+
{
36+
"type": "task",
37+
"id": "task-1",
38+
"title": "Add login validation",
39+
"snippet": "implement password rules",
40+
"score": 0.42
41+
}
42+
]
43+
})))
44+
.mount(&server)
45+
.await;
46+
47+
let cfg_dir = TempDir::new().unwrap();
48+
write_config(cfg_dir.path(), &server.uri());
49+
50+
Command::cargo_bin("oversight")
51+
.unwrap()
52+
.env("OVERSIGHT_CONFIG_HOME", cfg_dir.path())
53+
.args(["--format", "table", "search", "auth"])
54+
.assert()
55+
.success()
56+
.stdout(predicates::str::contains("Auth Endpoints"))
57+
.stdout(predicates::str::contains("Add login validation"));
58+
}
59+
60+
#[tokio::test]
61+
async fn search_prints_degraded_reason_to_stderr() {
62+
let server = MockServer::start().await;
63+
Mock::given(method("GET"))
64+
.and(path("/api/search"))
65+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
66+
"mode_used": "lexical",
67+
"results": [],
68+
"degraded_reason": "hybrid mode degrades to lexical in Phase 1"
69+
})))
70+
.mount(&server)
71+
.await;
72+
73+
let cfg_dir = TempDir::new().unwrap();
74+
write_config(cfg_dir.path(), &server.uri());
75+
76+
Command::cargo_bin("oversight")
77+
.unwrap()
78+
.env("OVERSIGHT_CONFIG_HOME", cfg_dir.path())
79+
.args(["--format", "table", "search", "needle", "--mode", "hybrid"])
80+
.assert()
81+
.success()
82+
.stderr(predicates::str::contains("note: hybrid mode degrades"));
83+
}
84+
85+
#[tokio::test]
86+
async fn system_status_returns_health() {
87+
let server = MockServer::start().await;
88+
Mock::given(method("GET"))
89+
.and(path("/health"))
90+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
91+
"status": "healthy",
92+
"version": "0.2.0",
93+
"uptime_seconds": 1234.5,
94+
"components": {"runtime": "ok", "store": "ok"}
95+
})))
96+
.mount(&server)
97+
.await;
98+
99+
let cfg_dir = TempDir::new().unwrap();
100+
write_config(cfg_dir.path(), &server.uri());
101+
102+
Command::cargo_bin("oversight")
103+
.unwrap()
104+
.env("OVERSIGHT_CONFIG_HOME", cfg_dir.path())
105+
.args(["--format", "json", "system", "status"])
106+
.assert()
107+
.success()
108+
.stdout(predicates::str::contains("\"status\": \"healthy\""))
109+
.stdout(predicates::str::contains("\"version\": \"0.2.0\""));
110+
}
111+
112+
#[tokio::test]
113+
async fn system_stats_renders() {
114+
let server = MockServer::start().await;
115+
Mock::given(method("GET"))
116+
.and(path("/api/stats"))
117+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
118+
"total_documents": 42,
119+
"total_edges": 17,
120+
"top_labels": [{"label": "design", "count": 5}, {"label": "wip", "count": 3}]
121+
})))
122+
.mount(&server)
123+
.await;
124+
125+
let cfg_dir = TempDir::new().unwrap();
126+
write_config(cfg_dir.path(), &server.uri());
127+
128+
Command::cargo_bin("oversight")
129+
.unwrap()
130+
.env("OVERSIGHT_CONFIG_HOME", cfg_dir.path())
131+
.args(["--format", "json", "system", "stats"])
132+
.assert()
133+
.success()
134+
.stdout(predicates::str::contains("\"total_documents\": 42"));
135+
}
136+
137+
#[tokio::test]
138+
async fn system_activities_renders_table() {
139+
let server = MockServer::start().await;
140+
Mock::given(method("GET"))
141+
.and(path("/api/activities"))
142+
.respond_with(ResponseTemplate::new(200).set_body_json(json!([
143+
{
144+
"id": "act-1",
145+
"agent_id": "coder",
146+
"activity_type": "run",
147+
"thread_id": null,
148+
"task_id": "task-1234abcd",
149+
"source_id": null,
150+
"status": "completed",
151+
"cost_usd": 0.0123,
152+
"token_input": 1000,
153+
"token_output": 500,
154+
"summary": null,
155+
"model": "sonnet",
156+
"workspace_id": null,
157+
"created_at": "2026-04-18T12:00:00Z"
158+
}
159+
])))
160+
.mount(&server)
161+
.await;
162+
163+
let cfg_dir = TempDir::new().unwrap();
164+
write_config(cfg_dir.path(), &server.uri());
165+
166+
Command::cargo_bin("oversight")
167+
.unwrap()
168+
.env("OVERSIGHT_CONFIG_HOME", cfg_dir.path())
169+
.args([
170+
"--format",
171+
"table",
172+
"system",
173+
"activities",
174+
"--agent",
175+
"coder",
176+
])
177+
.assert()
178+
.success()
179+
.stdout(predicates::str::contains("coder"))
180+
.stdout(predicates::str::contains("run"))
181+
.stdout(predicates::str::contains("completed"));
182+
}

0 commit comments

Comments
 (0)