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,282 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import type { NodeSDK } from '@opentelemetry/sdk-node';
3
+ import { mock, mockDeep, type DeepMockProxy } from 'vitest-mock-extended';
4
+
5
+ type SdkRecord = {
6
+ options: Record<string, unknown>;
7
+ instance: DeepMockProxy<NodeSDK>;
8
+ };
9
+
10
+ const mockedModules = [
11
+ '@opentelemetry/sdk-node',
12
+ '@opentelemetry/exporter-trace-otlp-http',
13
+ '@opentelemetry/exporter-metrics-otlp-http',
14
+ '@opentelemetry/sdk-metrics',
15
+ '@traceloop/node-server-sdk',
16
+ ];
17
+
18
+ // Track traceloop initialize calls globally
19
+ const traceloopInitializeCalls: Array<Record<string, unknown>> = [];
20
+
21
+ // Store mock initialize function globally
22
+ let globalMockInitialize: ReturnType<typeof vi.fn> | null = null;
23
+
24
+ async function loadInitWithMocks() {
25
+ const sdkInstances: SdkRecord[] = [];
26
+
27
+ class MockNodeSDK {
28
+ constructor(options: Record<string, unknown>) {
29
+ const instance = mockDeep<NodeSDK>();
30
+ instance.start.mockImplementation(() => {});
31
+ instance.shutdown.mockResolvedValue();
32
+ // Add getTracerProvider method (not in public interface but used internally)
33
+
34
+ (instance as any).getTracerProvider = vi.fn().mockReturnValue(mock());
35
+ sdkInstances.push({ options, instance });
36
+ return instance;
37
+ }
38
+ }
39
+
40
+ class MockOTLPTraceExporter {
41
+ options: Record<string, unknown>;
42
+
43
+ constructor(options: Record<string, unknown>) {
44
+ this.options = options;
45
+ }
46
+ }
47
+
48
+ class MockOTLPMetricExporter {
49
+ options: Record<string, unknown>;
50
+
51
+ constructor(options: Record<string, unknown>) {
52
+ this.options = options;
53
+ }
54
+ }
55
+
56
+ class MockPeriodicExportingMetricReader {
57
+ options: Record<string, unknown>;
58
+
59
+ constructor(options: Record<string, unknown>) {
60
+ this.options = options;
61
+ }
62
+ }
63
+
64
+ // Clear module cache first
65
+ vi.resetModules();
66
+
67
+ // Create mock function that captures calls and store it globally
68
+ globalMockInitialize = vi.fn((options?: Record<string, unknown>) => {
69
+ traceloopInitializeCalls.push(options || {});
70
+ });
71
+
72
+ // Re-setup the mock after resetModules
73
+ // Use virtual: true to make require() fail and fall back to async import
74
+ vi.doMock(
75
+ '@traceloop/node-server-sdk',
76
+ () => {
77
+ // This will be called for dynamic import() - return the mock
78
+ return {
79
+ initialize: globalMockInitialize!,
80
+ instrumentations: [{ name: 'openai' }, { name: 'langchain' }],
81
+ default: {
82
+ initialize: globalMockInitialize!,
83
+ instrumentations: [{ name: 'openai' }, { name: 'langchain' }],
84
+ },
85
+ };
86
+ },
87
+ { virtual: true },
88
+ );
89
+
90
+ vi.doMock('@opentelemetry/sdk-node', () => ({
91
+ NodeSDK: MockNodeSDK,
92
+ }));
93
+
94
+ vi.doMock('@opentelemetry/exporter-trace-otlp-http', () => ({
95
+ OTLPTraceExporter: MockOTLPTraceExporter,
96
+ }));
97
+
98
+ vi.doMock('@opentelemetry/exporter-metrics-otlp-http', () => ({
99
+ OTLPMetricExporter: MockOTLPMetricExporter,
100
+ }));
101
+
102
+ vi.doMock('@opentelemetry/sdk-metrics', () => ({
103
+ PeriodicExportingMetricReader: MockPeriodicExportingMetricReader,
104
+ }));
105
+
106
+ const mod = await import('./init');
107
+
108
+ return {
109
+ init: mod.init,
110
+ getConfig: mod.getConfig,
111
+ sdkInstances,
112
+ traceloopInitializeCalls,
113
+ mockTraceloop: {
114
+ initialize: globalMockInitialize!, // Return the same mock function reference
115
+ instrumentations: [{ name: 'openai' }, { name: 'langchain' }],
116
+ },
117
+ };
118
+ }
119
+
120
+ describe('init() OpenLLMetry integration', () => {
121
+ beforeEach(() => {
122
+ vi.resetModules();
123
+ traceloopInitializeCalls.length = 0; // Clear calls array
124
+ globalMockInitialize = null; // Reset mock function
125
+ });
126
+
127
+ afterEach(() => {
128
+ for (const mod of mockedModules) {
129
+ vi.doUnmock(mod);
130
+ }
131
+ vi.clearAllMocks();
132
+ delete process.env.AUTOTELEMETRY_METRICS;
133
+ delete process.env.NODE_ENV;
134
+ delete process.env.TRACELOOP_API_KEY;
135
+ });
136
+
137
+ it('should not initialize OpenLLMetry when disabled', async () => {
138
+ const { init, traceloopInitializeCalls } = await loadInitWithMocks();
139
+
140
+ init({ service: 'test-app' });
141
+
142
+ expect(traceloopInitializeCalls).toHaveLength(0);
143
+ });
144
+
145
+ // Skipped: vi.doMock doesn't properly intercept require('@traceloop/node-server-sdk')
146
+ // when the real module is installed. The real module loads instead of the mock,
147
+ // so initialize() calls aren't captured. This is a known vitest limitation with
148
+ // optional peer dependencies that may or may not be installed.
149
+ it.skip('should initialize OpenLLMetry when enabled', async () => {
150
+ const { init, traceloopInitializeCalls } = await loadInitWithMocks();
151
+
152
+ init({
153
+ service: 'test-app',
154
+ openllmetry: { enabled: true },
155
+ });
156
+
157
+ // Wait for async import to complete (require fails, falls back to async import)
158
+ // Use a longer timeout and retry logic for CI environments
159
+ let attempts = 0;
160
+ while (traceloopInitializeCalls.length === 0 && attempts < 20) {
161
+ await new Promise((resolve) => setTimeout(resolve, 50));
162
+ attempts++;
163
+ }
164
+
165
+ expect(traceloopInitializeCalls).toHaveLength(1);
166
+ const callOptions = traceloopInitializeCalls[0];
167
+ expect(callOptions).toBeDefined();
168
+ });
169
+
170
+ // Skipped: Same mocking issue as above - vi.doMock doesn't intercept require()
171
+ // calls for optional peer dependencies when the real module is installed.
172
+ it.skip('should pass OpenLLMetry options to initialize', async () => {
173
+ const { init, traceloopInitializeCalls } = await loadInitWithMocks();
174
+
175
+ init({
176
+ service: 'test-app',
177
+ openllmetry: {
178
+ enabled: true,
179
+ options: {
180
+ disableBatch: true,
181
+ apiKey: 'test-key',
182
+ },
183
+ },
184
+ });
185
+
186
+ // Wait for async import to complete
187
+ // Use a longer timeout for CI environments
188
+ await new Promise((resolve) => setTimeout(resolve, 500));
189
+
190
+ expect(traceloopInitializeCalls).toHaveLength(1);
191
+ const callOptions = traceloopInitializeCalls[0];
192
+ expect(callOptions).toMatchObject({
193
+ disableBatch: true,
194
+ apiKey: 'test-key',
195
+ });
196
+ });
197
+
198
+ // Skipped: Same mocking issue - vi.doMock doesn't intercept require() for optional
199
+ // peer dependencies, so the mock initialize() function is never called.
200
+ it.skip('should reuse autotel tracer provider when OpenLLMetry is enabled', async () => {
201
+ const { init, traceloopInitializeCalls, sdkInstances } =
202
+ await loadInitWithMocks();
203
+
204
+ init({
205
+ service: 'test-app',
206
+ openllmetry: { enabled: true },
207
+ });
208
+
209
+ expect(sdkInstances).toHaveLength(1);
210
+ const sdkInstance = sdkInstances[0].instance;
211
+
212
+ // Wait a bit for async operations if any
213
+ // Use a longer timeout for CI environments
214
+ await new Promise((resolve) => setTimeout(resolve, 500));
215
+
216
+ expect(traceloopInitializeCalls).toHaveLength(1);
217
+ const callOptions = traceloopInitializeCalls[0];
218
+ // Should pass tracer provider to OpenLLMetry
219
+ expect(callOptions).toBeDefined();
220
+ // Verify getTracerProvider was called to get the provider
221
+
222
+ expect((sdkInstance as any).getTracerProvider).toHaveBeenCalled();
223
+ });
224
+
225
+ it('should add OpenLLMetry instrumentations when selectiveInstrumentation is false', async () => {
226
+ const { init, sdkInstances, mockTraceloop } = await loadInitWithMocks();
227
+
228
+ init({
229
+ service: 'test-app',
230
+ openllmetry: { enabled: true },
231
+ integrations: false, // This means selectiveInstrumentation is true by default
232
+ });
233
+
234
+ const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
235
+ const instrumentations = options.instrumentations as unknown[];
236
+
237
+ // When selectiveInstrumentation is true (default), OpenLLMetry instrumentations should be added
238
+ expect(instrumentations).toBeDefined();
239
+ // Should include OpenLLMetry instrumentations
240
+ expect(mockTraceloop.instrumentations).toBeDefined();
241
+ });
242
+
243
+ it('should handle missing @traceloop/node-server-sdk gracefully', async () => {
244
+ vi.doMock('@traceloop/node-server-sdk', () => {
245
+ throw new Error('Module not found');
246
+ });
247
+
248
+ const { init } = await import('./init');
249
+
250
+ // Should not throw, but log a warning
251
+ expect(() => {
252
+ init({
253
+ service: 'test-app',
254
+ openllmetry: { enabled: true },
255
+ });
256
+ }).not.toThrow();
257
+ });
258
+
259
+ // Skipped: Same mocking issue - vi.doMock doesn't intercept require() calls,
260
+ // so the async import fallback path is used but the mock isn't found either.
261
+ // This prevents proper verification of the initialization sequence.
262
+ it.skip('should initialize OpenLLMetry after SDK start', async () => {
263
+ const { init, sdkInstances, traceloopInitializeCalls } =
264
+ await loadInitWithMocks();
265
+
266
+ init({
267
+ service: 'test-app',
268
+ openllmetry: { enabled: true },
269
+ });
270
+
271
+ // Wait for async import to complete (require fails, falls back to async import)
272
+ // Use a longer timeout for CI environments
273
+ await new Promise((resolve) => setTimeout(resolve, 500));
274
+
275
+ // Verify SDK started (it's called synchronously in init)
276
+ expect(sdkInstances).toHaveLength(1);
277
+ expect(sdkInstances[0].instance.start).toHaveBeenCalled();
278
+
279
+ // Verify OpenLLMetry was initialized (via async import)
280
+ expect(traceloopInitializeCalls).toHaveLength(1);
281
+ });
282
+ });
@@ -0,0 +1,215 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ /**
4
+ * Unit tests for protocol switching helper functions
5
+ *
6
+ * These tests verify the URL formatting and protocol resolution logic
7
+ * without requiring dynamic module loading or complex mocking.
8
+ */
9
+
10
+ // Helper functions copied from init.ts for testing
11
+ function formatEndpointUrl(
12
+ endpoint: string,
13
+ signal: 'traces' | 'metrics',
14
+ protocol: 'http' | 'grpc',
15
+ ): string {
16
+ if (protocol === 'grpc') {
17
+ // gRPC: strip any paths, return base endpoint
18
+ return endpoint.replace(/\/(v1\/)?(traces|metrics|logs)$/, '');
19
+ }
20
+
21
+ // HTTP: append signal path if not present
22
+ if (!endpoint.endsWith(`/v1/${signal}`)) {
23
+ return `${endpoint}/v1/${signal}`;
24
+ }
25
+
26
+ return endpoint;
27
+ }
28
+
29
+ function resolveProtocol(
30
+ configProtocol?: 'http' | 'grpc',
31
+ envProtocol?: string,
32
+ ): 'http' | 'grpc' {
33
+ // 1. Check config parameter (highest priority)
34
+ if (configProtocol === 'grpc' || configProtocol === 'http') {
35
+ return configProtocol;
36
+ }
37
+
38
+ // 2. Check OTEL_EXPORTER_OTLP_PROTOCOL env var
39
+ if (envProtocol === 'grpc') return 'grpc';
40
+ if (envProtocol === 'http/protobuf' || envProtocol === 'http') return 'http';
41
+
42
+ // 3. Default to HTTP
43
+ return 'http';
44
+ }
45
+
46
+ describe('Protocol resolution logic', () => {
47
+ describe('resolveProtocol()', () => {
48
+ it('should return http by default', () => {
49
+ expect(resolveProtocol()).toBe('http');
50
+ });
51
+
52
+ it('should respect config parameter over env var', () => {
53
+ expect(resolveProtocol('http', 'grpc')).toBe('http');
54
+ expect(resolveProtocol('grpc', 'http')).toBe('grpc');
55
+ });
56
+
57
+ it('should use grpc when env var is grpc', () => {
58
+ expect(resolveProtocol(undefined, 'grpc')).toBe('grpc');
59
+ });
60
+
61
+ it('should use http when env var is http', () => {
62
+ expect(resolveProtocol(undefined, 'http')).toBe('http');
63
+ });
64
+
65
+ it('should use http when env var is http/protobuf', () => {
66
+ expect(resolveProtocol(undefined, 'http/protobuf')).toBe('http');
67
+ });
68
+
69
+ it('should default to http for invalid env var', () => {
70
+ expect(resolveProtocol(undefined, 'invalid')).toBe('http');
71
+ expect(resolveProtocol(undefined, '')).toBe('http');
72
+ });
73
+
74
+ it('should prioritize config parameter', () => {
75
+ expect(resolveProtocol('grpc')).toBe('grpc');
76
+ expect(resolveProtocol('http')).toBe('http');
77
+ });
78
+ });
79
+
80
+ describe('formatEndpointUrl() for HTTP protocol', () => {
81
+ it('should append /v1/traces for traces', () => {
82
+ expect(formatEndpointUrl('http://localhost:4318', 'traces', 'http')).toBe(
83
+ 'http://localhost:4318/v1/traces',
84
+ );
85
+ });
86
+
87
+ it('should append /v1/metrics for metrics', () => {
88
+ expect(
89
+ formatEndpointUrl('http://localhost:4318', 'metrics', 'http'),
90
+ ).toBe('http://localhost:4318/v1/metrics');
91
+ });
92
+
93
+ it('should not double-append path if already present', () => {
94
+ expect(
95
+ formatEndpointUrl('http://localhost:4318/v1/traces', 'traces', 'http'),
96
+ ).toBe('http://localhost:4318/v1/traces');
97
+ });
98
+
99
+ it('should handle endpoints without http prefix', () => {
100
+ expect(formatEndpointUrl('localhost:4318', 'traces', 'http')).toBe(
101
+ 'localhost:4318/v1/traces',
102
+ );
103
+ });
104
+
105
+ it('should handle HTTPS endpoints', () => {
106
+ expect(
107
+ formatEndpointUrl('https://otlp.example.com', 'traces', 'http'),
108
+ ).toBe('https://otlp.example.com/v1/traces');
109
+ });
110
+
111
+ it('should handle endpoints with trailing slash', () => {
112
+ expect(
113
+ formatEndpointUrl('http://localhost:4318/', 'traces', 'http'),
114
+ ).toBe('http://localhost:4318//v1/traces');
115
+ });
116
+ });
117
+
118
+ describe('formatEndpointUrl() for gRPC protocol', () => {
119
+ it('should not append paths for gRPC', () => {
120
+ expect(formatEndpointUrl('api.honeycomb.io:443', 'traces', 'grpc')).toBe(
121
+ 'api.honeycomb.io:443',
122
+ );
123
+ });
124
+
125
+ it('should strip /v1/traces path from gRPC endpoints', () => {
126
+ expect(
127
+ formatEndpointUrl('api.example.com/v1/traces', 'traces', 'grpc'),
128
+ ).toBe('api.example.com');
129
+ });
130
+
131
+ it('should strip /v1/metrics path from gRPC endpoints', () => {
132
+ expect(
133
+ formatEndpointUrl('api.example.com/v1/metrics', 'metrics', 'grpc'),
134
+ ).toBe('api.example.com');
135
+ });
136
+
137
+ it('should strip /v1/logs path from gRPC endpoints', () => {
138
+ expect(
139
+ formatEndpointUrl('api.example.com/v1/logs', 'traces', 'grpc'),
140
+ ).toBe('api.example.com');
141
+ });
142
+
143
+ it('should strip paths without v1 prefix', () => {
144
+ expect(
145
+ formatEndpointUrl('api.example.com/traces', 'traces', 'grpc'),
146
+ ).toBe('api.example.com');
147
+ });
148
+
149
+ it('should handle gRPC URLs with grpc:// scheme', () => {
150
+ expect(formatEndpointUrl('grpc://localhost:4317', 'traces', 'grpc')).toBe(
151
+ 'grpc://localhost:4317',
152
+ );
153
+ });
154
+
155
+ it('should handle gRPC URLs with port numbers', () => {
156
+ expect(
157
+ formatEndpointUrl('collector.example.com:4317', 'traces', 'grpc'),
158
+ ).toBe('collector.example.com:4317');
159
+ });
160
+ });
161
+
162
+ describe('Edge cases', () => {
163
+ it('should handle empty endpoint for HTTP', () => {
164
+ expect(formatEndpointUrl('', 'traces', 'http')).toBe('/v1/traces');
165
+ });
166
+
167
+ it('should handle empty endpoint for gRPC', () => {
168
+ expect(formatEndpointUrl('', 'traces', 'grpc')).toBe('');
169
+ });
170
+
171
+ it('should preserve query parameters in HTTP', () => {
172
+ expect(
173
+ formatEndpointUrl('http://localhost:4318?foo=bar', 'traces', 'http'),
174
+ ).toBe('http://localhost:4318?foo=bar/v1/traces');
175
+ });
176
+
177
+ it('should preserve query parameters in gRPC', () => {
178
+ expect(
179
+ formatEndpointUrl('api.example.com:443?foo=bar', 'traces', 'grpc'),
180
+ ).toBe('api.example.com:443?foo=bar');
181
+ });
182
+ });
183
+ });
184
+
185
+ describe('Protocol configuration documentation', () => {
186
+ it('should document HTTP as default protocol', () => {
187
+ const defaultProtocol = resolveProtocol();
188
+ expect(defaultProtocol).toBe('http');
189
+ });
190
+
191
+ it('should document Honeycomb endpoint format', () => {
192
+ const honeycombEndpoint = 'api.honeycomb.io:443';
193
+ const formattedForGrpc = formatEndpointUrl(
194
+ honeycombEndpoint,
195
+ 'traces',
196
+ 'grpc',
197
+ );
198
+
199
+ expect(formattedForGrpc).toBe('api.honeycomb.io:443');
200
+ });
201
+
202
+ it('should document local collector HTTP format', () => {
203
+ const httpEndpoint = 'http://localhost:4318';
204
+ const formattedForHttp = formatEndpointUrl(httpEndpoint, 'traces', 'http');
205
+
206
+ expect(formattedForHttp).toBe('http://localhost:4318/v1/traces');
207
+ });
208
+
209
+ it('should document local collector gRPC format', () => {
210
+ const grpcEndpoint = 'localhost:4317';
211
+ const formattedForGrpc = formatEndpointUrl(grpcEndpoint, 'traces', 'grpc');
212
+
213
+ expect(formattedForGrpc).toBe('localhost:4317');
214
+ });
215
+ });