test(trace): guard empty trace lists against JSON null

Cover the service nil-slice contract and the client normalization of a null payload.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-21 02:38:08 +00:00
co-authored by Cursor
parent 26d8f4381d
commit 1f7f3d2271
2 changed files with 93 additions and 0 deletions
@@ -0,0 +1,38 @@
package traceimpact
import (
"context"
"encoding/json"
"strings"
"testing"
)
func TestListMethodsReturnEmptySlicesNotNil(t *testing.T) {
svc := NewService(nil)
ctx := context.Background()
items, err := svc.ListItems(ctx, 42)
if err != nil || items == nil {
t.Fatalf("ListItems items=%v err=%v", items, err)
}
index, err := svc.ListIndex(ctx, 42)
if err != nil || index == nil {
t.Fatalf("ListIndex index=%v err=%v", index, err)
}
marks, err := svc.ListOpenMarks(ctx, 42, "")
if err != nil || marks == nil {
t.Fatalf("ListOpenMarks marks=%v err=%v", marks, err)
}
raw, err := json.Marshal(map[string]any{
"items": items,
"index": index,
"recheck_marks": marks,
})
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(raw), "null") {
t.Fatalf("empty lists encoded as JSON null: %s", raw)
}
}
+55
View File
@@ -0,0 +1,55 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { fetchProjectTrace } from './projectTrace';
test('fetchProjectTrace turns null arrays into empty arrays', async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () =>
new Response(
JSON.stringify({
items: null,
hidden_count: 2,
index: null,
recheck_marks: null,
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
);
try {
const view = await fetchProjectTrace(7);
assert.deepEqual(view.items, []);
assert.equal(view.hidden_count, 2);
assert.deepEqual(view.index, []);
assert.deepEqual(view.recheck_marks, []);
} finally {
globalThis.fetch = originalFetch;
}
});
test('fetchProjectTrace keeps real arrays and rejects failed responses', async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () =>
new Response(
JSON.stringify({
items: [{ id: 1, business_key: 'UR-1', kind: 'requirement' }],
hidden_count: 0,
index: [{ doc_id: 'doc-1', status: 'ready', item_count: 1 }],
recheck_marks: [],
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
);
try {
const view = await fetchProjectTrace(7);
assert.equal(view.items.length, 1);
assert.equal(view.index[0]?.doc_id, 'doc-1');
assert.deepEqual(view.recheck_marks, []);
} finally {
globalThis.fetch = originalFetch;
}
globalThis.fetch = async () => new Response('nope', { status: 500 });
try {
await assert.rejects(() => fetchProjectTrace(7), /需求与影响加载失败: 500/);
} finally {
globalThis.fetch = originalFetch;
}
});