From 915d449f4150d44ee36d328efa22bb5c5e301e99 Mon Sep 17 00:00:00 2001 From: juice094 <160722440+juice094@users.noreply.github.com> Date: Fri, 15 May 2026 13:43:45 +0800 Subject: [PATCH 1/2] test(sync): verify collect_tasks skipped reasons (unmanaged/excluded/path_excluded) Wave 1 coverage: add 3 tests for the new SkippedRepoInfo return type. - test_collect_tasks_default_mode_excludes_untagged: verify reason='unmanaged' - test_collect_tasks_exclude_reason: verify --exclude produces reason='excluded' - test_collect_tasks_path_excluded_reason: verify exclude_paths produces reason='path_excluded' (uses cross-platform temp_dir for Windows compat) --- src/sync/tests.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/sync/tests.rs b/src/sync/tests.rs index a6a5b98..ebaa0e3 100644 --- a/src/sync/tests.rs +++ b/src/sync/tests.rs @@ -301,6 +301,78 @@ async fn test_collect_tasks_default_mode_excludes_untagged() { assert_eq!(tasks.len(), 1); assert_eq!(skipped.len(), 1); assert_eq!(tasks[0].id, "managed"); + assert_eq!(skipped[0].id, "untagged"); + assert_eq!(skipped[0].reason, "unmanaged"); +} + +#[tokio::test] +async fn test_collect_tasks_exclude_reason() { + use crate::registry::{RemoteEntry, RepoEntry, WorkspaceRegistry}; + use chrono::Utc; + use std::path::PathBuf; + + let mut conn = WorkspaceRegistry::init_in_memory().unwrap(); + + let managed = RepoEntry { + id: "repo-a".to_string(), + local_path: PathBuf::from("/tmp/repo-a"), + tags: vec!["managed".to_string()], + language: Some("rust".to_string()), + discovered_at: Utc::now(), + workspace_type: "git".to_string(), + data_tier: "private".to_string(), + last_synced_at: None, + stars: None, + remotes: vec![RemoteEntry { + remote_name: "origin".to_string(), + upstream_url: Some("https://github.com/test/repo-a".to_string()), + default_branch: Some("main".to_string()), + last_sync: None, + }], + }; + crate::registry::repo::save_repo(&mut conn, &managed).unwrap(); + + let (tasks, skipped) = tasks::collect_tasks(&conn, None, Some("repo-a"), &[]).await.unwrap(); + assert_eq!(tasks.len(), 0); + assert_eq!(skipped.len(), 1); + assert_eq!(skipped[0].id, "repo-a"); + assert_eq!(skipped[0].reason, "excluded"); +} + +#[tokio::test] +async fn test_collect_tasks_path_excluded_reason() { + use crate::registry::{RemoteEntry, RepoEntry, WorkspaceRegistry}; + use chrono::Utc; + + let mut conn = WorkspaceRegistry::init_in_memory().unwrap(); + + let tmp = std::env::temp_dir(); + let repo_path = tmp.join("node_modules").join("repo-a"); + let managed = RepoEntry { + id: "repo-a".to_string(), + local_path: repo_path.clone(), + tags: vec!["managed".to_string()], + language: Some("rust".to_string()), + discovered_at: Utc::now(), + workspace_type: "git".to_string(), + data_tier: "private".to_string(), + last_synced_at: None, + stars: None, + remotes: vec![RemoteEntry { + remote_name: "origin".to_string(), + upstream_url: Some("https://github.com/test/repo-a".to_string()), + default_branch: Some("main".to_string()), + last_sync: None, + }], + }; + crate::registry::repo::save_repo(&mut conn, &managed).unwrap(); + + let exclude_paths = vec![tmp.join("node_modules").to_string_lossy().to_string()]; + let (tasks, skipped) = tasks::collect_tasks(&conn, None, None, &exclude_paths).await.unwrap(); + assert_eq!(tasks.len(), 0); + assert_eq!(skipped.len(), 1); + assert_eq!(skipped[0].id, "repo-a"); + assert_eq!(skipped[0].reason, "path_excluded"); } #[tokio::test] From a00cbac88133838631e2d54c490912435e89b846 Mon Sep 17 00:00:00 2001 From: juice094 <160722440+juice094@users.noreply.github.com> Date: Fri, 15 May 2026 13:47:40 +0800 Subject: [PATCH 2/2] test(query): verify 'repos' expression returns all registered repositories Regression guard for the fix in PR #49: ensures that 'query repos' short-circuits keyword filtering and returns the full registry list. --- src/greptime.rs | 27 ++++++++++++++++ src/query.rs | 45 ++++++++++++++++++++++++++- tools/invariant-checks/run-checks.ps1 | 4 +++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/greptime.rs b/src/greptime.rs index 37c311d..33f6a3f 100644 --- a/src/greptime.rs +++ b/src/greptime.rs @@ -116,3 +116,30 @@ impl GreptimeClient { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::registry::HealthEntry; + use chrono::Utc; + + #[tokio::test] + async fn test_write_health_noop_when_disabled() { + let config = GreptimeConfig::default(); // enabled = false + let client = GreptimeClient::new(&config); + let entry = HealthEntry { + status: "ok".to_string(), + ahead: 0, + behind: 0, + checked_at: Utc::now(), + }; + assert!(client.write_health("repo1", &entry).await.is_ok()); + } + + #[tokio::test] + async fn test_write_stars_noop_when_disabled() { + let config = GreptimeConfig::default(); + let client = GreptimeClient::new(&config); + assert!(client.write_stars("repo1", 42).await.is_ok()); + } +} diff --git a/src/query.rs b/src/query.rs index 28b9e2c..5800f83 100644 --- a/src/query.rs +++ b/src/query.rs @@ -638,7 +638,7 @@ mod tests { local_path: PathBuf::from(path), tags: tags.iter().map(|t| t.to_string()).collect(), discovered_at: Utc::now(), - language: None, + language: Some("rust".to_string()), workspace_type: "git".to_string(), data_tier: "private".to_string(), last_synced_at: None, @@ -776,4 +776,47 @@ mod tests { let cond = Condition::Note("todo".to_string()); assert!(eval_condition(&r, &cond, None, None, ¬es).is_none()); } + + #[tokio::test] + async fn test_run_json_repos_returns_all() { + use crate::registry::{RepoEntry, WorkspaceRegistry}; + use chrono::Utc; + use std::path::PathBuf; + + let mut conn = WorkspaceRegistry::init_in_memory().unwrap(); + + let repo1 = RepoEntry { + id: "repo1".to_string(), + local_path: PathBuf::from("/tmp/repo1"), + tags: vec![], + language: Some("rust".to_string()), + discovered_at: Utc::now(), + workspace_type: "git".to_string(), + data_tier: "private".to_string(), + last_synced_at: None, + stars: None, + remotes: vec![], + }; + let repo2 = RepoEntry { + id: "repo2".to_string(), + local_path: PathBuf::from("/tmp/repos/repo2"), + tags: vec![], + language: Some("rust".to_string()), + discovered_at: Utc::now(), + workspace_type: "git".to_string(), + data_tier: "private".to_string(), + last_synced_at: None, + stars: None, + remotes: vec![], + }; + crate::registry::repo::save_repo(&mut conn, &repo1).unwrap(); + crate::registry::repo::save_repo(&mut conn, &repo2).unwrap(); + + let config = crate::config::Config::default(); + let result = run_json(&conn, "repos", 0, 1, &config).await.unwrap(); + + assert_eq!(result["count"].as_u64().unwrap(), 2); + let results = result["results"].as_array().unwrap(); + assert_eq!(results.len(), 2); + } } diff --git a/tools/invariant-checks/run-checks.ps1 b/tools/invariant-checks/run-checks.ps1 index 5d83470..1b15813 100644 --- a/tools/invariant-checks/run-checks.ps1 +++ b/tools/invariant-checks/run-checks.ps1 @@ -79,6 +79,10 @@ if (-not $diffFiles) { if ($file -notmatch '\.rs$') { continue } if ($file -match 'tests?/|_test\.rs$|benches/|examples/') { continue } + # Inline test modules (e.g. src/foo/tests.rs included via #[cfg(test)] mod tests;) + # have no #[cfg(test)] marker inside the file itself. + if ($file -match 'tests\.rs$') { continue } + # Get test line ranges for the file $testRanges = Get-TestLineRanges $file