CI / Check (pull_request) Successful in 5m29s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Onboarded targets are now the sole persisted entity. The legacy `TrackedRepository` model, the `repositories` collection, the `/repositories` API, the Repositories dashboard page, the one-shot migration, and the `UNIFIED_PIPELINE` transition flag are all removed. Net −1.7k LOC. Agent - New internal `pipeline::repo_view::RepoView` (non-persisted) replaces the `TrackedRepository` model; it's projected from an `OnboardedTarget` + its code artifact by `RepoView::from_target` (the old `repo_view_from_target`), so the scan/PR-review pipeline is byte-for-byte the same behaviour it already ran on the unified path — only the type's origin changed. - `run_scan` always runs the unified `run_target`; the legacy `orchestrator::run` and the `unified_pipeline` flag are gone. `run_pr_review` resolves the target from `onboarded_targets`. - Webhooks (github/gitea/gitlab), the CVE monitor, graph build, chat embeddings, health stats, and the pentest repo lookup all read `onboarded_targets`. - `delete_target` now cascades the full downstream set (findings, sbom, scans, cve, tracker issues, graph, embeddings, DAST targets + pentest sessions and their children) — matching the old repository delete. - `get_ssh_public_key` moved to the health handler; `repositories()` accessor, `repos.rs`, and `migrate/` deleted. Core - `TrackedRepository` removed; `ScanTrigger` stays. `unified_pipeline` config field removed. Dashboard - Repositories page + route deleted; overview / graph / chat / pentest-wizard read onboarded targets; `infrastructure/repositories.rs` trimmed to just the SSH-key fetch. Tests - Legacy repositories-API and migration integration tests removed; tenant isolation, cascade-delete, and stats tests repointed to `/targets` / `onboarded_targets`. `git.rs` gains a `sanitize_repo_dir` unit test. Local: fmt clean; agent/mcp clippy clean; dashboard server+web compile; core + agent lib tests (32) pass; integration tests compile. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
113 lines
3.7 KiB
Rust
113 lines
3.7 KiB
Rust
use crate::common::TestServer;
|
|
use serde_json::json;
|
|
|
|
#[tokio::test]
|
|
async fn stats_overview_reflects_inserted_data() {
|
|
let server = TestServer::start().await;
|
|
|
|
// Add a target
|
|
server
|
|
.post(
|
|
"/api/v1/targets",
|
|
&json!({
|
|
"name": "stats-repo",
|
|
"target_type": "web_app",
|
|
"artifacts": [{ "kind": "git_repo", "source_ref": "https://github.com/example/stats-repo.git", "branch": "main" }],
|
|
}),
|
|
)
|
|
.await;
|
|
|
|
// Insert findings directly
|
|
let mongodb_uri = std::env::var("TEST_MONGODB_URI")
|
|
.unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into());
|
|
let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap();
|
|
let db = client.database(&server.db_name());
|
|
let now = mongodb::bson::DateTime::now();
|
|
|
|
for (title, severity) in [
|
|
("Critical Bug", "critical"),
|
|
("High Bug", "high"),
|
|
("Medium Bug", "medium"),
|
|
("Low Bug", "low"),
|
|
] {
|
|
db.collection::<mongodb::bson::Document>("findings")
|
|
.insert_one(mongodb::bson::doc! {
|
|
"repo_id": "test-repo-id",
|
|
"fingerprint": format!("fp-{title}"),
|
|
"scanner": "test",
|
|
"scan_type": "sast",
|
|
"title": title,
|
|
"description": "desc",
|
|
"severity": severity,
|
|
"status": "open",
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
let resp = server.get("/api/v1/stats/overview").await;
|
|
assert_eq!(resp.status(), 200);
|
|
|
|
let body: serde_json::Value = resp.json().await.unwrap();
|
|
let data = &body["data"];
|
|
assert_eq!(data["repositories"], 1);
|
|
assert_eq!(data["total_findings"], 4);
|
|
assert_eq!(data["critical"], 1);
|
|
assert_eq!(data["high"], 1);
|
|
|
|
server.cleanup().await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn stats_update_after_finding_status_change() {
|
|
let server = TestServer::start().await;
|
|
|
|
// Insert a finding
|
|
let mongodb_uri = std::env::var("TEST_MONGODB_URI")
|
|
.unwrap_or_else(|_| "mongodb://root:example@localhost:27017/?authSource=admin".into());
|
|
let client = mongodb::Client::with_uri_str(&mongodb_uri).await.unwrap();
|
|
let db = client.database(&server.db_name());
|
|
let now = mongodb::bson::DateTime::now();
|
|
|
|
let result = db
|
|
.collection::<mongodb::bson::Document>("findings")
|
|
.insert_one(mongodb::bson::doc! {
|
|
"repo_id": "repo-1",
|
|
"fingerprint": "fp-stats-test",
|
|
"scanner": "test",
|
|
"scan_type": "sast",
|
|
"title": "Stats Test Finding",
|
|
"description": "desc",
|
|
"severity": "high",
|
|
"status": "open",
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
})
|
|
.await
|
|
.unwrap();
|
|
let finding_id = result.inserted_id.as_object_id().unwrap().to_hex();
|
|
|
|
// Stats should show 1 finding
|
|
let resp = server.get("/api/v1/stats/overview").await;
|
|
let body: serde_json::Value = resp.json().await.unwrap();
|
|
assert_eq!(body["data"]["total_findings"], 1);
|
|
|
|
// Mark it as resolved
|
|
server
|
|
.patch(
|
|
&format!("/api/v1/findings/{finding_id}/status"),
|
|
&json!({ "status": "resolved" }),
|
|
)
|
|
.await;
|
|
|
|
// The finding still exists (status changed, not deleted)
|
|
let resp = server.get("/api/v1/stats/overview").await;
|
|
let body: serde_json::Value = resp.json().await.unwrap();
|
|
// total_findings counts all findings regardless of status
|
|
assert_eq!(body["data"]["total_findings"], 1);
|
|
|
|
server.cleanup().await;
|
|
}
|