autotel 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1946 -0
  3. package/dist/chunk-2LNRY4QK.js +273 -0
  4. package/dist/chunk-2LNRY4QK.js.map +1 -0
  5. package/dist/chunk-3HENGDW2.js +587 -0
  6. package/dist/chunk-3HENGDW2.js.map +1 -0
  7. package/dist/chunk-4OAT42CA.cjs +73 -0
  8. package/dist/chunk-4OAT42CA.cjs.map +1 -0
  9. package/dist/chunk-5GWX5LFW.js +70 -0
  10. package/dist/chunk-5GWX5LFW.js.map +1 -0
  11. package/dist/chunk-5R2M36QB.js +195 -0
  12. package/dist/chunk-5R2M36QB.js.map +1 -0
  13. package/dist/chunk-5ZN622AO.js +73 -0
  14. package/dist/chunk-5ZN622AO.js.map +1 -0
  15. package/dist/chunk-77MSMAUQ.cjs +498 -0
  16. package/dist/chunk-77MSMAUQ.cjs.map +1 -0
  17. package/dist/chunk-ABPEQ6RK.cjs +596 -0
  18. package/dist/chunk-ABPEQ6RK.cjs.map +1 -0
  19. package/dist/chunk-BWYGJKRB.js +95 -0
  20. package/dist/chunk-BWYGJKRB.js.map +1 -0
  21. package/dist/chunk-BZHG5IZ4.js +73 -0
  22. package/dist/chunk-BZHG5IZ4.js.map +1 -0
  23. package/dist/chunk-G7VZBCD6.cjs +35 -0
  24. package/dist/chunk-G7VZBCD6.cjs.map +1 -0
  25. package/dist/chunk-GVLK7YUU.cjs +30 -0
  26. package/dist/chunk-GVLK7YUU.cjs.map +1 -0
  27. package/dist/chunk-HCCXC7XG.js +205 -0
  28. package/dist/chunk-HCCXC7XG.js.map +1 -0
  29. package/dist/chunk-HE6T6FIX.cjs +203 -0
  30. package/dist/chunk-HE6T6FIX.cjs.map +1 -0
  31. package/dist/chunk-KIXWPOCO.cjs +100 -0
  32. package/dist/chunk-KIXWPOCO.cjs.map +1 -0
  33. package/dist/chunk-KVGNW3FC.js +87 -0
  34. package/dist/chunk-KVGNW3FC.js.map +1 -0
  35. package/dist/chunk-LITNXTTT.js +3 -0
  36. package/dist/chunk-LITNXTTT.js.map +1 -0
  37. package/dist/chunk-M4ANN7RL.js +114 -0
  38. package/dist/chunk-M4ANN7RL.js.map +1 -0
  39. package/dist/chunk-NC52UBR2.cjs +32 -0
  40. package/dist/chunk-NC52UBR2.cjs.map +1 -0
  41. package/dist/chunk-NHCNRQD3.cjs +212 -0
  42. package/dist/chunk-NHCNRQD3.cjs.map +1 -0
  43. package/dist/chunk-NZ72VDNY.cjs +4 -0
  44. package/dist/chunk-NZ72VDNY.cjs.map +1 -0
  45. package/dist/chunk-P6JUDYNO.js +57 -0
  46. package/dist/chunk-P6JUDYNO.js.map +1 -0
  47. package/dist/chunk-RJYY7BWX.js +1349 -0
  48. package/dist/chunk-RJYY7BWX.js.map +1 -0
  49. package/dist/chunk-TRI4V5BF.cjs +126 -0
  50. package/dist/chunk-TRI4V5BF.cjs.map +1 -0
  51. package/dist/chunk-UL33I6IS.js +139 -0
  52. package/dist/chunk-UL33I6IS.js.map +1 -0
  53. package/dist/chunk-URRW6M2C.cjs +61 -0
  54. package/dist/chunk-URRW6M2C.cjs.map +1 -0
  55. package/dist/chunk-UY3UYPBZ.cjs +77 -0
  56. package/dist/chunk-UY3UYPBZ.cjs.map +1 -0
  57. package/dist/chunk-W3253FGB.cjs +277 -0
  58. package/dist/chunk-W3253FGB.cjs.map +1 -0
  59. package/dist/chunk-W7LHZVQF.js +26 -0
  60. package/dist/chunk-W7LHZVQF.js.map +1 -0
  61. package/dist/chunk-WBWNM6LB.cjs +1360 -0
  62. package/dist/chunk-WBWNM6LB.cjs.map +1 -0
  63. package/dist/chunk-WFJ7L2RV.js +494 -0
  64. package/dist/chunk-WFJ7L2RV.js.map +1 -0
  65. package/dist/chunk-X4RMFFMR.js +28 -0
  66. package/dist/chunk-X4RMFFMR.js.map +1 -0
  67. package/dist/chunk-Y4Y2S7BM.cjs +92 -0
  68. package/dist/chunk-Y4Y2S7BM.cjs.map +1 -0
  69. package/dist/chunk-YLPNXZFI.cjs +143 -0
  70. package/dist/chunk-YLPNXZFI.cjs.map +1 -0
  71. package/dist/chunk-YTXEZ4SD.cjs +77 -0
  72. package/dist/chunk-YTXEZ4SD.cjs.map +1 -0
  73. package/dist/chunk-Z6ZWNWWR.js +30 -0
  74. package/dist/chunk-Z6ZWNWWR.js.map +1 -0
  75. package/dist/config.cjs +26 -0
  76. package/dist/config.cjs.map +1 -0
  77. package/dist/config.d.cts +75 -0
  78. package/dist/config.d.ts +75 -0
  79. package/dist/config.js +5 -0
  80. package/dist/config.js.map +1 -0
  81. package/dist/db.cjs +233 -0
  82. package/dist/db.cjs.map +1 -0
  83. package/dist/db.d.cts +123 -0
  84. package/dist/db.d.ts +123 -0
  85. package/dist/db.js +228 -0
  86. package/dist/db.js.map +1 -0
  87. package/dist/decorators.cjs +67 -0
  88. package/dist/decorators.cjs.map +1 -0
  89. package/dist/decorators.d.cts +91 -0
  90. package/dist/decorators.d.ts +91 -0
  91. package/dist/decorators.js +65 -0
  92. package/dist/decorators.js.map +1 -0
  93. package/dist/event-subscriber.cjs +6 -0
  94. package/dist/event-subscriber.cjs.map +1 -0
  95. package/dist/event-subscriber.d.cts +116 -0
  96. package/dist/event-subscriber.d.ts +116 -0
  97. package/dist/event-subscriber.js +3 -0
  98. package/dist/event-subscriber.js.map +1 -0
  99. package/dist/event-testing.cjs +21 -0
  100. package/dist/event-testing.cjs.map +1 -0
  101. package/dist/event-testing.d.cts +110 -0
  102. package/dist/event-testing.d.ts +110 -0
  103. package/dist/event-testing.js +4 -0
  104. package/dist/event-testing.js.map +1 -0
  105. package/dist/event.cjs +30 -0
  106. package/dist/event.cjs.map +1 -0
  107. package/dist/event.d.cts +282 -0
  108. package/dist/event.d.ts +282 -0
  109. package/dist/event.js +13 -0
  110. package/dist/event.js.map +1 -0
  111. package/dist/exporters.cjs +17 -0
  112. package/dist/exporters.cjs.map +1 -0
  113. package/dist/exporters.d.cts +1 -0
  114. package/dist/exporters.d.ts +1 -0
  115. package/dist/exporters.js +4 -0
  116. package/dist/exporters.js.map +1 -0
  117. package/dist/functional.cjs +46 -0
  118. package/dist/functional.cjs.map +1 -0
  119. package/dist/functional.d.cts +478 -0
  120. package/dist/functional.d.ts +478 -0
  121. package/dist/functional.js +13 -0
  122. package/dist/functional.js.map +1 -0
  123. package/dist/http.cjs +189 -0
  124. package/dist/http.cjs.map +1 -0
  125. package/dist/http.d.cts +169 -0
  126. package/dist/http.d.ts +169 -0
  127. package/dist/http.js +184 -0
  128. package/dist/http.js.map +1 -0
  129. package/dist/index.cjs +333 -0
  130. package/dist/index.cjs.map +1 -0
  131. package/dist/index.d.cts +758 -0
  132. package/dist/index.d.ts +758 -0
  133. package/dist/index.js +143 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/instrumentation.cjs +182 -0
  136. package/dist/instrumentation.cjs.map +1 -0
  137. package/dist/instrumentation.d.cts +49 -0
  138. package/dist/instrumentation.d.ts +49 -0
  139. package/dist/instrumentation.js +179 -0
  140. package/dist/instrumentation.js.map +1 -0
  141. package/dist/logger.cjs +19 -0
  142. package/dist/logger.cjs.map +1 -0
  143. package/dist/logger.d.cts +146 -0
  144. package/dist/logger.d.ts +146 -0
  145. package/dist/logger.js +6 -0
  146. package/dist/logger.js.map +1 -0
  147. package/dist/metric-helpers.cjs +31 -0
  148. package/dist/metric-helpers.cjs.map +1 -0
  149. package/dist/metric-helpers.d.cts +13 -0
  150. package/dist/metric-helpers.d.ts +13 -0
  151. package/dist/metric-helpers.js +6 -0
  152. package/dist/metric-helpers.js.map +1 -0
  153. package/dist/metric-testing.cjs +21 -0
  154. package/dist/metric-testing.cjs.map +1 -0
  155. package/dist/metric-testing.d.cts +110 -0
  156. package/dist/metric-testing.d.ts +110 -0
  157. package/dist/metric-testing.js +4 -0
  158. package/dist/metric-testing.js.map +1 -0
  159. package/dist/metric.cjs +26 -0
  160. package/dist/metric.cjs.map +1 -0
  161. package/dist/metric.d.cts +240 -0
  162. package/dist/metric.d.ts +240 -0
  163. package/dist/metric.js +9 -0
  164. package/dist/metric.js.map +1 -0
  165. package/dist/processors.cjs +17 -0
  166. package/dist/processors.cjs.map +1 -0
  167. package/dist/processors.d.cts +1 -0
  168. package/dist/processors.d.ts +1 -0
  169. package/dist/processors.js +4 -0
  170. package/dist/processors.js.map +1 -0
  171. package/dist/sampling.cjs +40 -0
  172. package/dist/sampling.cjs.map +1 -0
  173. package/dist/sampling.d.cts +260 -0
  174. package/dist/sampling.d.ts +260 -0
  175. package/dist/sampling.js +7 -0
  176. package/dist/sampling.js.map +1 -0
  177. package/dist/semantic-helpers.cjs +35 -0
  178. package/dist/semantic-helpers.cjs.map +1 -0
  179. package/dist/semantic-helpers.d.cts +442 -0
  180. package/dist/semantic-helpers.d.ts +442 -0
  181. package/dist/semantic-helpers.js +14 -0
  182. package/dist/semantic-helpers.js.map +1 -0
  183. package/dist/tail-sampling-processor.cjs +13 -0
  184. package/dist/tail-sampling-processor.cjs.map +1 -0
  185. package/dist/tail-sampling-processor.d.cts +27 -0
  186. package/dist/tail-sampling-processor.d.ts +27 -0
  187. package/dist/tail-sampling-processor.js +4 -0
  188. package/dist/tail-sampling-processor.js.map +1 -0
  189. package/dist/testing.cjs +286 -0
  190. package/dist/testing.cjs.map +1 -0
  191. package/dist/testing.d.cts +291 -0
  192. package/dist/testing.d.ts +291 -0
  193. package/dist/testing.js +263 -0
  194. package/dist/testing.js.map +1 -0
  195. package/dist/trace-context-DRZdUvVY.d.cts +181 -0
  196. package/dist/trace-context-DRZdUvVY.d.ts +181 -0
  197. package/dist/trace-helpers.cjs +54 -0
  198. package/dist/trace-helpers.cjs.map +1 -0
  199. package/dist/trace-helpers.d.cts +524 -0
  200. package/dist/trace-helpers.d.ts +524 -0
  201. package/dist/trace-helpers.js +5 -0
  202. package/dist/trace-helpers.js.map +1 -0
  203. package/dist/tracer-provider.cjs +21 -0
  204. package/dist/tracer-provider.cjs.map +1 -0
  205. package/dist/tracer-provider.d.cts +169 -0
  206. package/dist/tracer-provider.d.ts +169 -0
  207. package/dist/tracer-provider.js +4 -0
  208. package/dist/tracer-provider.js.map +1 -0
  209. package/package.json +280 -0
  210. package/src/baggage-span-processor.test.ts +202 -0
  211. package/src/baggage-span-processor.ts +98 -0
  212. package/src/circuit-breaker.test.ts +341 -0
  213. package/src/circuit-breaker.ts +184 -0
  214. package/src/config.test.ts +94 -0
  215. package/src/config.ts +169 -0
  216. package/src/db.test.ts +252 -0
  217. package/src/db.ts +447 -0
  218. package/src/decorators.test.ts +203 -0
  219. package/src/decorators.ts +188 -0
  220. package/src/env-config.test.ts +246 -0
  221. package/src/env-config.ts +158 -0
  222. package/src/event-queue.test.ts +222 -0
  223. package/src/event-queue.ts +203 -0
  224. package/src/event-subscriber.ts +136 -0
  225. package/src/event-testing.ts +197 -0
  226. package/src/event.test.ts +718 -0
  227. package/src/event.ts +556 -0
  228. package/src/exporters.ts +96 -0
  229. package/src/functional.test.ts +1059 -0
  230. package/src/functional.ts +2295 -0
  231. package/src/http.test.ts +487 -0
  232. package/src/http.ts +424 -0
  233. package/src/index.ts +158 -0
  234. package/src/init.customization.test.ts +210 -0
  235. package/src/init.integrations.test.ts +366 -0
  236. package/src/init.openllmetry.test.ts +282 -0
  237. package/src/init.protocol.test.ts +215 -0
  238. package/src/init.ts +1426 -0
  239. package/src/instrumentation.test.ts +108 -0
  240. package/src/instrumentation.ts +308 -0
  241. package/src/logger.test.ts +117 -0
  242. package/src/logger.ts +246 -0
  243. package/src/metric-helpers.ts +47 -0
  244. package/src/metric-testing.ts +197 -0
  245. package/src/metric.ts +434 -0
  246. package/src/metrics.test.ts +205 -0
  247. package/src/operation-context.ts +93 -0
  248. package/src/processors.ts +106 -0
  249. package/src/rate-limiter.test.ts +199 -0
  250. package/src/rate-limiter.ts +98 -0
  251. package/src/sampling.test.ts +513 -0
  252. package/src/sampling.ts +428 -0
  253. package/src/semantic-helpers.test.ts +311 -0
  254. package/src/semantic-helpers.ts +584 -0
  255. package/src/shutdown.test.ts +311 -0
  256. package/src/shutdown.ts +222 -0
  257. package/src/stub.integration.test.ts +361 -0
  258. package/src/tail-sampling-processor.test.ts +226 -0
  259. package/src/tail-sampling-processor.ts +51 -0
  260. package/src/testing.ts +670 -0
  261. package/src/trace-context.ts +470 -0
  262. package/src/trace-helpers.new.test.ts +278 -0
  263. package/src/trace-helpers.test.ts +242 -0
  264. package/src/trace-helpers.ts +690 -0
  265. package/src/tracer-provider.test.ts +183 -0
  266. package/src/tracer-provider.ts +266 -0
  267. package/src/track.test.ts +153 -0
  268. package/src/track.ts +120 -0
  269. package/src/validation.test.ts +306 -0
  270. package/src/validation.ts +239 -0
  271. package/src/variable-name-inference.test.ts +178 -0
  272. package/src/variable-name-inference.ts +242 -0
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Tests for semantic convention helpers
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from 'vitest';
6
+ import {
7
+ traceLLM,
8
+ traceDB,
9
+ traceHTTP,
10
+ traceMessaging,
11
+ } from './semantic-helpers';
12
+ import { createTraceCollector } from './testing';
13
+
14
+ describe('Semantic Helpers', () => {
15
+ let collector: ReturnType<typeof createTraceCollector>;
16
+
17
+ beforeEach(() => {
18
+ collector = createTraceCollector();
19
+ });
20
+
21
+ describe('traceLLM', () => {
22
+ it('should add Gen AI semantic convention attributes', async () => {
23
+ const generateText = traceLLM({
24
+ model: 'gpt-4',
25
+ operation: 'chat',
26
+ system: 'openai',
27
+ })((_ctx) => async (prompt: string) => {
28
+ return `Response to: ${prompt}`;
29
+ });
30
+
31
+ await generateText('Hello');
32
+
33
+ const spans = collector.getSpans();
34
+ expect(spans).toHaveLength(1);
35
+
36
+ const span = spans[0];
37
+ expect(span.attributes['gen.ai.request.model']).toBe('gpt-4');
38
+ expect(span.attributes['gen.ai.operation.name']).toBe('chat');
39
+ expect(span.attributes['gen.ai.system']).toBe('openai');
40
+ });
41
+
42
+ it('should use default operation when not specified', async () => {
43
+ const generateText = traceLLM({
44
+ model: 'claude-3',
45
+ })((_ctx) => async () => 'result');
46
+
47
+ await generateText();
48
+
49
+ const spans = collector.getSpans();
50
+ expect(spans[0].attributes['gen.ai.operation.name']).toBe('chat');
51
+ });
52
+
53
+ it('should support embedding operation', async () => {
54
+ const embed = traceLLM({
55
+ model: 'text-embedding-3-small',
56
+ operation: 'embedding',
57
+ system: 'openai',
58
+ })((_ctx) => async (_text: string) => [0.1, 0.2, 0.3]);
59
+
60
+ await embed('test text');
61
+
62
+ const spans = collector.getSpans();
63
+ expect(spans[0].attributes['gen.ai.operation.name']).toBe('embedding');
64
+ });
65
+
66
+ it('should support additional custom attributes', async () => {
67
+ const generateText = traceLLM({
68
+ model: 'gpt-4',
69
+ attributes: {
70
+ 'custom.attribute': 'custom-value',
71
+ 'custom.number': 123,
72
+ },
73
+ })((_ctx) => async () => 'result');
74
+
75
+ await generateText();
76
+
77
+ const spans = collector.getSpans();
78
+ const span = spans[0];
79
+ expect(span.attributes['gen.ai.request.model']).toBe('gpt-4');
80
+ expect(span.attributes['custom.attribute']).toBe('custom-value');
81
+ expect(span.attributes['custom.number']).toBe(123);
82
+ });
83
+ });
84
+
85
+ describe('traceDB', () => {
86
+ it('should add DB semantic convention attributes', async () => {
87
+ const getUser = traceDB({
88
+ system: 'postgresql',
89
+ operation: 'SELECT',
90
+ dbName: 'app_db',
91
+ collection: 'users',
92
+ })((_ctx) => async (userId: string) => {
93
+ return { id: userId, name: 'John' };
94
+ });
95
+
96
+ await getUser('123');
97
+
98
+ const spans = collector.getSpans();
99
+ expect(spans).toHaveLength(1);
100
+
101
+ const span = spans[0];
102
+ expect(span.attributes['db.system']).toBe('postgresql');
103
+ expect(span.attributes['db.operation']).toBe('SELECT');
104
+ expect(span.attributes['db.name']).toBe('app_db');
105
+ expect(span.attributes['db.collection.name']).toBe('users');
106
+ });
107
+
108
+ it('should work without optional attributes', async () => {
109
+ const query = traceDB({
110
+ system: 'mongodb',
111
+ })((_ctx) => async () => ({ results: [] }));
112
+
113
+ await query();
114
+
115
+ const spans = collector.getSpans();
116
+ const span = spans[0];
117
+ expect(span.attributes['db.system']).toBe('mongodb');
118
+ expect(span.attributes['db.operation']).toBeUndefined();
119
+ expect(span.attributes['db.name']).toBeUndefined();
120
+ });
121
+
122
+ it('should support custom attributes', async () => {
123
+ const query = traceDB({
124
+ system: 'redis',
125
+ operation: 'GET',
126
+ attributes: {
127
+ 'db.redis.ttl': 3600,
128
+ },
129
+ })((_ctx) => async (key: string) => `value-${key}`);
130
+
131
+ await query('test-key');
132
+
133
+ const spans = collector.getSpans();
134
+ const span = spans[0];
135
+ expect(span.attributes['db.system']).toBe('redis');
136
+ expect(span.attributes['db.redis.ttl']).toBe(3600);
137
+ });
138
+ });
139
+
140
+ describe('traceHTTP', () => {
141
+ it('should add HTTP semantic convention attributes', async () => {
142
+ const fetchUser = traceHTTP({
143
+ method: 'GET',
144
+ url: 'https://api.example.com/users/:id',
145
+ })((_ctx) => async (userId: string) => {
146
+ return { id: userId };
147
+ });
148
+
149
+ await fetchUser('123');
150
+
151
+ const spans = collector.getSpans();
152
+ expect(spans).toHaveLength(1);
153
+
154
+ const span = spans[0];
155
+ expect(span.attributes['http.request.method']).toBe('GET');
156
+ expect(span.attributes['url.full']).toBe(
157
+ 'https://api.example.com/users/:id',
158
+ );
159
+ });
160
+
161
+ it('should work with only method', async () => {
162
+ const request = traceHTTP({
163
+ method: 'POST',
164
+ })((_ctx) => async (_data: object) => ({ success: true }));
165
+
166
+ await request({ test: 'data' });
167
+
168
+ const spans = collector.getSpans();
169
+ const span = spans[0];
170
+ expect(span.attributes['http.request.method']).toBe('POST');
171
+ expect(span.attributes['url.full']).toBeUndefined();
172
+ });
173
+
174
+ it('should work with only URL', async () => {
175
+ const request = traceHTTP({
176
+ url: 'https://api.example.com',
177
+ })((_ctx) => async () => ({ success: true }));
178
+
179
+ await request();
180
+
181
+ const spans = collector.getSpans();
182
+ const span = spans[0];
183
+ expect(span.attributes['url.full']).toBe('https://api.example.com');
184
+ expect(span.attributes['http.request.method']).toBeUndefined();
185
+ });
186
+
187
+ it('should support custom attributes', async () => {
188
+ const request = traceHTTP({
189
+ method: 'POST',
190
+ url: 'https://webhook.example.com',
191
+ attributes: {
192
+ 'http.request.retry_count': 3,
193
+ 'http.request.timeout': 5000,
194
+ },
195
+ })((_ctx) => async () => ({ success: true }));
196
+
197
+ await request();
198
+
199
+ const spans = collector.getSpans();
200
+ const span = spans[0];
201
+ expect(span.attributes['http.request.retry_count']).toBe(3);
202
+ expect(span.attributes['http.request.timeout']).toBe(5000);
203
+ });
204
+ });
205
+
206
+ describe('traceMessaging', () => {
207
+ it('should add Messaging semantic convention attributes', async () => {
208
+ const publishEvent = traceMessaging({
209
+ system: 'kafka',
210
+ operation: 'publish',
211
+ destination: 'user-events',
212
+ })((_ctx) => async (_event: object) => {
213
+ return { messageId: '123' };
214
+ });
215
+
216
+ await publishEvent({ type: 'user.created' });
217
+
218
+ const spans = collector.getSpans();
219
+ expect(spans).toHaveLength(1);
220
+
221
+ const span = spans[0];
222
+ expect(span.attributes['messaging.system']).toBe('kafka');
223
+ expect(span.attributes['messaging.operation']).toBe('publish');
224
+ expect(span.attributes['messaging.destination.name']).toBe('user-events');
225
+ });
226
+
227
+ it('should work with minimal config', async () => {
228
+ const sendMessage = traceMessaging({
229
+ system: 'rabbitmq',
230
+ })((_ctx) => async () => ({ sent: true }));
231
+
232
+ await sendMessage();
233
+
234
+ const spans = collector.getSpans();
235
+ const span = spans[0];
236
+ expect(span.attributes['messaging.system']).toBe('rabbitmq');
237
+ expect(span.attributes['messaging.operation']).toBeUndefined();
238
+ expect(span.attributes['messaging.destination.name']).toBeUndefined();
239
+ });
240
+
241
+ it('should support receive operation', async () => {
242
+ const consumeMessage = traceMessaging({
243
+ system: 'sqs',
244
+ operation: 'receive',
245
+ destination: 'notifications',
246
+ })((_ctx) => async () => ({ messages: [] }));
247
+
248
+ await consumeMessage();
249
+
250
+ const spans = collector.getSpans();
251
+ const span = spans[0];
252
+ expect(span.attributes['messaging.operation']).toBe('receive');
253
+ });
254
+
255
+ it('should support custom attributes', async () => {
256
+ const publishBatch = traceMessaging({
257
+ system: 'aws_sqs',
258
+ operation: 'publish',
259
+ destination: 'orders',
260
+ attributes: {
261
+ 'messaging.batch.message_count': 10,
262
+ 'messaging.kafka.partition': 0,
263
+ },
264
+ })((_ctx) => async () => ({ success: true }));
265
+
266
+ await publishBatch();
267
+
268
+ const spans = collector.getSpans();
269
+ const span = spans[0];
270
+ expect(span.attributes['messaging.batch.message_count']).toBe(10);
271
+ expect(span.attributes['messaging.kafka.partition']).toBe(0);
272
+ });
273
+ });
274
+
275
+ describe('Attribute merging', () => {
276
+ it('should merge custom attributes with semantic attributes in traceLLM', async () => {
277
+ const fn = traceLLM({
278
+ model: 'gpt-4',
279
+ attributes: {
280
+ 'gen.ai.request.temperature': 0.7,
281
+ 'custom.attr': 'value',
282
+ },
283
+ })((_ctx) => async () => 'result');
284
+
285
+ await fn();
286
+
287
+ const spans = collector.getSpans();
288
+ const span = spans[0];
289
+ expect(span.attributes['gen.ai.request.model']).toBe('gpt-4');
290
+ expect(span.attributes['gen.ai.request.temperature']).toBe(0.7);
291
+ expect(span.attributes['custom.attr']).toBe('value');
292
+ });
293
+
294
+ it('should allow custom attributes to override semantic defaults', async () => {
295
+ const fn = traceDB({
296
+ system: 'postgresql',
297
+ operation: 'SELECT',
298
+ attributes: {
299
+ 'db.operation': 'CUSTOM_OPERATION', // Override default
300
+ },
301
+ })((_ctx) => async () => ({ rows: [] }));
302
+
303
+ await fn();
304
+
305
+ const spans = collector.getSpans();
306
+ const span = spans[0];
307
+ // Custom attribute should win
308
+ expect(span.attributes['db.operation']).toBe('CUSTOM_OPERATION');
309
+ });
310
+ });
311
+ });