Skip to content

Commit 983ba64

Browse files
committed
fix(CLI-BH): seed dedup set from initial fetch, fix test reproducibility
- Add onInitialLogs callback to FollowConfig so trace follow mode seeds the seenWithoutTs set from the initial batch, preventing duplicates on the first poll cycle. - Replace Math.random() in property tests with fast-check combinators (oneof, constant, tuple) for deterministic reproducibility.
1 parent 757955e commit 983ba64

File tree

2 files changed

+35
-23
lines changed

2 files changed

+35
-23
lines changed

src/commands/log/list.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ type FollowConfig<T extends LogLike> = {
195195
fetch: (statsPeriod: string, afterTimestamp?: number) => Promise<T[]>;
196196
/** Extract only the genuinely new entries from a poll response */
197197
extractNew: (logs: T[], lastTimestamp: number) => T[];
198+
/**
199+
* Called with the initial batch of logs before polling begins.
200+
* Use this to seed dedup state (e.g., tracking seen log IDs).
201+
*/
202+
onInitialLogs?: (logs: T[]) => void;
198203
};
199204

200205
/**
@@ -315,6 +320,7 @@ function executeFollowMode<T extends LogLike>(
315320
if (initialLogs[0]) {
316321
lastTimestamp = initialLogs[0].timestamp_precise ?? lastTimestamp;
317322
}
323+
config.onInitialLogs?.(initialLogs);
318324
scheduleNextPoll();
319325
})
320326
.catch((error: unknown) => {
@@ -479,6 +485,13 @@ export const listCommand = buildListCommand("log", {
479485
seenWithoutTs.add(l.id);
480486
return true;
481487
}),
488+
onInitialLogs: (logs) => {
489+
for (const l of logs) {
490+
if (l.timestamp_precise === undefined) {
491+
seenWithoutTs.add(l.id);
492+
}
493+
}
494+
},
482495
});
483496
} else {
484497
await executeTraceSingleFetch(stdout, org, flags.trace, flags);

test/lib/trace-log-schema.property.test.ts

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88

99
import { describe, expect, test } from "bun:test";
1010
import {
11+
constant,
1112
assert as fcAssert,
1213
nat,
1314
oneof,
1415
option,
1516
property,
1617
string,
18+
tuple,
1719
} from "fast-check";
1820
import {
1921
DetailedSentryLogSchema,
@@ -33,33 +35,30 @@ function numberOrString(arb = nat()) {
3335
}
3436

3537
/**
36-
* Pick a random representation for a numeric field:
37-
* number, string, or undefined (omitted).
38+
* Arbitrary that produces a number as either the number itself, its
39+
* string representation, or undefined — simulating optional numeric
40+
* fields that the API may omit or serialize as strings.
3841
*/
39-
function randomNumericField(value: number): number | string | undefined {
40-
const r = Math.random();
41-
if (r < 0.3) return;
42-
if (r < 0.65) return value;
43-
return String(value);
42+
function optionalNumberOrString(arb = nat()) {
43+
return oneof(arb, arb.map(String), constant(undefined));
4444
}
4545

4646
/** Arbitrary for a valid trace-log entry with mixed number/string numeric fields */
47-
const traceLogEntryArb = nat().chain((projectId) =>
48-
nat().chain((sevNum) =>
49-
nat().chain((tsPrecise) =>
50-
option(string(), { nil: undefined }).map((msg) => ({
51-
id: "test-log-id",
52-
"project.id": Math.random() > 0.5 ? projectId : String(projectId),
53-
trace: "aaaa1111bbbb2222cccc3333dddd4444",
54-
severity_number: randomNumericField(sevNum),
55-
severity: "info",
56-
timestamp: "2025-01-30T14:32:15+00:00",
57-
timestamp_precise: randomNumericField(tsPrecise),
58-
message: msg ?? null,
59-
}))
60-
)
61-
)
62-
);
47+
const traceLogEntryArb = tuple(
48+
numberOrString(),
49+
optionalNumberOrString(),
50+
optionalNumberOrString(),
51+
option(string(), { nil: undefined })
52+
).map(([projectId, sevNum, tsPrecise, msg]) => ({
53+
id: "test-log-id",
54+
"project.id": projectId,
55+
trace: "aaaa1111bbbb2222cccc3333dddd4444",
56+
severity_number: sevNum,
57+
severity: "info",
58+
timestamp: "2025-01-30T14:32:15+00:00",
59+
timestamp_precise: tsPrecise,
60+
message: msg ?? null,
61+
}));
6362

6463
describe("property: TraceLogSchema coercion", () => {
6564
test("always parses successfully with number or string numeric fields", () => {

0 commit comments

Comments
 (0)