test: Remove redundant comments from test files

Applies the same doc comment cleanup to test files:
- Removes test module headers (//! lines)
- Removes obvious test function comments
- Retains comments explaining non-obvious test scenarios

Test names should be descriptive enough to convey intent without
additional comments. Complex test setup or assertions that need
explanation retain their comments.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-02-05 00:04:39 -05:00
parent 65583ed5d6
commit dd2869fd98
9 changed files with 7 additions and 158 deletions

View File

@@ -1,5 +1,3 @@
//! Tests for DiffNote position extraction in note transformer.
use lore::gitlab::transformers::discussion::transform_notes_with_diff_position;
use lore::gitlab::types::{
GitLabAuthor, GitLabDiscussion, GitLabLineRange, GitLabLineRangePoint, GitLabNote,
@@ -60,8 +58,6 @@ fn make_discussion(notes: Vec<GitLabNote>) -> GitLabDiscussion {
}
}
// === DiffNote Position Field Extraction ===
#[test]
fn extracts_position_paths_from_diffnote() {
let position = GitLabNotePosition {
@@ -174,7 +170,7 @@ fn line_range_uses_old_line_fallback_when_new_line_missing() {
line_code: None,
line_type: Some("old".to_string()),
old_line: Some(20),
new_line: None, // missing - should fall back to old_line
new_line: None,
},
end: GitLabLineRangePoint {
line_code: None,
@@ -203,8 +199,6 @@ fn line_range_uses_old_line_fallback_when_new_line_missing() {
assert_eq!(notes[0].position_line_range_end, Some(25));
}
// === Regular Notes (non-DiffNote) ===
#[test]
fn regular_note_has_none_for_all_position_fields() {
let note = make_basic_note(1, "2024-01-16T09:00:00.000Z");
@@ -224,8 +218,6 @@ fn regular_note_has_none_for_all_position_fields() {
assert_eq!(notes[0].position_head_sha, None);
}
// === Strict Timestamp Parsing ===
#[test]
fn returns_error_for_invalid_created_at_timestamp() {
let mut note = make_basic_note(1, "2024-01-16T09:00:00.000Z");
@@ -264,8 +256,6 @@ fn returns_error_for_invalid_resolved_at_timestamp() {
assert!(result.is_err());
}
// === Mixed Discussion (DiffNote + Regular Notes) ===
#[test]
fn handles_mixed_diffnote_and_regular_notes() {
let position = GitLabNotePosition {
@@ -286,16 +276,12 @@ fn handles_mixed_diffnote_and_regular_notes() {
let notes = transform_notes_with_diff_position(&discussion, 100).unwrap();
assert_eq!(notes.len(), 2);
// First note is DiffNote with position
assert_eq!(notes[0].position_new_path, Some("file.rs".to_string()));
assert_eq!(notes[0].position_new_line, Some(42));
// Second note is regular with None position fields
assert_eq!(notes[1].position_new_path, None);
assert_eq!(notes[1].position_new_line, None);
}
// === Position Preservation ===
#[test]
fn preserves_note_position_index() {
let pos1 = GitLabNotePosition {
@@ -330,11 +316,8 @@ fn preserves_note_position_index() {
assert_eq!(notes[1].position, 1);
}
// === Edge Cases ===
#[test]
fn handles_diffnote_with_empty_position_fields() {
// DiffNote exists but all position fields are None
let position = GitLabNotePosition {
old_path: None,
new_path: None,
@@ -351,7 +334,6 @@ fn handles_diffnote_with_empty_position_fields() {
let notes = transform_notes_with_diff_position(&discussion, 100).unwrap();
// All position fields should be None, not cause an error
assert_eq!(notes[0].position_old_path, None);
assert_eq!(notes[0].position_new_path, None);
}
@@ -376,6 +358,5 @@ fn handles_file_position_type() {
assert_eq!(notes[0].position_type, Some("file".to_string()));
assert_eq!(notes[0].position_new_path, Some("binary.bin".to_string()));
// File-level comments have no line numbers
assert_eq!(notes[0].position_new_line, None);
}

View File

@@ -1,16 +1,8 @@
//! Integration tests for embedding storage and vector search.
//!
//! These tests create an in-memory SQLite database with sqlite-vec loaded,
//! apply all migrations through 010 (chunk config), and verify KNN search
//! and metadata operations.
use lore::core::db::create_connection;
use rusqlite::Connection;
use std::path::PathBuf;
use tempfile::TempDir;
/// Create a test DB on disk (required for sqlite-vec which needs the extension loaded).
/// Uses create_connection to get the sqlite-vec extension registered.
fn create_test_db() -> (TempDir, Connection) {
let tmp = TempDir::new().unwrap();
let db_path = tmp.path().join("test.db");
@@ -35,7 +27,6 @@ fn create_test_db() -> (TempDir, Connection) {
.unwrap_or_else(|e| panic!("Migration {} failed: {}", version, e));
}
// Seed a project
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
[],
@@ -54,7 +45,6 @@ fn insert_document(conn: &Connection, id: i64, title: &str, content: &str) {
.unwrap();
}
/// Create a 768-dim vector with a specific dimension set to 1.0 (unit vector along axis).
fn axis_vector(dim: usize) -> Vec<f32> {
let mut v = vec![0.0f32; 768];
v[dim] = 1.0;
@@ -89,12 +79,10 @@ fn knn_search_returns_nearest_neighbors() {
insert_document(&conn, 2, "Doc B", "Content about database optimization.");
insert_document(&conn, 3, "Doc C", "Content about logging infrastructure.");
// Doc 1: axis 0, Doc 2: axis 1, Doc 3: axis 2
insert_embedding(&conn, 1, 0, &axis_vector(0));
insert_embedding(&conn, 2, 0, &axis_vector(1));
insert_embedding(&conn, 3, 0, &axis_vector(2));
// Query vector close to axis 0 (should match doc 1)
let mut query = vec![0.0f32; 768];
query[0] = 0.9;
query[1] = 0.1;
@@ -132,7 +120,6 @@ fn knn_search_deduplicates_chunks() {
"Very long content that was chunked.",
);
// Same document, two chunks, both similar to query
let mut v1 = vec![0.0f32; 768];
v1[0] = 1.0;
let mut v2 = vec![0.0f32; 768];
@@ -144,7 +131,6 @@ fn knn_search_deduplicates_chunks() {
let results = lore::search::search_vector(&conn, &axis_vector(0), 10).unwrap();
// Should deduplicate: same document_id appears at most once
let unique_docs: std::collections::HashSet<i64> =
results.iter().map(|r| r.document_id).collect();
assert_eq!(
@@ -161,7 +147,6 @@ fn orphan_trigger_deletes_embeddings_on_document_delete() {
insert_document(&conn, 1, "Will be deleted", "Content.");
insert_embedding(&conn, 1, 0, &axis_vector(0));
// Verify embedding exists
let count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM embeddings WHERE rowid = 1000",
@@ -171,11 +156,9 @@ fn orphan_trigger_deletes_embeddings_on_document_delete() {
.unwrap();
assert_eq!(count, 1, "Embedding should exist before delete");
// Delete the document
conn.execute("DELETE FROM documents WHERE id = 1", [])
.unwrap();
// Verify embedding was cascade-deleted via trigger
let count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM embeddings WHERE rowid = 1000",
@@ -188,7 +171,6 @@ fn orphan_trigger_deletes_embeddings_on_document_delete() {
"Trigger should delete embeddings when document is deleted"
);
// Verify metadata was cascade-deleted via FK
let meta_count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM embedding_metadata WHERE document_id = 1",
@@ -207,19 +189,12 @@ fn empty_database_returns_no_results() {
assert!(results.is_empty(), "Empty DB should return no results");
}
// --- Bug-fix regression tests ---
#[test]
fn overflow_doc_with_error_sentinel_not_re_detected_as_pending() {
// Bug 2: Documents skipped for chunk overflow must record a sentinel error
// in embedding_metadata so they are not re-detected as pending on subsequent
// pipeline runs (which would cause an infinite re-processing loop).
let (_tmp, conn) = create_test_db();
insert_document(&conn, 1, "Overflow doc", "Some content");
// Simulate what the pipeline does when a document exceeds CHUNK_ROWID_MULTIPLIER:
// it records an error sentinel at chunk_index=0.
let now = chrono::Utc::now().timestamp_millis();
conn.execute(
"INSERT INTO embedding_metadata
@@ -230,7 +205,6 @@ fn overflow_doc_with_error_sentinel_not_re_detected_as_pending() {
)
.unwrap();
// Now find_pending_documents should NOT return this document
let pending =
lore::embedding::find_pending_documents(&conn, 100, 0, "nomic-embed-text").unwrap();
assert!(
@@ -239,7 +213,6 @@ fn overflow_doc_with_error_sentinel_not_re_detected_as_pending() {
pending.len()
);
// count_pending_documents should also return 0
let count = lore::embedding::count_pending_documents(&conn, "nomic-embed-text").unwrap();
assert_eq!(
count, 0,
@@ -249,11 +222,8 @@ fn overflow_doc_with_error_sentinel_not_re_detected_as_pending() {
#[test]
fn count_and_find_pending_agree() {
// Bug 1: count_pending_documents and find_pending_documents must use
// logically equivalent WHERE clauses to produce consistent results.
let (_tmp, conn) = create_test_db();
// Case 1: No documents at all
let count = lore::embedding::count_pending_documents(&conn, "nomic-embed-text").unwrap();
let found =
lore::embedding::find_pending_documents(&conn, 1000, 0, "nomic-embed-text").unwrap();
@@ -263,7 +233,6 @@ fn count_and_find_pending_agree() {
"Empty DB: count and find should agree"
);
// Case 2: New document (no metadata)
insert_document(&conn, 1, "New doc", "Content");
let count = lore::embedding::count_pending_documents(&conn, "nomic-embed-text").unwrap();
let found =
@@ -275,7 +244,6 @@ fn count_and_find_pending_agree() {
);
assert_eq!(count, 1);
// Case 3: Document with matching metadata (not pending)
let now = chrono::Utc::now().timestamp_millis();
conn.execute(
"INSERT INTO embedding_metadata
@@ -295,7 +263,6 @@ fn count_and_find_pending_agree() {
);
assert_eq!(count, 0);
// Case 4: Config drift (chunk_max_bytes mismatch)
conn.execute(
"UPDATE embedding_metadata SET chunk_max_bytes = 999 WHERE document_id = 1",
[],
@@ -314,14 +281,11 @@ fn count_and_find_pending_agree() {
#[test]
fn full_embed_delete_is_atomic() {
// Bug 7: The --full flag's two DELETE statements should be atomic.
// This test verifies that both tables are cleared together.
let (_tmp, conn) = create_test_db();
insert_document(&conn, 1, "Doc", "Content");
insert_embedding(&conn, 1, 0, &axis_vector(0));
// Verify data exists
let meta_count: i64 = conn
.query_row("SELECT COUNT(*) FROM embedding_metadata", [], |r| r.get(0))
.unwrap();
@@ -331,7 +295,6 @@ fn full_embed_delete_is_atomic() {
assert_eq!(meta_count, 1);
assert_eq!(embed_count, 1);
// Execute the atomic delete (same as embed.rs --full)
conn.execute_batch(
"BEGIN;
DELETE FROM embedding_metadata;

View File

@@ -1,5 +1,3 @@
//! Tests for test fixtures - verifies they deserialize correctly.
use lore::gitlab::types::{GitLabDiscussion, GitLabIssue};
use serde::de::DeserializeOwned;
use std::path::PathBuf;
@@ -38,14 +36,11 @@ fn fixture_gitlab_issues_page_deserializes() {
"Need at least 3 issues for pagination tests"
);
// Check first issue has labels
assert!(!issues[0].labels.is_empty());
// Check second issue has null description and empty labels
assert!(issues[1].description.is_none());
assert!(issues[1].labels.is_empty());
// Check third issue has multiple labels
assert!(issues[2].labels.len() >= 3);
}
@@ -67,7 +62,6 @@ fn fixture_gitlab_discussions_page_deserializes() {
"Need multiple discussions for testing"
);
// Check we have both individual_note=true and individual_note=false
let has_individual = discussions.iter().any(|d| d.individual_note);
let has_threaded = discussions.iter().any(|d| !d.individual_note);
assert!(

View File

@@ -1,8 +1,3 @@
//! Integration tests for FTS5 search.
//!
//! These tests create an in-memory SQLite database, apply migrations through 008 (FTS5),
//! seed documents, and verify search behavior.
use rusqlite::Connection;
fn create_test_db() -> Connection {
@@ -28,7 +23,6 @@ fn create_test_db() -> Connection {
.unwrap_or_else(|e| panic!("Migration {} failed: {}", version, e));
}
// Seed a project
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
[],
@@ -110,7 +104,6 @@ fn fts_stemming_matches() {
"Deployment configuration for production servers.",
);
// "running" should match "runner" and "executing" via porter stemmer
let results =
lore::search::search_fts(&conn, "running", 10, lore::search::FtsQueryMode::Safe).unwrap();
assert!(
@@ -157,11 +150,9 @@ fn fts_special_characters_handled() {
"The C++ compiler segfaults on template metaprogramming.",
);
// Special characters should not crash the search
let results =
lore::search::search_fts(&conn, "C++ compiler", 10, lore::search::FtsQueryMode::Safe)
.unwrap();
// Safe mode sanitizes the query — it should still return results or at least not crash
assert!(results.len() <= 1);
}
@@ -169,7 +160,6 @@ fn fts_special_characters_handled() {
fn fts_result_ordering_by_relevance() {
let conn = create_test_db();
// Doc 1: "authentication" in title and content
insert_document(
&conn,
1,
@@ -177,7 +167,6 @@ fn fts_result_ordering_by_relevance() {
"Authentication system redesign",
"The authentication system needs a complete redesign. Authentication flows are broken.",
);
// Doc 2: "authentication" only in content, once
insert_document(
&conn,
2,
@@ -185,7 +174,6 @@ fn fts_result_ordering_by_relevance() {
"Login page update",
"Updated the login page with better authentication error messages.",
);
// Doc 3: unrelated
insert_document(
&conn,
3,
@@ -203,7 +191,6 @@ fn fts_result_ordering_by_relevance() {
.unwrap();
assert!(results.len() >= 2, "Should match at least 2 documents");
// Doc 1 should rank higher (more occurrences of the term)
assert_eq!(
results[0].document_id, 1,
"Document with more term occurrences should rank first"
@@ -246,7 +233,6 @@ fn fts_snippet_generated() {
.unwrap();
assert!(!results.is_empty());
// Snippet should contain some text (may have FTS5 highlight markers)
assert!(
!results[0].snippet.is_empty(),
"Snippet should be generated"
@@ -265,7 +251,6 @@ fn fts_triggers_sync_on_insert() {
"This is test content for FTS trigger verification.",
);
// Verify FTS table has an entry via direct query
let fts_count: i64 = conn
.query_row(
"SELECT COUNT(*) FROM documents_fts WHERE documents_fts MATCH 'test'",
@@ -289,7 +274,6 @@ fn fts_triggers_sync_on_delete() {
"This content will be deleted from the index.",
);
// Verify it's indexed
let before: i64 = conn
.query_row(
"SELECT COUNT(*) FROM documents_fts WHERE documents_fts MATCH 'deletable'",
@@ -299,11 +283,9 @@ fn fts_triggers_sync_on_delete() {
.unwrap();
assert_eq!(before, 1);
// Delete the document
conn.execute("DELETE FROM documents WHERE id = 1", [])
.unwrap();
// Verify it's removed from FTS
let after: i64 = conn
.query_row(
"SELECT COUNT(*) FROM documents_fts WHERE documents_fts MATCH 'deletable'",
@@ -318,7 +300,6 @@ fn fts_triggers_sync_on_delete() {
fn fts_null_title_handled() {
let conn = create_test_db();
// Discussion documents have NULL titles
conn.execute(
"INSERT INTO documents (id, source_type, source_id, project_id, title, content_text, content_hash, url)
VALUES (1, 'discussion', 1, 1, NULL, 'Discussion about API rate limiting strategies.', 'hash1', 'https://example.com/1')",

View File

@@ -1,9 +1,3 @@
//! Golden query test suite.
//!
//! Verifies end-to-end search quality with known-good expected results.
//! Uses a seeded SQLite DB with deterministic fixture data and no external
//! dependencies (no Ollama, no GitLab).
#![allow(dead_code)]
use rusqlite::Connection;
@@ -12,7 +6,6 @@ use std::path::PathBuf;
use lore::search::{FtsQueryMode, SearchFilters, SearchMode, apply_filters, search_fts};
/// A golden query test case.
#[derive(Debug, Deserialize)]
struct GoldenQuery {
query: String,
@@ -42,12 +35,10 @@ fn load_golden_queries() -> Vec<GoldenQuery> {
.unwrap_or_else(|e| panic!("Failed to parse golden queries: {}", e))
}
/// Create an in-memory database with FTS5 schema and seed deterministic fixture data.
fn create_seeded_db() -> Connection {
let conn = Connection::open_in_memory().unwrap();
conn.pragma_update(None, "foreign_keys", "ON").unwrap();
// Apply migrations 001-008 (FTS5)
let migrations_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("migrations");
for version in 1..=8 {
let entries: Vec<_> = std::fs::read_dir(&migrations_dir)
@@ -65,7 +56,6 @@ fn create_seeded_db() -> Connection {
.unwrap_or_else(|e| panic!("Migration {} failed: {}", version, e));
}
// Seed project
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace, web_url)
VALUES (1, 100, 'group/project', 'https://gitlab.example.com/group/project')",
@@ -73,9 +63,7 @@ fn create_seeded_db() -> Connection {
)
.unwrap();
// Seed deterministic documents
let documents = vec![
// id=1: Auth issue (matches: authentication, login, OAuth, JWT, token, refresh)
(
1,
"issue",
@@ -86,7 +74,6 @@ fn create_seeded_db() -> Connection {
Multiple users reported authentication failures across all OAuth providers.",
"testuser",
),
// id=2: User profile MR (matches: user, profile, avatar, upload)
(
2,
"merge_request",
@@ -96,7 +83,6 @@ fn create_seeded_db() -> Connection {
responsive design for mobile and desktop viewports.",
"developer1",
),
// id=3: Database migration issue (matches: database, migration, PostgreSQL, schema)
(
3,
"issue",
@@ -106,7 +92,6 @@ fn create_seeded_db() -> Connection {
rewritten to use the new schema modification syntax. All staging environments affected.",
"dba_admin",
),
// id=4: Performance MR (matches: performance, optimization, caching, query)
(
4,
"merge_request",
@@ -116,7 +101,6 @@ fn create_seeded_db() -> Connection {
to 180ms. Added connection pooling and prepared statement caching.",
"senior_dev",
),
// id=5: API rate limiting discussion (matches: API, rate, limiting, throttle)
(
5,
"discussion",
@@ -127,7 +111,6 @@ fn create_seeded_db() -> Connection {
Need to handle burst traffic during peak hours without throttling legitimate users.",
"architect",
),
// id=6: UI/CSS issue (matches: CSS, styling, frontend, responsive, UI)
(
6,
"issue",
@@ -138,7 +121,6 @@ fn create_seeded_db() -> Connection {
conflicting CSS specificity with the theme system.",
"frontend_dev",
),
// id=7: CI/CD MR (matches: CI, CD, pipeline, deployment, Docker)
(
7,
"merge_request",
@@ -148,7 +130,6 @@ fn create_seeded_db() -> Connection {
support for failed deployments. Pipeline runtime reduced from 45min to 12min.",
"devops_lead",
),
// id=8: Security issue (matches: security, vulnerability, XSS, injection)
(
8,
"issue",
@@ -169,7 +150,6 @@ fn create_seeded_db() -> Connection {
.unwrap();
}
// Seed labels for filtered queries
conn.execute_batch(
"INSERT INTO document_labels (document_id, label_name) VALUES (1, 'bug');
INSERT INTO document_labels (document_id, label_name) VALUES (1, 'authentication');
@@ -212,7 +192,6 @@ fn golden_queries_all_pass() {
for (i, gq) in queries.iter().enumerate() {
let mode = SearchMode::parse(&gq.mode).unwrap_or(SearchMode::Lexical);
// For lexical-only golden queries (no Ollama needed)
assert_eq!(
mode,
SearchMode::Lexical,
@@ -221,11 +200,9 @@ fn golden_queries_all_pass() {
gq.mode
);
// Run FTS search
let fts_results = search_fts(&conn, &gq.query, 50, FtsQueryMode::Safe).unwrap();
let doc_ids: Vec<i64> = fts_results.iter().map(|r| r.document_id).collect();
// Apply filters if any
let filters = build_search_filters(&gq.filters);
let filtered_ids = if filters.has_any_filter() {
apply_filters(&conn, &doc_ids, &filters).unwrap()
@@ -233,7 +210,6 @@ fn golden_queries_all_pass() {
doc_ids.clone()
};
// Check min_results
if filtered_ids.len() < gq.min_results {
failures.push(format!(
"FAIL [{}] \"{}\": expected >= {} results, got {} (description: {})",
@@ -246,13 +222,10 @@ fn golden_queries_all_pass() {
continue;
}
// Check each expected doc_id is in top max_rank
for expected_id in &gq.expected_doc_ids {
let position = filtered_ids.iter().position(|id| id == expected_id);
match position {
Some(pos) if pos < gq.max_rank => {
// Pass
}
Some(pos) if pos < gq.max_rank => {}
Some(pos) => {
failures.push(format!(
"FAIL [{}] \"{}\": expected doc_id {} in top {}, found at rank {} (description: {})",

View File

@@ -1,8 +1,3 @@
//! Integration tests for hybrid search combining FTS + vector.
//!
//! Tests all three search modes (lexical, semantic, hybrid) and
//! verifies graceful degradation when embeddings are unavailable.
use lore::core::db::create_connection;
use lore::search::{FtsQueryMode, SearchFilters, SearchMode, search_fts, search_hybrid};
use rusqlite::Connection;
@@ -89,7 +84,6 @@ fn lexical_mode_uses_fts_only() {
assert!(!results.is_empty(), "Lexical search should find results");
assert_eq!(results[0].document_id, 1);
// Lexical mode should not produce Ollama-related warnings
assert!(
warnings.iter().all(|w| !w.contains("Ollama")),
"Lexical mode should not warn about Ollama"
@@ -98,12 +92,10 @@ fn lexical_mode_uses_fts_only() {
#[test]
fn lexical_mode_no_embeddings_required() {
// Use in-memory DB without sqlite-vec for pure FTS
let conn = Connection::open_in_memory().unwrap();
conn.pragma_update(None, "foreign_keys", "ON").unwrap();
let migrations_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("migrations");
// Only apply through migration 008 (FTS5, no embeddings)
for version in 1..=8 {
let entries: Vec<_> = std::fs::read_dir(&migrations_dir)
.unwrap()
@@ -159,7 +151,7 @@ fn hybrid_mode_degrades_to_fts_without_client() {
let (results, warnings) = rt
.block_on(search_hybrid(
&conn,
None, // No Ollama client
None,
"performance slow",
SearchMode::Hybrid,
&filters,
@@ -168,7 +160,6 @@ fn hybrid_mode_degrades_to_fts_without_client() {
.unwrap();
assert!(!results.is_empty(), "Should fall back to FTS results");
// Should warn about missing Ollama client
assert!(
warnings.iter().any(|w| w.to_lowercase().contains("vector")
|| w.to_lowercase().contains("ollama")
@@ -184,14 +175,12 @@ fn hybrid_mode_degrades_to_fts_without_client() {
fn rrf_ranking_combines_signals() {
use lore::search::rank_rrf;
// Two documents with different rankings in each signal
let vector_results = vec![(1_i64, 0.1), (2, 0.5)]; // doc 1 closer
let fts_results = vec![(2_i64, -5.0), (1, -3.0)]; // doc 2 higher BM25
let vector_results = vec![(1_i64, 0.1), (2, 0.5)];
let fts_results = vec![(2_i64, -5.0), (1, -3.0)];
let rrf = rank_rrf(&vector_results, &fts_results);
assert_eq!(rrf.len(), 2, "Should return both documents");
// Both docs appear in both signals, so both get RRF scores
for r in &rrf {
assert!(r.rrf_score > 0.0, "RRF score should be positive");
}
@@ -235,7 +224,6 @@ fn filters_by_source_type() {
#[test]
fn search_mode_variants_exist() {
// Verify all enum variants compile and are distinct
let hybrid = SearchMode::Hybrid;
let lexical = SearchMode::Lexical;
let semantic = SearchMode::Semantic;

View File

@@ -1,5 +1,3 @@
//! Tests for database migrations.
use rusqlite::Connection;
use std::path::PathBuf;
@@ -41,7 +39,6 @@ fn migration_002_creates_issues_table() {
let conn = create_test_db();
apply_migrations(&conn, 2);
// Verify issues table exists with expected columns
let columns: Vec<String> = conn
.prepare("PRAGMA table_info(issues)")
.unwrap()
@@ -124,13 +121,11 @@ fn migration_002_enforces_state_check() {
let conn = create_test_db();
apply_migrations(&conn, 2);
// First insert a project so we can reference it
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
[],
).unwrap();
// Valid states should work
conn.execute(
"INSERT INTO issues (gitlab_id, project_id, iid, state, created_at, updated_at, last_seen_at)
VALUES (1, 1, 1, 'opened', 1000, 1000, 1000)",
@@ -143,7 +138,6 @@ fn migration_002_enforces_state_check() {
[],
).unwrap();
// Invalid state should fail
let result = conn.execute(
"INSERT INTO issues (gitlab_id, project_id, iid, state, created_at, updated_at, last_seen_at)
VALUES (3, 1, 3, 'invalid', 1000, 1000, 1000)",
@@ -158,7 +152,6 @@ fn migration_002_enforces_noteable_type_check() {
let conn = create_test_db();
apply_migrations(&conn, 2);
// Setup: project and issue
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
[],
@@ -169,14 +162,12 @@ fn migration_002_enforces_noteable_type_check() {
[],
).unwrap();
// Valid: Issue discussion with issue_id
conn.execute(
"INSERT INTO discussions (gitlab_discussion_id, project_id, issue_id, noteable_type, last_seen_at)
VALUES ('abc123', 1, 1, 'Issue', 1000)",
[],
).unwrap();
// Invalid: noteable_type not in allowed values
let result = conn.execute(
"INSERT INTO discussions (gitlab_discussion_id, project_id, issue_id, noteable_type, last_seen_at)
VALUES ('def456', 1, 1, 'Commit', 1000)",
@@ -184,7 +175,6 @@ fn migration_002_enforces_noteable_type_check() {
);
assert!(result.is_err());
// Invalid: Issue type but no issue_id
let result = conn.execute(
"INSERT INTO discussions (gitlab_discussion_id, project_id, noteable_type, last_seen_at)
VALUES ('ghi789', 1, 'Issue', 1000)",
@@ -198,7 +188,6 @@ fn migration_002_cascades_on_project_delete() {
let conn = create_test_db();
apply_migrations(&conn, 2);
// Setup: project, issue, label, issue_label link, discussion, note
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
[],
@@ -229,11 +218,9 @@ fn migration_002_cascades_on_project_delete() {
[],
).unwrap();
// Delete project
conn.execute("DELETE FROM projects WHERE id = 1", [])
.unwrap();
// Verify cascade: all related data should be gone
let issue_count: i64 = conn
.query_row("SELECT COUNT(*) FROM issues", [], |r| r.get(0))
.unwrap();
@@ -265,8 +252,6 @@ fn migration_002_updates_schema_version() {
assert_eq!(version, 2);
}
// === Migration 005 Tests ===
#[test]
fn migration_005_creates_milestones_table() {
let conn = create_test_db();
@@ -331,7 +316,6 @@ fn migration_005_milestones_cascade_on_project_delete() {
let conn = create_test_db();
apply_migrations(&conn, 5);
// Setup: project with milestone
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
[],
@@ -341,11 +325,9 @@ fn migration_005_milestones_cascade_on_project_delete() {
[],
).unwrap();
// Delete project
conn.execute("DELETE FROM projects WHERE id = 1", [])
.unwrap();
// Verify milestone is gone
let count: i64 = conn
.query_row("SELECT COUNT(*) FROM milestones", [], |r| r.get(0))
.unwrap();
@@ -357,7 +339,6 @@ fn migration_005_assignees_cascade_on_issue_delete() {
let conn = create_test_db();
apply_migrations(&conn, 5);
// Setup: project, issue, assignee
conn.execute(
"INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project')",
[],
@@ -373,10 +354,8 @@ fn migration_005_assignees_cascade_on_issue_delete() {
)
.unwrap();
// Delete issue
conn.execute("DELETE FROM issues WHERE id = 1", []).unwrap();
// Verify assignee link is gone
let count: i64 = conn
.query_row("SELECT COUNT(*) FROM issue_assignees", [], |r| r.get(0))
.unwrap();

View File

@@ -1,5 +1,3 @@
//! Tests for MR discussion transformer.
use lore::gitlab::transformers::discussion::transform_mr_discussion;
use lore::gitlab::types::{GitLabAuthor, GitLabDiscussion, GitLabNote};
@@ -77,7 +75,7 @@ fn transform_mr_discussion_computes_resolvable_from_notes() {
let result = transform_mr_discussion(&discussion, 100, 42);
assert!(result.resolvable);
assert!(!result.resolved); // resolvable but not resolved
assert!(!result.resolved);
}
#[test]

View File

@@ -1,5 +1,3 @@
//! Tests for MR transformer module.
use lore::gitlab::transformers::merge_request::transform_merge_request;
use lore::gitlab::types::{GitLabAuthor, GitLabMergeRequest, GitLabReferences, GitLabReviewer};
@@ -63,7 +61,7 @@ fn transforms_mr_with_all_fields() {
assert_eq!(result.merge_request.gitlab_id, 12345);
assert_eq!(result.merge_request.iid, 42);
assert_eq!(result.merge_request.project_id, 200); // Local project ID, not GitLab's
assert_eq!(result.merge_request.project_id, 200);
assert_eq!(result.merge_request.title, "Add user authentication");
assert_eq!(
result.merge_request.description,
@@ -105,22 +103,17 @@ fn parses_timestamps_to_ms_epoch() {
let mr = make_test_mr();
let result = transform_merge_request(&mr, 200).unwrap();
// 2024-01-15T10:00:00.000Z = 1705312800000 ms
assert_eq!(result.merge_request.created_at, 1705312800000);
// 2024-01-20T14:30:00.000Z = 1705761000000 ms
assert_eq!(result.merge_request.updated_at, 1705761000000);
// merged_at should also be parsed
assert_eq!(result.merge_request.merged_at, Some(1705761000000));
}
#[test]
fn handles_timezone_offset_timestamps() {
let mut mr = make_test_mr();
// GitLab can return timestamps with timezone offset
mr.created_at = "2024-01-15T05:00:00-05:00".to_string();
let result = transform_merge_request(&mr, 200).unwrap();
// 05:00 EST = 10:00 UTC = same as original test
assert_eq!(result.merge_request.created_at, 1705312800000);
}
@@ -322,7 +315,6 @@ fn handles_closed_at_timestamp() {
let result = transform_merge_request(&mr, 200).unwrap();
assert!(result.merge_request.merged_at.is_none());
// 2024-01-18T12:00:00.000Z = 1705579200000 ms
assert_eq!(result.merge_request.closed_at, Some(1705579200000));
}