Skip to main content

gobby_code/
lib.rs

1pub mod commands;
2pub mod config;
3pub mod db;
4pub mod falkor;
5pub mod freshness;
6pub mod git;
7pub mod graph;
8pub mod index;
9pub mod models;
10pub mod output;
11pub mod progress;
12pub mod project;
13pub mod projection;
14pub mod savings;
15pub mod schema;
16pub mod search;
17pub mod secrets;
18pub mod setup;
19pub mod skill;
20pub mod utils;
21pub mod vector;
22
23pub use index::api::{IndexDegradation, IndexDurations, IndexOutcome, IndexRequest, index_files};
24
25#[cfg(test)]
26mod tests {
27    use serde::Serialize;
28    use serde::de::DeserializeOwned;
29
30    fn assert_cli_independent_contract<T>()
31    where
32        T: Serialize + DeserializeOwned,
33    {
34        let type_name = std::any::type_name::<T>();
35        assert!(!type_name.contains("commands::"), "{type_name}");
36        assert!(!type_name.contains("output::"), "{type_name}");
37        assert!(!type_name.contains("clap"), "{type_name}");
38    }
39
40    #[test]
41    fn public_projection_api_is_cli_independent() {
42        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
43        for rel_path in [
44            "src/index/api.rs",
45            "src/graph/typed_query.rs",
46            "src/graph/code_graph.rs",
47            "src/vector/code_symbols.rs",
48            "src/projection/sync.rs",
49        ] {
50            assert!(
51                manifest_dir.join(rel_path).exists(),
52                "missing projection boundary module {rel_path}"
53            );
54        }
55
56        assert_cli_independent_contract::<crate::index::api::CodeFactWriteRequest>();
57        assert_cli_independent_contract::<crate::index::api::CodeFactWriteSummary>();
58        assert_cli_independent_contract::<crate::index::api::IndexRequest>();
59        assert_cli_independent_contract::<crate::index::api::IndexOutcome>();
60        assert_cli_independent_contract::<crate::index::api::IndexDurations>();
61        assert_cli_independent_contract::<crate::index::api::IndexDegradation>();
62        assert_cli_independent_contract::<crate::graph::code_graph::GraphLifecycleRequest>();
63        assert_cli_independent_contract::<crate::graph::code_graph::GraphLifecycleOutput>();
64        assert_cli_independent_contract::<crate::graph::code_graph::GraphReadRequest>();
65        assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorSearchRequest>(
66        );
67        assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorSearchHit>();
68        assert_cli_independent_contract::<crate::vector::code_symbols::CodeSymbolVectorPayload>();
69        assert_cli_independent_contract::<crate::projection::sync::ProjectionSyncRequest>();
70        assert_cli_independent_contract::<crate::projection::sync::ProjectionSyncStatus>();
71    }
72
73    #[test]
74    fn falkor_facade_is_available() {
75        let _ = std::any::type_name::<crate::falkor::FalkorClient>();
76
77        let ctx = crate::config::Context {
78            database_url: "postgresql://localhost/nonexistent".to_string(),
79            project_root: std::path::PathBuf::from("/nonexistent"),
80            project_id: "project-1".to_string(),
81            quiet: true,
82            falkordb: None,
83            qdrant: None,
84            embedding: None,
85            code_vectors: crate::config::CodeVectorSettings::default(),
86            daemon_url: None,
87        };
88
89        let value = crate::falkor::with_falkor(&ctx, 7usize, |_| Ok(9usize))
90            .expect("missing FalkorDB config degrades to default");
91        assert_eq!(value, 7);
92    }
93
94    #[test]
95    fn foundation_consumer_migration() {
96        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
97        let cargo = std::fs::read_to_string(manifest_dir.join("Cargo.toml"))
98            .expect("read gobby-code Cargo.toml");
99        for feature in ["postgres", "falkor", "qdrant", "search", "indexing"] {
100            assert!(
101                cargo.contains(feature),
102                "gobby-code must enable gobby-core feature `{feature}`"
103            );
104        }
105
106        let config =
107            std::fs::read_to_string(manifest_dir.join("src/config.rs")).expect("read config.rs");
108        assert!(config.contains("gobby_core::config::resolve_falkordb_config"));
109        assert!(config.contains("gobby_core::config::resolve_qdrant_config"));
110        assert!(config.contains("gobby_core::config::resolve_embedding_config"));
111        assert!(config.contains("impl gobby_core::config::ConfigSource for PostgresConfigSource"));
112        assert!(config.contains("gobby_core::postgres::read_config_value"));
113        assert!(!config.contains("fn decode_config_value("));
114
115        let db = std::fs::read_to_string(manifest_dir.join("src/db.rs")).expect("read db.rs");
116        assert!(db.contains("gobby_core::postgres::connect_readonly"));
117        assert!(db.contains("gobby_core::postgres::connect_readwrite"));
118        assert!(!db.contains("Client::connect(database_url, NoTls)"));
119
120        let graph = std::fs::read_to_string(manifest_dir.join("src/graph/code_graph.rs"))
121            .expect("read graph/code_graph.rs");
122        assert!(graph.contains("gobby_core::falkor::with_graph"));
123        assert!(!graph.contains("falkor::with_falkor"));
124
125        let semantic = std::fs::read_to_string(manifest_dir.join("src/search/semantic.rs"))
126            .expect("read search/semantic.rs");
127        assert!(semantic.contains("gobby_core::qdrant::with_qdrant"));
128        assert!(semantic.contains("gobby_core::qdrant::collection_name"));
129        assert!(semantic.contains("gobby_core::qdrant::search"));
130    }
131
132    #[test]
133    fn indexing_search_primitive_migration() {
134        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
135
136        let walker = std::fs::read_to_string(manifest_dir.join("src/index/walker.rs"))
137            .expect("read index/walker.rs");
138        assert!(walker.contains("gobby_core::indexing::WalkerSettings"));
139        let local_walker_builder = ["WalkBuilder", "::new(root)"].concat();
140        assert!(!walker.contains(&local_walker_builder));
141
142        let hasher = std::fs::read_to_string(manifest_dir.join("src/index/hasher.rs"))
143            .expect("read index/hasher.rs");
144        assert!(hasher.contains("gobby_core::indexing::file_content_hash"));
145        let local_buffer = format!("let mut buf = [0u8; {}]", 64 * 1024);
146        assert!(!hasher.contains(&local_buffer));
147
148        let rrf =
149            std::fs::read_to_string(manifest_dir.join("src/search/rrf.rs")).expect("read rrf.rs");
150        assert!(rrf.contains("gobby_core::search::rrf_merge"));
151        let local_rrf_const = ["const ", "RRF_K"].concat();
152        assert!(!rrf.contains(&local_rrf_const));
153
154        let chunker = std::fs::read_to_string(manifest_dir.join("src/index/chunker.rs"))
155            .expect("read index/chunker.rs");
156        assert!(!chunker.contains("use gobby_core::indexing::Chunk"));
157        assert!(!chunker.contains("use gobby_core::indexing::ChunkIdentity"));
158        assert!(!chunker.contains("use gobby_core::indexing::IndexEvent"));
159        assert!(!chunker.contains("use gobby_core::indexing::index_events_from_hashes"));
160    }
161
162    #[test]
163    fn falkor_facade_exception_scoped_to_falkor_rs() {
164        let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
165        let src_dir = manifest_dir.join("src");
166        let mut offenders = Vec::new();
167
168        fn visit(path: &std::path::Path, offenders: &mut Vec<std::path::PathBuf>) {
169            for entry in std::fs::read_dir(path).expect("read source directory") {
170                let entry = entry.expect("source entry");
171                let path = entry.path();
172                if path.is_dir() {
173                    visit(&path, offenders);
174                    continue;
175                }
176                if path.extension().and_then(|ext| ext.to_str()) != Some("rs") {
177                    continue;
178                }
179                let source = std::fs::read_to_string(&path).expect("read source file");
180                let builder = ["Falkor", "Client", "Builder"].concat();
181                if source.contains(&builder) && !path.ends_with("falkor.rs") {
182                    offenders.push(path);
183                }
184            }
185        }
186
187        visit(&src_dir, &mut offenders);
188        assert!(
189            offenders.is_empty(),
190            "Falkor client builder must remain scoped to falkor.rs: {offenders:?}"
191        );
192    }
193}