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:
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user