-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathcache.test.ts
More file actions
145 lines (121 loc) · 5.65 KB
/
cache.test.ts
File metadata and controls
145 lines (121 loc) · 5.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/nitro';
test.describe('Cache Instrumentation', () => {
const SEMANTIC_ATTRIBUTE_CACHE_KEY = 'cache.key';
const SEMANTIC_ATTRIBUTE_CACHE_HIT = 'cache.hit';
test('instruments cachedFunction and cachedHandler calls and creates spans with correct attributes', async ({
request,
}) => {
const transactionPromise = waitForTransaction('nitro-3', transactionEvent => {
return transactionEvent.transaction?.includes('GET /api/test-cache') ?? false;
});
const response = await request.get('/api/test-cache');
expect(response.status()).toBe(200);
const transaction = await transactionPromise;
const findSpansByOp = (op: string) => {
return transaction.spans?.filter(span => span.data?.[SEMANTIC_ATTRIBUTE_SENTRY_OP] === op) || [];
};
const allCacheSpans = transaction.spans?.filter(
span => span.data?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] === 'auto.cache.nitro',
);
expect(allCacheSpans?.length).toBeGreaterThan(0);
// getItem spans for cachedFunction - should have both cache miss and cache hit
const getItemSpans = findSpansByOp('cache.get_item');
expect(getItemSpans.length).toBeGreaterThan(0);
// Find cache miss (first call to getCachedUser('123'))
const cacheMissSpan = getItemSpans.find(
span =>
typeof span.data?.[SEMANTIC_ATTRIBUTE_CACHE_KEY] === 'string' &&
span.data[SEMANTIC_ATTRIBUTE_CACHE_KEY].includes('user:123') &&
!span.data?.[SEMANTIC_ATTRIBUTE_CACHE_HIT],
);
if (cacheMissSpan) {
expect(cacheMissSpan.data).toMatchObject({
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'cache.get_item',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.cache.nitro',
[SEMANTIC_ATTRIBUTE_CACHE_HIT]: false,
'db.operation.name': 'getItem',
});
}
// Find cache hit (second call to getCachedUser('123'))
const cacheHitSpan = getItemSpans.find(
span =>
typeof span.data?.[SEMANTIC_ATTRIBUTE_CACHE_KEY] === 'string' &&
span.data[SEMANTIC_ATTRIBUTE_CACHE_KEY].includes('user:123') &&
span.data?.[SEMANTIC_ATTRIBUTE_CACHE_HIT],
);
if (cacheHitSpan) {
expect(cacheHitSpan.data).toMatchObject({
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'cache.get_item',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.cache.nitro',
[SEMANTIC_ATTRIBUTE_CACHE_HIT]: true,
'db.operation.name': 'getItem',
});
}
// setItem spans for cachedFunction - when cache miss occurs, value is set
const setItemSpans = findSpansByOp('cache.set_item');
expect(setItemSpans.length).toBeGreaterThan(0);
const cacheSetSpan = setItemSpans.find(
span =>
typeof span.data?.[SEMANTIC_ATTRIBUTE_CACHE_KEY] === 'string' &&
span.data[SEMANTIC_ATTRIBUTE_CACHE_KEY].includes('user:123'),
);
if (cacheSetSpan) {
expect(cacheSetSpan.data).toMatchObject({
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'cache.set_item',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.cache.nitro',
'db.operation.name': 'setItem',
});
}
// Spans for different cached functions
const dataKeySpans = getItemSpans.filter(
span =>
typeof span.data?.[SEMANTIC_ATTRIBUTE_CACHE_KEY] === 'string' &&
span.data[SEMANTIC_ATTRIBUTE_CACHE_KEY].includes('data:test-key'),
);
expect(dataKeySpans.length).toBeGreaterThan(0);
// Spans for cachedHandler
const cachedHandlerSpans = getItemSpans.filter(
span =>
typeof span.data?.[SEMANTIC_ATTRIBUTE_CACHE_KEY] === 'string' &&
span.data[SEMANTIC_ATTRIBUTE_CACHE_KEY].includes('cachedHandler'),
);
expect(cachedHandlerSpans.length).toBeGreaterThan(0);
// Verify all cache spans have OK status
allCacheSpans?.forEach(span => {
expect(span.status).toBe('ok');
});
// Verify cache spans are properly nested under the transaction
allCacheSpans?.forEach(span => {
expect(span.parent_span_id).toBeDefined();
});
});
test('correctly tracks cache hits and misses for cachedFunction', async ({ request }) => {
const uniqueUser = `test-${Date.now()}`;
const uniqueData = `data-${Date.now()}`;
const transactionPromise = waitForTransaction('nitro-3', transactionEvent => {
return transactionEvent.transaction?.includes('GET /api/test-cache') ?? false;
});
await request.get(`/api/test-cache?user=${uniqueUser}&data=${uniqueData}`);
const transaction = await transactionPromise;
const allCacheSpans = transaction.spans?.filter(
span => span.data?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] === 'auto.cache.nitro',
);
expect(allCacheSpans?.length).toBeGreaterThan(0);
const allGetItemSpans = allCacheSpans?.filter(
span => span.data?.[SEMANTIC_ATTRIBUTE_SENTRY_OP] === 'cache.get_item',
);
const allSetItemSpans = allCacheSpans?.filter(
span => span.data?.[SEMANTIC_ATTRIBUTE_SENTRY_OP] === 'cache.set_item',
);
expect(allGetItemSpans?.length).toBeGreaterThan(0);
expect(allSetItemSpans?.length).toBeGreaterThan(0);
const cacheMissSpans = allGetItemSpans?.filter(span => span.data?.[SEMANTIC_ATTRIBUTE_CACHE_HIT] === false);
const cacheHitSpans = allGetItemSpans?.filter(span => span.data?.[SEMANTIC_ATTRIBUTE_CACHE_HIT] === true);
// At least one cache miss (first calls to getCachedUser and getCachedData)
expect(cacheMissSpans?.length).toBeGreaterThanOrEqual(1);
// At least one cache hit (second calls to getCachedUser and getCachedData)
expect(cacheHitSpans?.length).toBeGreaterThanOrEqual(1);
});
});