Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ program
program
.command("traces")
.description("List audit trail entries")
.option("-s, --server <id>", "Filter by server")
.option("-s, --server <id>", "Filter by server ID, partial ID, or slug")
.option("-a, --agent <id>", "Filter by agent")
.option("-l, --limit <n>", `Limit rows shown (default: ${DEFAULT_LIST_LIMIT}; JSON keeps legacy ${LEGACY_JSON_TRACE_LIMIT})`)
.option("--cursor <n>", "Zero-based row offset for pagination")
Expand All @@ -1012,7 +1012,11 @@ program
if (opts.agent) {
fetched = listTracesByAgent(opts.agent, cursor + limit + (json ? 0 : 1), db);
} else {
fetched = listTraces(opts.server, undefined, cursor + limit + (json ? 0 : 1), db);
// traces.server_id always holds the full UUID, so an unresolved slug or the
// 8-char ID this table renders would match nothing and print an empty result
// at rc=0 — indistinguishable from a server with no traces.
const serverId = opts.server ? resolveServerOrExit(opts.server, db).id : undefined;
fetched = listTraces(serverId, undefined, cursor + limit + (json ? 0 : 1), db);
}
const traces = fetched.slice(cursor, cursor + limit);
if (json) {
Expand Down
38 changes: 38 additions & 0 deletions test/cli-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,44 @@ describe("CLI integration tests", () => {
}
});

test("traces --server resolves full ID, short ID, slug, and name, and rejects unknown identifiers", async () => {
const { dbFlag, cleanup } = withTmpDb();
try {
await run(`${CLI} ${dbFlag} servers:add -n "Alpha Server" --slug alpha-srv`);
await run(`${CLI} ${dbFlag} servers:add -n "Beta Server" --slug beta-srv`);
await run(`${CLI} ${dbFlag} trace:add --server alpha-srv --event "alpha.event"`);
await run(`${CLI} ${dbFlag} trace:add --server beta-srv --event "beta.event"`);

const { stdout: allJson } = await run(`${CLI} ${dbFlag} traces --json`);
const all = JSON.parse(allJson) as { event: string; server_id: string }[];
expect(all.map(t => t.event).sort()).toEqual(["alpha.event", "beta.event"]);
const alphaId = all.find(t => t.event === "alpha.event")!.server_id;

const events = async (identifier: string) => {
const { stdout } = await run(`${CLI} ${dbFlag} traces --server ${identifier} --json`);
return (JSON.parse(stdout) as { event: string }[]).map(t => t.event);
};

// Positive control: the full UUID already resolved before this fix, so a
// failure here means the fixture or the harness is wrong, not the filter.
expect(await events(alphaId)).toEqual(["alpha.event"]);

// The short ID is the value the traces table itself renders in its SERVER column.
expect(await events(alphaId.slice(0, 8))).toEqual(["alpha.event"]);

expect(await events("alpha-srv")).toEqual(["alpha.event"]);
expect(await events('"Beta Server"')).toEqual(["beta.event"]);

// An unknown identifier must fail loudly. Printing an empty table at rc=0 is
// indistinguishable from a real server that genuinely has no traces.
const unknown = await runExpectFailure(`${CLI} ${dbFlag} traces --server zzz-not-a-server-zzz`);
expect(unknown.stderr).toContain("Server not found");
expect(unknown.code).not.toBe(0);
} finally {
cleanup();
}
});

test("list commands default to compact paginated output while JSON can stay complete", async () => {
const { dbFlag, cleanup } = withTmpDb();
try {
Expand Down
Loading