프로젝트 이름 `kb` → `kebab` rename 의 첫 단계. - workspace `Cargo.toml`: members `crates/kb-*` → `crates/kebab-*`, repository URL `altair823/kb` → `altair823/kebab`. - 18 crate 폴더 rename via `git mv` (history 보존). - 각 crate `Cargo.toml`: `name = "kb-*"` → `"kebab-*"`, path deps `../kb-*` → `../kebab-*`. - 모든 `.rs`: `kb_<id>` snake-case 모듈 path 18 개 (`kb_core`, `kb_config`, `kb_app`, `kb_cli`, `kb_eval`, `kb_search`, `kb_chunk`, `kb_normalize`, `kb_source_fs`, `kb_parse_md`, `kb_parse_types`, `kb_store_sqlite`, `kb_store_vector`, `kb_embed`, `kb_embed_local`, `kb_llm`, `kb_llm_local`, `kb_rag`) → `kebab_<id>` 일괄 sed (단어 경계 \\b 사용해 영어 문장 안의 "kb" 약어 미오염). CLI binary 이름 (`[[bin]] name = "kb"`), 환경변수 `KB_*`, XDG paths, tracing target, 그리고 docs sweep 은 다음 commit 에서. ## 검증 - `cargo check --workspace` clean — 모든 crate 빌드 통과 후 commit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
84 lines
2.4 KiB
Rust
84 lines
2.4 KiB
Rust
//! Migration test: a fresh DB, after `run_migrations`, exposes every
|
||
//! table and index P1 needs (per §5.1–§5.7).
|
||
|
||
use kebab_store_sqlite::SqliteStore;
|
||
|
||
mod common;
|
||
|
||
#[test]
|
||
fn fresh_db_has_all_p1_tables_and_indexes() {
|
||
let env = common::TestEnv::new();
|
||
let store = SqliteStore::open(&env.config()).expect("open");
|
||
store.run_migrations().expect("run migrations");
|
||
|
||
// Pull the list of user tables from sqlite_master.
|
||
let tables: Vec<String> = env.with_conn(|c| {
|
||
let mut stmt = c.prepare(
|
||
"SELECT name FROM sqlite_master
|
||
WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
|
||
ORDER BY name",
|
||
)?;
|
||
let rows = stmt.query_map([], |r| r.get::<_, String>(0))?;
|
||
let tables: rusqlite::Result<Vec<String>> = rows.collect();
|
||
tables
|
||
});
|
||
|
||
let required = [
|
||
"answers",
|
||
"assets",
|
||
"blocks",
|
||
"chunks",
|
||
"document_tags",
|
||
"documents",
|
||
"embedding_records",
|
||
"eval_query_results",
|
||
"eval_runs",
|
||
"ingest_runs",
|
||
"jobs",
|
||
// refinery's own bookkeeping table (`refinery_schema_history`)
|
||
// also lands here; we don't pin it but it's expected.
|
||
"migrations",
|
||
"schema_meta",
|
||
];
|
||
for t in required {
|
||
assert!(
|
||
tables.iter().any(|n| n == t),
|
||
"table `{t}` missing; got {tables:?}"
|
||
);
|
||
}
|
||
|
||
// Pin the documented indexes (subset that matters for hot paths).
|
||
let indexes: Vec<String> = env.with_conn(|c| {
|
||
let mut stmt = c.prepare(
|
||
"SELECT name FROM sqlite_master
|
||
WHERE type = 'index' AND name NOT LIKE 'sqlite_%'
|
||
ORDER BY name",
|
||
)?;
|
||
let rows = stmt.query_map([], |r| r.get::<_, String>(0))?;
|
||
let idx: rusqlite::Result<Vec<String>> = rows.collect();
|
||
idx
|
||
});
|
||
for i in [
|
||
"idx_assets_workspace_path",
|
||
"idx_assets_media_type",
|
||
"idx_docs_workspace_path",
|
||
"idx_docs_lang",
|
||
"idx_docs_source_type",
|
||
"idx_document_tags_tag",
|
||
"idx_blocks_doc_id",
|
||
"idx_chunks_doc_id",
|
||
"idx_chunks_chunker_version",
|
||
"idx_embed_chunk",
|
||
"idx_embed_model",
|
||
"idx_jobs_status",
|
||
"idx_jobs_kind",
|
||
"idx_answers_created_at",
|
||
"idx_answers_grounded",
|
||
] {
|
||
assert!(
|
||
indexes.iter().any(|n| n == i),
|
||
"index `{i}` missing; got {indexes:?}"
|
||
);
|
||
}
|
||
}
|