Skip to content

Commit d3377e1

Browse files
committed
Add unit tests and fix integration test
1 parent 73d5389 commit d3377e1

File tree

3 files changed

+221
-3
lines changed

3 files changed

+221
-3
lines changed

dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/pageload-headers/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ sentryTest(
2424
const url = await getLocalTestUrl({
2525
testDir: __dirname,
2626
responseHeaders: {
27-
'Server-Timing': `sentry-trace;desc=${META_TAG_TRACE_ID}-${META_TAG_PARENT_SPAN_ID}, baggage;desc="${META_TAG_BAGGAGE}"`,
27+
'Server-Timing': `sentry-trace;desc=${META_TAG_TRACE_ID}-${META_TAG_PARENT_SPAN_ID}-1, baggage;desc="${META_TAG_BAGGAGE}"`,
2828
},
2929
});
3030

packages/browser/test/tracing/browserTracingIntegration.test.ts

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { BrowserClient } from '../../src/client';
2525
import { WINDOW } from '../../src/helpers';
2626
import {
2727
browserTracingIntegration,
28+
getServerTiming,
2829
startBrowserTracingNavigationSpan,
2930
startBrowserTracingPageLoadSpan,
3031
} from '../../src/tracing/browserTracingIntegration';
@@ -1029,6 +1030,224 @@ describe('browserTracingIntegration', () => {
10291030
});
10301031
});
10311032

1033+
describe('getServerTiming', () => {
1034+
it('retrieves server timing description when available', () => {
1035+
// Mock the performance API
1036+
const mockServerTiming = [
1037+
{ name: 'sentry-trace', duration: 0, description: '12312012123120121231201212312012-1121201211212012-1' },
1038+
{ name: 'baggage', duration: 0, description: 'sentry-release=2.1.14,sentry-sample_rand=0.456' },
1039+
];
1040+
1041+
const mockNavigationEntry = {
1042+
serverTiming: mockServerTiming,
1043+
};
1044+
1045+
vi.spyOn(WINDOW.performance, 'getEntriesByType').mockReturnValue([
1046+
mockNavigationEntry as any,
1047+
]);
1048+
1049+
const sentryTrace = getServerTiming('sentry-trace');
1050+
const baggage = getServerTiming('baggage');
1051+
1052+
expect(sentryTrace).toBe('12312012123120121231201212312012-1121201211212012-1');
1053+
expect(baggage).toBe('sentry-release=2.1.14,sentry-sample_rand=0.456');
1054+
});
1055+
1056+
it('returns undefined when server timing entry is not found', () => {
1057+
const mockServerTiming = [{ name: 'other-timing', duration: 0, description: 'some-value' }];
1058+
1059+
const mockNavigationEntry = {
1060+
serverTiming: mockServerTiming,
1061+
};
1062+
1063+
vi.spyOn(WINDOW.performance, 'getEntriesByType').mockReturnValue([
1064+
mockNavigationEntry as any,
1065+
]);
1066+
1067+
const result = getServerTiming('sentry-trace');
1068+
1069+
expect(result).toBeUndefined();
1070+
});
1071+
1072+
it('returns undefined when performance API is not available', () => {
1073+
const originalPerformance = WINDOW.performance;
1074+
// @ts-expect-error - intentionally setting to undefined
1075+
WINDOW.performance = undefined;
1076+
1077+
const result = getServerTiming('sentry-trace');
1078+
1079+
expect(result).toBeUndefined();
1080+
1081+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
1082+
// @ts-ignore its read only
1083+
WINDOW.performance = originalPerformance;
1084+
});
1085+
1086+
it('returns undefined when serverTiming is not available', () => {
1087+
const mockNavigationEntry = {
1088+
serverTiming: undefined,
1089+
};
1090+
1091+
vi.spyOn(WINDOW.performance, 'getEntriesByType').mockReturnValue([
1092+
mockNavigationEntry as any,
1093+
]);
1094+
1095+
const result = getServerTiming('sentry-trace');
1096+
1097+
expect(result).toBeUndefined();
1098+
});
1099+
});
1100+
1101+
describe('using Server-Timing headers', () => {
1102+
it('uses Server-Timing headers for pageload span', () => {
1103+
// Mock the performance API with Server-Timing data
1104+
const mockServerTiming = [
1105+
{ name: 'sentry-trace', duration: 0, description: '12312012123120121231201212312012-1121201211212012-0' },
1106+
{ name: 'baggage', duration: 0, description: 'sentry-release=2.1.14,foo=bar,sentry-sample_rand=0.123' },
1107+
];
1108+
1109+
const mockNavigationEntry = {
1110+
serverTiming: mockServerTiming,
1111+
};
1112+
1113+
vi.spyOn(WINDOW.performance, 'getEntriesByType').mockReturnValue([
1114+
mockNavigationEntry as any,
1115+
]);
1116+
1117+
const client = new BrowserClient(
1118+
getDefaultBrowserClientOptions({
1119+
tracesSampleRate: 1,
1120+
integrations: [browserTracingIntegration()],
1121+
}),
1122+
);
1123+
setCurrentClient(client);
1124+
1125+
// pageload transactions are created as part of the browserTracingIntegration's initialization
1126+
client.init();
1127+
1128+
const idleSpan = getActiveSpan()!;
1129+
expect(idleSpan).toBeDefined();
1130+
1131+
const dynamicSamplingContext = getDynamicSamplingContextFromSpan(idleSpan);
1132+
const propagationContext = getCurrentScope().getPropagationContext();
1133+
1134+
// Span is correct
1135+
expect(spanToJSON(idleSpan).op).toBe('pageload');
1136+
expect(spanToJSON(idleSpan).trace_id).toEqual('12312012123120121231201212312012');
1137+
expect(spanToJSON(idleSpan).parent_span_id).toEqual('1121201211212012');
1138+
expect(spanIsSampled(idleSpan)).toBe(false);
1139+
1140+
expect(dynamicSamplingContext).toBeDefined();
1141+
expect(dynamicSamplingContext).toStrictEqual({ release: '2.1.14', sample_rand: '0.123' });
1142+
1143+
// Propagation context keeps the Server-Timing trace data for later events on the same route to add them to the trace
1144+
expect(propagationContext.traceId).toEqual('12312012123120121231201212312012');
1145+
expect(propagationContext.parentSpanId).toEqual('1121201211212012');
1146+
expect(propagationContext.sampleRand).toBe(0.123);
1147+
});
1148+
1149+
it('meta tags take precedence over Server-Timing headers', () => {
1150+
// Set up both meta tags and Server-Timing headers
1151+
document.head.innerHTML =
1152+
'<meta name="sentry-trace" content="11111111111111111111111111111111-2222222222222222-1">' +
1153+
'<meta name="baggage" content="sentry-release=3.0.0,sentry-sample_rand=0.999">';
1154+
1155+
const mockServerTiming = [
1156+
{ name: 'sentry-trace', duration: 0, description: '12312012123120121231201212312012-1121201211212012-0' },
1157+
{ name: 'baggage', duration: 0, description: 'sentry-release=2.1.14,sentry-sample_rand=0.123' },
1158+
];
1159+
1160+
const mockNavigationEntry = {
1161+
serverTiming: mockServerTiming,
1162+
};
1163+
1164+
vi.spyOn(WINDOW.performance, 'getEntriesByType').mockReturnValue([
1165+
mockNavigationEntry as any,
1166+
]);
1167+
1168+
const client = new BrowserClient(
1169+
getDefaultBrowserClientOptions({
1170+
tracesSampleRate: 1,
1171+
integrations: [browserTracingIntegration()],
1172+
}),
1173+
);
1174+
setCurrentClient(client);
1175+
1176+
client.init();
1177+
1178+
const idleSpan = getActiveSpan()!;
1179+
expect(idleSpan).toBeDefined();
1180+
1181+
const dynamicSamplingContext = getDynamicSamplingContextFromSpan(idleSpan);
1182+
const propagationContext = getCurrentScope().getPropagationContext();
1183+
1184+
// Span should use meta tag data, not Server-Timing data
1185+
expect(spanToJSON(idleSpan).trace_id).toEqual('11111111111111111111111111111111');
1186+
expect(spanToJSON(idleSpan).parent_span_id).toEqual('2222222222222222');
1187+
expect(spanIsSampled(idleSpan)).toBe(true);
1188+
1189+
expect(dynamicSamplingContext).toStrictEqual({ release: '3.0.0', sample_rand: '0.999' });
1190+
1191+
expect(propagationContext.traceId).toEqual('11111111111111111111111111111111');
1192+
expect(propagationContext.parentSpanId).toEqual('2222222222222222');
1193+
expect(propagationContext.sampleRand).toBe(0.999);
1194+
});
1195+
1196+
it('uses passed in tracing data over Server-Timing headers', () => {
1197+
const mockServerTiming = [
1198+
{ name: 'sentry-trace', duration: 0, description: '12312012123120121231201212312012-1121201211212012-0' },
1199+
{ name: 'baggage', duration: 0, description: 'sentry-release=2.1.14,sentry-sample_rand=0.123' },
1200+
];
1201+
1202+
const mockNavigationEntry = {
1203+
serverTiming: mockServerTiming,
1204+
};
1205+
1206+
vi.spyOn(WINDOW.performance, 'getEntriesByType').mockReturnValue([
1207+
mockNavigationEntry as any,
1208+
]);
1209+
1210+
const client = new BrowserClient(
1211+
getDefaultBrowserClientOptions({
1212+
tracesSampleRate: 1,
1213+
integrations: [browserTracingIntegration({ instrumentPageLoad: false })],
1214+
}),
1215+
);
1216+
setCurrentClient(client);
1217+
1218+
client.init();
1219+
1220+
// manually create a pageload span with tracing data
1221+
startBrowserTracingPageLoadSpan(
1222+
client,
1223+
{
1224+
name: 'test span',
1225+
},
1226+
{
1227+
sentryTrace: '99999999999999999999999999999999-8888888888888888-1',
1228+
baggage: 'sentry-release=4.0.0,sentry-sample_rand=0.777',
1229+
},
1230+
);
1231+
1232+
const idleSpan = getActiveSpan()!;
1233+
expect(idleSpan).toBeDefined();
1234+
1235+
const dynamicSamplingContext = getDynamicSamplingContextFromSpan(idleSpan);
1236+
const propagationContext = getCurrentScope().getPropagationContext();
1237+
1238+
// Span should use passed-in data, not Server-Timing data
1239+
expect(spanToJSON(idleSpan).trace_id).toEqual('99999999999999999999999999999999');
1240+
expect(spanToJSON(idleSpan).parent_span_id).toEqual('8888888888888888');
1241+
expect(spanIsSampled(idleSpan)).toBe(true);
1242+
1243+
expect(dynamicSamplingContext).toStrictEqual({ release: '4.0.0', sample_rand: '0.777' });
1244+
1245+
expect(propagationContext.traceId).toEqual('99999999999999999999999999999999');
1246+
expect(propagationContext.parentSpanId).toEqual('8888888888888888');
1247+
expect(propagationContext.sampleRand).toBe(0.777);
1248+
});
1249+
});
1250+
10321251
describe('idleTimeout', () => {
10331252
it('is created by default', () => {
10341253
vi.useFakeTimers();

packages/core/src/tracing/dynamicSamplingContext.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ export function getDynamicSamplingContextFromSpan(span: Span): Readonly<Partial<
9393
rootSpanAttributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE];
9494

9595
function applyLocalSampleRateToDsc(dsc: Partial<DynamicSamplingContext>): Partial<DynamicSamplingContext> {
96-
// Only apply local sample rate if the DSC doesn't already have one
97-
if (!dsc.sample_rate && (typeof rootSpanSampleRate === 'number' || typeof rootSpanSampleRate === 'string')) {
96+
if (typeof rootSpanSampleRate === 'number' || typeof rootSpanSampleRate === 'string') {
9897
dsc.sample_rate = `${rootSpanSampleRate}`;
9998
}
10099
return dsc;

0 commit comments

Comments
 (0)