cqs 1.22.0

Code intelligence and RAG for AI agents. Semantic search, call graphs, impact analysis, type dependencies, and smart context assembly — in single tool calls. 54 languages + L5X/L5K PLC exports, 91.2% Recall@1 (BGE-large), 0.951 MRR (296 queries). Local ML, GPU-accelerated.
Documentation
//! Common test fixtures and helpers
//!
//! Usage in test files:
//! ```ignore
//! mod common;
//! use common::TestStore;
//! ```

use cqs::embedder::Embedding;
use cqs::parser::{Chunk, ChunkType, Language};
use cqs::store::{ModelInfo, Store};
use std::path::PathBuf;
use tempfile::TempDir;

/// Test store with automatic cleanup
///
/// Wraps a `Store` with its backing `TempDir`, ensuring the directory
/// lives as long as the store is in use.
pub struct TestStore {
    /// The store instance
    pub store: Store,
    /// Temp directory (kept alive to prevent cleanup)
    _dir: TempDir,
}

impl TestStore {
    /// Create an initialized test store in a temporary directory
    pub fn new() -> Self {
        let dir = TempDir::new().expect("Failed to create temp dir");
        let db_path = dir.path().join("index.db");
        let store = Store::open(&db_path).expect("Failed to open store");
        store
            .init(&ModelInfo::default())
            .expect("Failed to init store");
        Self { store, _dir: dir }
    }

    /// Get the database path for this test store
    #[allow(dead_code)]
    pub fn db_path(&self) -> PathBuf {
        self._dir.path().join("index.db")
    }

    /// Create a test store with custom model info
    #[allow(dead_code)]
    pub fn with_model(model: &ModelInfo) -> Self {
        let dir = TempDir::new().expect("Failed to create temp dir");
        let db_path = dir.path().join("index.db");
        let store = Store::open(&db_path).expect("Failed to open store");
        store.init(model).expect("Failed to init store");
        Self { store, _dir: dir }
    }
}

impl std::ops::Deref for TestStore {
    type Target = Store;

    fn deref(&self) -> &Self::Target {
        &self.store
    }
}

/// Create a test chunk with sensible defaults
#[allow(dead_code)]
pub fn test_chunk(name: &str, content: &str) -> Chunk {
    let hash = blake3::hash(content.as_bytes()).to_hex().to_string();
    Chunk {
        id: format!("test.rs:1:{}", &hash[..8]),
        file: PathBuf::from("test.rs"),
        language: Language::Rust,
        chunk_type: ChunkType::Function,
        name: name.to_string(),
        signature: format!("fn {}()", name),
        content: content.to_string(),
        doc: None,
        line_start: 1,
        line_end: 5,
        content_hash: hash,
        parent_id: None,
        window_idx: None,
        parent_type_name: None,
    }
}

/// Create a mock 768-dim embedding (E5-base-v2)
///
/// The seed value determines the direction of the embedding vector.
/// Same seed = same direction = high similarity.
/// Different seeds = different directions = lower similarity.
pub fn mock_embedding(seed: f32) -> Embedding {
    let mut v = vec![seed; cqs::EMBEDDING_DIM];
    // Normalize to unit length
    let norm: f32 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
    if norm > 0.0 {
        for x in &mut v {
            *x /= norm;
        }
    }
    Embedding::new(v)
}