diff --git a/TESTING_GUIDE.md b/TESTING_GUIDE.md index 41daa0271..0bafbfaaf 100644 --- a/TESTING_GUIDE.md +++ b/TESTING_GUIDE.md @@ -19,6 +19,7 @@ Use the full suite by default. Use targeted filters only while iterating locally ## Test Stack +- Issue #5128 compact graph zero-result coverage belongs in `JsonEnvelopeWrapperIssue4585Tests.cs`. Keep `callees` aligned with `references` and `callers` by asserting an empty compact location envelope and zero returned/total cardinality both with and without explicit `--fields`, while reusing the graph-ready bounded-response fixture. - Issue #5098 inspect projection coverage belongs in `QueryCommandRunnerInspectTests.cs`. Keep one graph-ready partial-type fixture covering canonical and alias selectors, multiple leaves across collections, parent-over-child precedence, stable row order, empty arrays, compact truncation, byte bounds, body paging/recovery, partial-family metadata, queryless catalog discovery, and typed unknown parent/leaf errors. `ConsoleUiTests.cs` separately pins the `` usage, nested-field help text, and examples. - Path-containment coverage for #5091 pairs platform-independent `PathCasingTests` seams for mixed parent-namespace case policies and directory identities with `FileIndexerTests` integration through an internal file symlink. Keep both checks when changing internal-symlink policy: a case-insensitive project mount must not make a distinct case-only sibling in its case-sensitive parent namespace appear internal, while genuine same-namespace aliases remain accepted. - `WorkspaceCheckTruncationIssue5055Tests` owns the bounded `workspace_check` list contract. Keep zero, below-limit, exact-limit, and over-limit coverage for all six categories, plus the 1260/20/1240 example and one shared filesystem fixture for raw JSON, list-only projection, compact output, byte-budget trimming, deterministic order, and human sample labeling. @@ -1113,6 +1114,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests" ## テストスタック +- Issue #5128 の compact graph 0件 coverage は `JsonEnvelopeWrapperIssue4585Tests.cs` が担当します。graph-ready な bounded-response fixture を再利用し、明示的な `--fields` の有無の両方で、`callees` が `references` / `callers` と同様に空の compact location envelope と返却件数・総件数0を維持することを検証してください。 - Issue #5098 の inspect projection coverage は `QueryCommandRunnerInspectTests.cs` が担当します。graph-ready な partial type fixture 1つで canonical / alias selector、collection をまたぐ複数 leaf、parent の child に対する優先、安定した row 順、空配列、compact truncation、byte 上限、body paging / recovery、partial-family metadata、query 不要の catalog discovery、未知 parent / leaf の型付き error を維持してください。`ConsoleUiTests.cs` では `` usage、nested-field help text、例を別途固定します。 - #5091 の path containment coverage は、親 directory namespace の case policy と directory identity が混在する状況を OS 非依存で再現する `PathCasingTests` の seam と、内部 file symlink を通す `FileIndexerTests` の integration を対にします。internal-symlink policy を変更するときは両方を維持し、case-insensitive な project mount の policy で、case-sensitive な親 namespace にある別 identity の case-only sibling を内部 path と誤認しないこと、および同一 namespace の正当な alias は引き続き受理されることを検証してください。 - `WorkspaceCheckTruncationIssue5055Tests` は上限付き `workspace_check` 一覧の契約を担当します。6カテゴリすべての0件・上限未満・上限一致・上限超過、1260/20/1240 の例、および raw JSON、一覧単独 projection、compact 出力、byte budget による短縮、決定的順序、human 向け sample 表示を1つの共有 filesystem fixture で検証する coverage を維持してください。 diff --git a/changelog.d/unreleased/5128.fixed.md b/changelog.d/unreleased/5128.fixed.md new file mode 100644 index 000000000..12a80b7ac --- /dev/null +++ b/changelog.d/unreleased/5128.fixed.md @@ -0,0 +1,16 @@ +--- +category: fixed +issues: + - 5128 +affected: + - src/CodeIndex/Cli/QueryCommandRunner.Graph.cs + - tests/CodeIndex.Tests/JsonEnvelopeWrapperIssue4585Tests.cs +--- + +## English + +- **Zero-match `callees` queries now preserve the empty compact envelope (#5128)** — compact JSON output reports `count: 0` with an empty `results` array, including bounded projections that use `--fields` or `--max-json-bytes`, instead of fabricating one empty row. + +## 日本語 + +- **0件の `callees` query が空の compact envelope を維持するようになりました (#5128)** — compact JSON 出力は、`--fields` や `--max-json-bytes` を使う bounded projection を含め、空の row を1件生成せず、空の `results` 配列と `count: 0` を返します。 diff --git a/src/CodeIndex/Cli/QueryCommandRunner.Graph.cs b/src/CodeIndex/Cli/QueryCommandRunner.Graph.cs index 5b46b0147..290ab9318 100644 --- a/src/CodeIndex/Cli/QueryCommandRunner.Graph.cs +++ b/src/CodeIndex/Cli/QueryCommandRunner.Graph.cs @@ -378,6 +378,8 @@ public static int RunCallees(string[] cmdArgs, JsonSerializerOptions jsonOptions WriteHdlGraphContractWarningIfNeeded(options.Json, hdlGraphSignal); if (results.Count == 0) { + if (options.Json && TryWriteEmptyFormattedResult(options, jsonOptions)) + return ZeroResultExitCode(options); if (options.Json) WriteGraphZeroJsonResult(reader, "callees", jsonOptions, graphAvailable: reader._hasReferencesTable, exact ? exactSignal : (ExactQuerySignal?)null, exactZeroHint, queryOptions: options, extraFields: payload => AddGraphContractJsonFields(payload, reader, jsonOptions, sqlGraphSignal, hdlGraphSignal)); else if (!options.Json) diff --git a/tests/CodeIndex.Tests/JsonEnvelopeWrapperIssue4585Tests.cs b/tests/CodeIndex.Tests/JsonEnvelopeWrapperIssue4585Tests.cs index c6832cd52..7c6fdc88f 100644 --- a/tests/CodeIndex.Tests/JsonEnvelopeWrapperIssue4585Tests.cs +++ b/tests/CodeIndex.Tests/JsonEnvelopeWrapperIssue4585Tests.cs @@ -75,6 +75,34 @@ public sealed class Consumer Assert.True(document.RootElement.GetProperty("results")[0].TryGetProperty("file", out _)); } } + + var zeroCalleeCases = new[] + { + new[] + { + "callees", "DefinitelyNoSuchSymbol", "--exact-name", "--json", "--compact", + "--limit", "1", "--max-json-bytes", "8192", "--db", dbPath, + }, + new[] + { + "callees", "DefinitelyNoSuchSymbol", "--exact-name", "--json", "--compact", + "--fields", "path,line", "--limit", "1", "--max-json-bytes", "8192", "--db", dbPath, + }, + }; + foreach (var args in zeroCalleeCases) + { + var (exitCode, stdout, stderr) = CaptureConsole(() => ProgramRunner.Run(args, _jsonOptions, "1.0.0-test")); + + Assert.Equal(CommandExitCodes.Success, exitCode); + Assert.Equal(string.Empty, stderr); + using var document = JsonDocument.Parse(stdout); + var root = document.RootElement; + Assert.Equal("compact", root.GetProperty("format").GetString()); + Assert.Equal(0, root.GetProperty("count").GetInt32()); + Assert.Empty(root.GetProperty("results").EnumerateArray()); + Assert.Equal(0, root.GetProperty("metadata").GetProperty("result_count").GetInt32()); + Assert.Equal(0, root.GetProperty("metadata").GetProperty("total_count").GetInt32()); + } } finally {