Skip to content

Commit 0375ca3

Browse files
committed
fix: skip Sentry.addBreadcrumb when breadcrumb data is empty (MD-05)
1 parent 1cd809f commit 0375ca3

4 files changed

Lines changed: 33 additions & 42 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@power-rent/try-catch': patch
3+
---
4+
5+
Fix MD-05: `.breadcrumbs(...)` no longer invokes the reporter's `addBreadcrumbs` (`Sentry.addBreadcrumb`) when the extracted data is empty (e.g. `.breadcrumbs([])`, transformer errors, or extraction against a primitive). Empty breadcrumb events are now suppressed rather than recorded as no-op entries.

src/__tests__/Try.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ describe('Try', () => {
406406
.unwrap();
407407

408408
await expect(exec).rejects.toThrow('validation error');
409+
expect(Sentry.captureException).not.toHaveBeenCalled();
409410
});
410411

411412
it('should not give typescript error', async () => {
@@ -1242,6 +1243,7 @@ describe('Try', () => {
12421243
.report('failed')
12431244
.unwrap();
12441245
}).toThrow('validation error');
1246+
expect(Sentry.captureException).not.toHaveBeenCalled();
12451247
});
12461248

12471249
it('should not give typescript error', () => {
@@ -1891,7 +1893,7 @@ describe('Try', () => {
18911893

18921894
it('async unwrap: breadcrumbs called once when .breadcrumbs() configured', async () => {
18931895
await expect(
1894-
new Try(async () => { throw new Error('boom'); })
1896+
new Try(async (_p: { x: number }) => { throw new Error('boom'); }, { x: 1 })
18951897
.breadcrumbs(['x'])
18961898
.unwrap()
18971899
).rejects.toThrow('boom');
@@ -1900,31 +1902,31 @@ describe('Try', () => {
19001902

19011903
it('sync unwrap: breadcrumbs called once when .breadcrumbs() configured', () => {
19021904
expect(() =>
1903-
new Try(() => { throw new Error('boom'); })
1905+
new Try((_p: { x: number }) => { throw new Error('boom'); }, { x: 1 })
19041906
.breadcrumbs(['x'])
19051907
.unwrap()
19061908
).toThrow('boom');
19071909
expect(mockAddBreadcrumbs).toHaveBeenCalledTimes(1);
19081910
});
19091911

19101912
it('async error(): breadcrumbs called once; returns normalized Error', async () => {
1911-
const err = await new Try(async () => { throw new Error('boom'); })
1913+
const err = await new Try(async (_p: { x: number }) => { throw new Error('boom'); }, { x: 1 })
19121914
.breadcrumbs(['x'])
19131915
.error();
19141916
expect(mockAddBreadcrumbs).toHaveBeenCalledTimes(1);
19151917
expect(err).toBeInstanceOf(Error);
19161918
});
19171919

19181920
it('sync error(): breadcrumbs called once; returns normalized Error', () => {
1919-
const err = new Try(() => { throw new Error('boom'); })
1921+
const err = new Try((_p: { x: number }) => { throw new Error('boom'); }, { x: 1 })
19201922
.breadcrumbs(['x'])
19211923
.error();
19221924
expect(mockAddBreadcrumbs).toHaveBeenCalledTimes(1);
19231925
expect(err).toBeInstanceOf(Error);
19241926
});
19251927

19261928
it('result(): breadcrumbs called once on failure', async () => {
1927-
const r = await new Try(async () => { throw new Error('boom'); })
1929+
const r = await new Try(async (_p: { x: number }) => { throw new Error('boom'); }, { x: 1 })
19281930
.breadcrumbs(['x'])
19291931
.result();
19301932
expect(r.success).toBe(false);
@@ -1933,7 +1935,7 @@ describe('Try', () => {
19331935

19341936
it('.report().breadcrumbs(): addBreadcrumbs called exactly once total (no double-add)', async () => {
19351937
await expect(
1936-
new Try(async () => { throw new Error('boom'); })
1938+
new Try(async (_p: { x: number }) => { throw new Error('boom'); }, { x: 1 })
19371939
.report('msg')
19381940
.breadcrumbs(['x'])
19391941
.unwrap()

src/__tests__/flexible-breadcrumbs.test.ts

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,8 @@ describe('Flexible Breadcrumbs System', () => {
4545

4646
await new Try(throwingFunction, params).breadcrumbs([]).value();
4747

48-
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith(
49-
expect.objectContaining({
50-
message: 'Calling throwingFunction function',
51-
data: {},
52-
}),
53-
);
48+
// Empty extracted data should short-circuit the reporter call.
49+
expect(Sentry.addBreadcrumb).not.toHaveBeenCalled();
5450
});
5551
});
5652

@@ -332,12 +328,8 @@ describe('Flexible Breadcrumbs System', () => {
332328
'Error in breadcrumb transformer:',
333329
expect.any(Error),
334330
);
335-
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith(
336-
expect.objectContaining({
337-
message: 'Calling testFunction function',
338-
data: {}, // Empty data due to transformer error
339-
}),
340-
);
331+
// Transformer error yields empty data — reporter call is short-circuited.
332+
expect(Sentry.addBreadcrumb).not.toHaveBeenCalled();
341333

342334
consoleSpy.mockRestore();
343335
});
@@ -363,12 +355,8 @@ describe('Flexible Breadcrumbs System', () => {
363355
.value();
364356

365357
expect(consoleSpy).not.toHaveBeenCalled();
366-
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith(
367-
expect.objectContaining({
368-
message: 'Calling testFunction function',
369-
data: {}, // Empty data due to transformer error
370-
}),
371-
);
358+
// Transformer error yields empty data — reporter call is short-circuited.
359+
expect(Sentry.addBreadcrumb).not.toHaveBeenCalled();
372360

373361
consoleSpy.mockRestore();
374362
});
@@ -409,12 +397,8 @@ describe('Flexible Breadcrumbs System', () => {
409397
.breadcrumbs([{ param: 0, keys: ['nonExistentKey'] } as any])
410398
.value();
411399

412-
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith(
413-
expect.objectContaining({
414-
message: 'Calling testFunction function',
415-
data: {}, // Empty because param 0 is not an object
416-
}),
417-
);
400+
// No keys extracted from a primitive — reporter call is short-circuited.
401+
expect(Sentry.addBreadcrumb).not.toHaveBeenCalled();
418402
});
419403
});
420404

@@ -451,12 +435,8 @@ describe('Flexible Breadcrumbs System', () => {
451435

452436
await new Try(testFunction, 'test').debug(false).breadcrumbs([]).value();
453437

454-
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith(
455-
expect.objectContaining({
456-
message: 'Calling testFunction function',
457-
data: {},
458-
}),
459-
);
438+
// Empty config yields empty data — reporter call is short-circuited.
439+
expect(Sentry.addBreadcrumb).not.toHaveBeenCalled();
460440
});
461441

462442
it('should handle mixed valid and invalid extractors', async () => {
@@ -544,12 +524,8 @@ describe('Flexible Breadcrumbs System', () => {
544524

545525
await new Try(noParams).debug(false).breadcrumbs([]).value();
546526

547-
expect(Sentry.addBreadcrumb).toHaveBeenCalledWith(
548-
expect.objectContaining({
549-
message: 'Calling noParams function',
550-
data: {},
551-
}),
552-
);
527+
// No data extracted — reporter call is short-circuited.
528+
expect(Sentry.addBreadcrumb).not.toHaveBeenCalled();
553529
});
554530

555531
it('should handle anonymous functions properly', async () => {

src/core/Try.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,14 @@ export class Try<
849849
this.local.breadcrumbData = this.extractAllBreadcrumbData();
850850
}
851851

852+
// Skip the reporter call when no data was extracted. Do not set the
853+
// idempotence guard here — `breadcrumbConfig` is immutable on a single
854+
// instance, so "empty now" implies "empty on any retry" and there is
855+
// no double-recording risk from leaving the guard unset.
856+
if (Object.keys(this.local.breadcrumbData).length === 0) {
857+
return;
858+
}
859+
852860
const functionName = this.fn.name || 'anonymous';
853861

854862
Try.defaultReporter.addBreadcrumbs(this.local.breadcrumbData, functionName);

0 commit comments

Comments
 (0)