use super::*; fn setup_db() -> Connection { let conn = Connection::open_in_memory().unwrap(); conn.execute_batch( " CREATE TABLE projects ( id INTEGER PRIMARY KEY, gitlab_project_id INTEGER UNIQUE NOT NULL, path_with_namespace TEXT NOT NULL, default_branch TEXT, web_url TEXT, created_at INTEGER, updated_at INTEGER, raw_payload_id INTEGER ); CREATE INDEX idx_projects_path ON projects(path_with_namespace); ", ) .unwrap(); conn } fn insert_project(conn: &Connection, id: i64, path: &str) { conn.execute( "INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (?1, ?2, ?3)", rusqlite::params![id, id * 100, path], ) .unwrap(); } #[test] fn test_exact_match() { let conn = setup_db(); insert_project(&conn, 1, "backend/auth-service"); let id = resolve_project(&conn, "backend/auth-service").unwrap(); assert_eq!(id, 1); } #[test] fn test_case_insensitive() { let conn = setup_db(); insert_project(&conn, 1, "backend/auth-service"); let id = resolve_project(&conn, "Backend/Auth-Service").unwrap(); assert_eq!(id, 1); } #[test] fn test_suffix_unambiguous() { let conn = setup_db(); insert_project(&conn, 1, "backend/auth-service"); insert_project(&conn, 2, "frontend/web-ui"); let id = resolve_project(&conn, "auth-service").unwrap(); assert_eq!(id, 1); } #[test] fn test_suffix_ambiguous() { let conn = setup_db(); insert_project(&conn, 1, "backend/auth-service"); insert_project(&conn, 2, "frontend/auth-service"); let err = resolve_project(&conn, "auth-service").unwrap_err(); let msg = err.to_string(); assert!( msg.contains("ambiguous"), "Expected ambiguous error, got: {}", msg ); assert!(msg.contains("backend/auth-service")); assert!(msg.contains("frontend/auth-service")); } #[test] fn test_substring_unambiguous() { let conn = setup_db(); insert_project(&conn, 1, "vs/python-code"); insert_project(&conn, 2, "vs/typescript-code"); let id = resolve_project(&conn, "typescript").unwrap(); assert_eq!(id, 2); } #[test] fn test_substring_case_insensitive() { let conn = setup_db(); insert_project(&conn, 1, "vs/python-code"); insert_project(&conn, 2, "vs/typescript-code"); let id = resolve_project(&conn, "TypeScript").unwrap(); assert_eq!(id, 2); } #[test] fn test_substring_ambiguous() { let conn = setup_db(); insert_project(&conn, 1, "vs/python-code"); insert_project(&conn, 2, "vs/typescript-code"); let err = resolve_project(&conn, "code").unwrap_err(); let msg = err.to_string(); assert!( msg.contains("ambiguous"), "Expected ambiguous error, got: {}", msg ); assert!(msg.contains("vs/python-code")); assert!(msg.contains("vs/typescript-code")); } #[test] fn test_suffix_preferred_over_substring() { let conn = setup_db(); insert_project(&conn, 1, "backend/auth-service"); insert_project(&conn, 2, "backend/auth-service-v2"); let id = resolve_project(&conn, "auth-service").unwrap(); assert_eq!(id, 1); } #[test] fn test_no_match() { let conn = setup_db(); insert_project(&conn, 1, "backend/auth-service"); let err = resolve_project(&conn, "nonexistent").unwrap_err(); let msg = err.to_string(); assert!( msg.contains("not found"), "Expected not found error, got: {}", msg ); assert!(msg.contains("backend/auth-service")); } #[test] fn test_empty_projects() { let conn = setup_db(); let err = resolve_project(&conn, "anything").unwrap_err(); let msg = err.to_string(); assert!(msg.contains("No projects have been synced")); } #[test] fn test_underscore_not_wildcard() { let conn = setup_db(); insert_project(&conn, 1, "backend/my_project"); insert_project(&conn, 2, "backend/my-project"); // `_` in user input must not match `-` (LIKE wildcard behavior) let id = resolve_project(&conn, "my_project").unwrap(); assert_eq!(id, 1); } #[test] fn test_percent_not_wildcard() { let conn = setup_db(); insert_project(&conn, 1, "backend/a%b"); insert_project(&conn, 2, "backend/axyzb"); // `%` in user input must not match arbitrary strings let id = resolve_project(&conn, "a%b").unwrap(); assert_eq!(id, 1); } #[test] fn test_lookup_by_gitlab_project_id() { use crate::test_support::{insert_project as insert_proj, setup_test_db}; let conn = setup_test_db(); insert_proj(&conn, 1, "team/alpha"); insert_proj(&conn, 2, "team/beta"); // insert_project sets gitlab_project_id = id * 100 let path: String = conn .query_row( "SELECT path_with_namespace FROM projects WHERE gitlab_project_id = ?1 ORDER BY id LIMIT 1", rusqlite::params![200_i64], |row| row.get(0), ) .unwrap(); assert_eq!(path, "team/beta"); }