style: cargo fmt --all (round 4 ingest log feature follow-up)

Phase C4 executor 의 마지막 `fix(test): clippy + fmt fixes` commit 이
test file 부분만 fmt 적용. workspace 전체 fmt 누락 발견 → cargo fmt --all
적용. 모든 import alphabetical reorder + line wrapping 정합.

추가 untracked artifact 동시 commit:
- docs/superpowers/specs/2026-05-28-v0.20-ingest-log-spec.md (491 line, ACCEPT)
- docs/superpowers/plans/2026-05-28-v0.20-ingest-log-plan.md (616 line, ACCEPT)

workspace test: 1370 passed / 0 failed / 50 ignored, ingest_log_smoke green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 04:18:40 +00:00
parent 445b096215
commit 685007789a
235 changed files with 6520 additions and 3955 deletions

View File

@@ -23,7 +23,11 @@ async fn schema_tool_emits_error_v1_when_db_missing() {
handler.state(),
kebab_mcp::tools::schema::SchemaInput::default(),
);
assert_eq!(result.is_error, Some(true), "expected isError=true on missing DB");
assert_eq!(
result.is_error,
Some(true),
"expected isError=true on missing DB"
);
let content = result.content.first().unwrap();
let text = match &content.raw {
@@ -31,6 +35,9 @@ async fn schema_tool_emits_error_v1_when_db_missing() {
other => panic!("expected text content, got {other:?}"),
};
let v: serde_json::Value = serde_json::from_str(text).unwrap();
assert_eq!(v.get("schema_version").and_then(|s| s.as_str()), Some("error.v1"));
assert_eq!(
v.get("schema_version").and_then(|s| s.as_str()),
Some("error.v1")
);
assert_eq!(v.get("code").and_then(|s| s.as_str()), Some("not_indexed"));
}

View File

@@ -9,10 +9,7 @@ use rmcp::model::RawContent;
fn minimal_config(data_dir: &std::path::Path, workspace_root: &std::path::Path) -> Config {
let mut cfg = Config::defaults();
cfg.storage.data_dir = data_dir.to_string_lossy().into_owned();
cfg.storage.model_dir = data_dir
.join("models")
.to_string_lossy()
.into_owned();
cfg.storage.model_dir = data_dir.join("models").to_string_lossy().into_owned();
cfg.workspace.root = workspace_root.to_string_lossy().into_owned();
cfg.workspace.exclude.clear();
cfg.models.embedding.provider = "none".to_string();

View File

@@ -31,7 +31,11 @@ fn setup() -> (tempfile::TempDir, KebabHandler) {
"# Alpha\n\nThis document mentions kebab and bread.",
)
.unwrap();
let scope = SourceScope { root: workspace_root.clone(), include: vec![], exclude: vec![] };
let scope = SourceScope {
root: workspace_root.clone(),
include: vec![],
exclude: vec![],
};
let _ = kebab_app::ingest_with_config(config.clone(), scope, false).unwrap();
let state = KebabAppState::new(config, None);
let handler = KebabHandler::new(state);
@@ -39,7 +43,10 @@ fn setup() -> (tempfile::TempDir, KebabHandler) {
}
fn extract_json(result: &rmcp::model::CallToolResult) -> serde_json::Value {
assert!(!result.is_error.unwrap_or(false), "expected isError=false, got {result:?}");
assert!(
!result.is_error.unwrap_or(false),
"expected isError=false, got {result:?}"
);
let content = result.content.first().expect("at least one content item");
let text = match &content.raw {
RawContent::Text(t) => &t.text,
@@ -89,7 +96,7 @@ async fn bulk_search_invalid_item_field_continues_with_per_item_error() {
let input = kebab_mcp::tools::bulk_search::BulkSearchInput {
queries: vec![
json!({"query": "kebab", "mode": "lexical"}),
json!({"query": "bread", "mode": "bogus"}), // invalid mode
json!({"query": "bread", "mode": "bogus"}), // invalid mode
],
};
let result = kebab_mcp::tools::bulk_search::handle(handler.state(), input);
@@ -117,5 +124,8 @@ async fn bulk_search_over_cap_returns_tool_error() {
RawContent::Text(t) => &t.text,
other => panic!("expected Text content, got {other:?}"),
};
assert!(text.contains("max 100"), "expected 'max 100' in error: {text}");
assert!(
text.contains("max 100"),
"expected 'max 100' in error: {text}"
);
}

View File

@@ -15,10 +15,7 @@ use rmcp::model::RawContent;
fn minimal_config(data_dir: &std::path::Path, workspace_root: &std::path::Path) -> Config {
let mut cfg = Config::defaults();
cfg.storage.data_dir = data_dir.to_string_lossy().into_owned();
cfg.storage.model_dir = data_dir
.join("models")
.to_string_lossy()
.into_owned();
cfg.storage.model_dir = data_dir.join("models").to_string_lossy().into_owned();
cfg.workspace.root = workspace_root.to_string_lossy().into_owned();
cfg.workspace.exclude.clear();
cfg.models.embedding.provider = "none".to_string();

View File

@@ -112,6 +112,14 @@ async fn ingest_file_tool_idempotent_on_second_call() {
other => panic!("expected text, got {other:?}"),
};
let v2: serde_json::Value = serde_json::from_str(text2).unwrap();
assert_eq!(v2.get("new").and_then(serde_json::Value::as_u64), Some(0), "{v2:?}");
assert_eq!(v2.get("unchanged").and_then(serde_json::Value::as_u64), Some(1), "{v2:?}");
assert_eq!(
v2.get("new").and_then(serde_json::Value::as_u64),
Some(0),
"{v2:?}"
);
assert_eq!(
v2.get("unchanged").and_then(serde_json::Value::as_u64),
Some(1),
"{v2:?}"
);
}

View File

@@ -10,10 +10,7 @@ use rmcp::model::RawContent;
fn minimal_config(data_dir: &std::path::Path, workspace_root: &std::path::Path) -> Config {
let mut cfg = Config::defaults();
cfg.storage.data_dir = data_dir.to_string_lossy().into_owned();
cfg.storage.model_dir = data_dir
.join("models")
.to_string_lossy()
.into_owned();
cfg.storage.model_dir = data_dir.join("models").to_string_lossy().into_owned();
cfg.workspace.root = workspace_root.to_string_lossy().into_owned();
cfg.workspace.exclude.clear();
cfg.models.embedding.provider = "none".to_string();
@@ -52,7 +49,10 @@ async fn schema_tool_returns_schema_v1_json() {
"expected isError=false on healthy schema, got {result:?}"
);
let content = result.content.first().expect("expected at least one content item");
let content = result
.content
.first()
.expect("expected at least one content item");
// Content = Annotated<RawContent>; deref to get the inner RawContent.
let text = match &content.raw {
@@ -67,7 +67,9 @@ async fn schema_tool_returns_schema_v1_json() {
"unexpected schema_version in: {v}"
);
assert_eq!(
v.get("capabilities").and_then(|c| c.get("mcp_server")).and_then(serde_json::Value::as_bool),
v.get("capabilities")
.and_then(|c| c.get("mcp_server"))
.and_then(serde_json::Value::as_bool),
Some(true),
"mcp_server capability flag should be true after fb-30",
);

View File

@@ -10,10 +10,7 @@ use rmcp::model::RawContent;
fn minimal_config(data_dir: &std::path::Path, workspace_root: &std::path::Path) -> Config {
let mut cfg = Config::defaults();
cfg.storage.data_dir = data_dir.to_string_lossy().into_owned();
cfg.storage.model_dir = data_dir
.join("models")
.to_string_lossy()
.into_owned();
cfg.storage.model_dir = data_dir.join("models").to_string_lossy().into_owned();
cfg.workspace.root = workspace_root.to_string_lossy().into_owned();
cfg.workspace.exclude.clear();
cfg.models.embedding.provider = "none".to_string();
@@ -99,15 +96,15 @@ async fn search_tool_returns_search_response_v1() {
"expected at least one hit for 'kebab' in 'a.md'"
);
assert_eq!(
hits[0]
.get("schema_version")
.and_then(|s| s.as_str()),
hits[0].get("schema_version").and_then(|s| s.as_str()),
Some("search_hit.v1"),
"first hit should carry schema_version=search_hit.v1"
);
// truncated must be present (bool); next_cursor may be null on last page.
assert!(
v.get("truncated").and_then(serde_json::Value::as_bool).is_some(),
v.get("truncated")
.and_then(serde_json::Value::as_bool)
.is_some(),
"envelope should carry truncated:bool"
);
assert!(

View File

@@ -79,7 +79,10 @@ async fn search_with_trace_true_returns_trace_field() {
let result = kebab_mcp::tools::search::handle(handler.state(), make_input(Some(true)));
let v = extract_json(&result);
assert_eq!(v["schema_version"], "search_response.v1");
assert!(v["trace"].is_object(), "trace field present when trace:true");
assert!(
v["trace"].is_object(),
"trace field present when trace:true"
);
assert!(v["trace"]["timing"]["total_ms"].is_number());
assert!(v["trace"]["lexical"].is_array());
assert!(v["trace"]["vector"].is_array());

View File

@@ -7,7 +7,12 @@ use kebab_mcp::build_tools_vec;
#[test]
fn tools_list_returns_eight_tools() {
let tools = build_tools_vec();
assert_eq!(tools.len(), 8, "expected exactly 8 tools, got {}", tools.len());
assert_eq!(
tools.len(),
8,
"expected exactly 8 tools, got {}",
tools.len()
);
let names: Vec<&str> = tools.iter().map(|t| t.name.as_ref()).collect();
assert!(names.contains(&"schema"), "missing 'schema' tool");
@@ -15,7 +20,10 @@ fn tools_list_returns_eight_tools() {
assert!(names.contains(&"search"), "missing 'search' tool");
assert!(names.contains(&"ask"), "missing 'ask' tool");
assert!(names.contains(&"ingest_file"), "missing 'ingest_file' tool");
assert!(names.contains(&"ingest_stdin"), "missing 'ingest_stdin' tool");
assert!(
names.contains(&"ingest_stdin"),
"missing 'ingest_stdin' tool"
);
assert!(names.contains(&"fetch"), "missing 'fetch' tool");
assert!(names.contains(&"bulk_search"), "missing 'bulk_search' tool");
}