@ogcio/o11y-sdk-node 0.4.2 → 0.6.1

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 (140) hide show
  1. package/dist/sdk-core/index.d.ts +1 -0
  2. package/dist/sdk-core/index.js +1 -0
  3. package/dist/sdk-core/lib/index.d.ts +2 -0
  4. package/dist/sdk-core/lib/index.js +2 -0
  5. package/dist/sdk-core/lib/redaction/basic-redactor.d.ts +7 -0
  6. package/dist/sdk-core/lib/redaction/basic-redactor.js +18 -0
  7. package/dist/sdk-core/lib/redaction/email-redactor.d.ts +8 -0
  8. package/dist/sdk-core/lib/redaction/email-redactor.js +17 -0
  9. package/dist/sdk-core/lib/redaction/index.d.ts +9 -0
  10. package/dist/sdk-core/lib/redaction/index.js +4 -0
  11. package/dist/sdk-core/lib/redaction/ip-redactor.d.ts +9 -0
  12. package/dist/sdk-core/lib/redaction/ip-redactor.js +23 -0
  13. package/dist/sdk-core/lib/redaction/ppsn-redactor.d.ts +8 -0
  14. package/dist/sdk-core/lib/redaction/ppsn-redactor.js +17 -0
  15. package/dist/sdk-core/lib/utils/data-structures.d.ts +15 -0
  16. package/dist/{lib/internals/redaction/pii-detection.js → sdk-core/lib/utils/data-structures.js} +4 -27
  17. package/dist/sdk-core/lib/utils/index.d.ts +2 -0
  18. package/dist/sdk-core/lib/utils/index.js +2 -0
  19. package/dist/sdk-core/lib/utils/string-decoding.d.ts +7 -0
  20. package/dist/sdk-core/lib/utils/string-decoding.js +22 -0
  21. package/dist/{index.d.ts → sdk-node/index.d.ts} +1 -0
  22. package/dist/{index.js → sdk-node/index.js} +1 -0
  23. package/dist/{lib → sdk-node/lib}/config-manager.d.ts +1 -1
  24. package/dist/sdk-node/lib/exporter/console.d.ts +3 -0
  25. package/dist/sdk-node/lib/exporter/grpc.d.ts +3 -0
  26. package/dist/sdk-node/lib/exporter/http.d.ts +3 -0
  27. package/dist/{lib → sdk-node/lib}/exporter/index.d.ts +2 -2
  28. package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.d.ts +5 -5
  29. package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.js +6 -2
  30. package/dist/sdk-node/lib/exporter/processor-config.d.ts +7 -0
  31. package/dist/{lib → sdk-node/lib}/exporter/processor-config.js +4 -0
  32. package/dist/{lib → sdk-node/lib}/index.d.ts +11 -0
  33. package/dist/{lib → sdk-node/lib}/instrumentation.node.js +1 -1
  34. package/dist/sdk-node/lib/internals/redaction/redactors/email.d.ts +8 -0
  35. package/dist/sdk-node/lib/internals/redaction/redactors/email.js +19 -0
  36. package/dist/sdk-node/lib/internals/redaction/redactors/index.d.ts +16 -0
  37. package/dist/sdk-node/lib/internals/redaction/redactors/index.js +13 -0
  38. package/dist/sdk-node/lib/internals/redaction/redactors/ip.d.ts +8 -0
  39. package/dist/sdk-node/lib/internals/redaction/redactors/ip.js +20 -0
  40. package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.d.ts +8 -0
  41. package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.js +18 -0
  42. package/dist/{lib → sdk-node/lib}/metrics.d.ts +1 -1
  43. package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.d.ts +3 -3
  44. package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.d.ts +3 -3
  45. package/dist/sdk-node/lib/processor/nextjs-logger-processor.d.ts +7 -0
  46. package/dist/sdk-node/lib/processor/nextjs-logger-processor.js +30 -0
  47. package/dist/sdk-node/lib/processor/nextjs-span-processor.d.ts +8 -0
  48. package/dist/sdk-node/lib/processor/nextjs-span-processor.js +25 -0
  49. package/dist/{lib → sdk-node/lib}/resource.d.ts +2 -2
  50. package/dist/{lib → sdk-node/lib}/traces.d.ts +1 -1
  51. package/dist/{lib → sdk-node/lib}/traces.js +1 -1
  52. package/dist/{lib → sdk-node/lib}/url-sampler.d.ts +3 -3
  53. package/dist/{lib → sdk-node/lib}/utils.d.ts +1 -1
  54. package/dist/sdk-node/package.json +62 -0
  55. package/package.json +28 -25
  56. package/CHANGELOG.md +0 -233
  57. package/dist/lib/exporter/console.d.ts +0 -3
  58. package/dist/lib/exporter/grpc.d.ts +0 -3
  59. package/dist/lib/exporter/http.d.ts +0 -3
  60. package/dist/lib/exporter/processor-config.d.ts +0 -5
  61. package/dist/lib/internals/redaction/pii-detection.d.ts +0 -25
  62. package/dist/lib/internals/redaction/redactors/email.d.ts +0 -8
  63. package/dist/lib/internals/redaction/redactors/email.js +0 -48
  64. package/dist/lib/internals/redaction/redactors/index.d.ts +0 -4
  65. package/dist/lib/internals/redaction/redactors/index.js +0 -6
  66. package/dist/lib/internals/redaction/redactors/ip.d.ts +0 -10
  67. package/dist/lib/internals/redaction/redactors/ip.js +0 -54
  68. package/dist/lib/internals/shared-metrics.d.ts +0 -7
  69. package/dist/lib/internals/shared-metrics.js +0 -18
  70. package/dist/package.json +0 -59
  71. package/dist/vitest.config.d.ts +0 -2
  72. package/dist/vitest.config.js +0 -45
  73. package/index.ts +0 -9
  74. package/lib/config-manager.ts +0 -12
  75. package/lib/exporter/console.ts +0 -33
  76. package/lib/exporter/grpc.ts +0 -65
  77. package/lib/exporter/http.ts +0 -56
  78. package/lib/exporter/index.ts +0 -9
  79. package/lib/exporter/pii-exporter-decorator.ts +0 -187
  80. package/lib/exporter/processor-config.ts +0 -23
  81. package/lib/index.ts +0 -118
  82. package/lib/instrumentation.node.ts +0 -115
  83. package/lib/internals/hooks.ts +0 -14
  84. package/lib/internals/redaction/pii-detection.ts +0 -113
  85. package/lib/internals/redaction/redactors/email.ts +0 -58
  86. package/lib/internals/redaction/redactors/index.ts +0 -12
  87. package/lib/internals/redaction/redactors/ip.ts +0 -68
  88. package/lib/internals/shared-metrics.ts +0 -34
  89. package/lib/metrics.ts +0 -75
  90. package/lib/processor/enrich-logger-processor.ts +0 -34
  91. package/lib/processor/enrich-span-processor.ts +0 -39
  92. package/lib/resource.ts +0 -30
  93. package/lib/traces.ts +0 -78
  94. package/lib/url-sampler.ts +0 -52
  95. package/lib/utils.ts +0 -22
  96. package/test/config-manager.test.ts +0 -34
  97. package/test/exporter/pii-exporter-decorator.test.ts +0 -88
  98. package/test/index.test.ts +0 -70
  99. package/test/integration/README.md +0 -74
  100. package/test/integration/docker-utils.sh +0 -214
  101. package/test/integration/main.sh +0 -52
  102. package/test/integration/teardown.sh +0 -7
  103. package/test/integration/test_fastify-o11y-pii-enabled/http-tracing.integration.test.ts +0 -56
  104. package/test/integration/test_fastify-o11y-pii-enabled/pii.integration.test.ts +0 -68
  105. package/test/integration/test_fastify-o11y-pii-enabled/run.sh +0 -42
  106. package/test/integration/test_without-o11y/run.sh +0 -30
  107. package/test/integration/test_without-o11y/verify-status.integration.test.ts +0 -32
  108. package/test/internals/hooks.test.ts +0 -45
  109. package/test/internals/pii-detection.test.ts +0 -265
  110. package/test/internals/redactors/email.test.ts +0 -81
  111. package/test/internals/redactors/ip.test.ts +0 -93
  112. package/test/internals/shared-metrics.test.ts +0 -34
  113. package/test/metrics.test.ts +0 -142
  114. package/test/node-config.test.ts +0 -190
  115. package/test/processor/enrich-logger-processor.test.ts +0 -58
  116. package/test/processor/enrich-span-processor.test.ts +0 -52
  117. package/test/resource.test.ts +0 -33
  118. package/test/traces/active-span.test.ts +0 -26
  119. package/test/traces/with-span.test.ts +0 -356
  120. package/test/url-sampler.test.ts +0 -215
  121. package/test/utils/alloy-log-parser.ts +0 -53
  122. package/test/utils/mock-signals.ts +0 -144
  123. package/test/validation.test.ts +0 -103
  124. package/tsconfig.json +0 -15
  125. package/vitest.config.ts +0 -46
  126. /package/dist/{lib → sdk-node/lib}/config-manager.js +0 -0
  127. /package/dist/{lib → sdk-node/lib}/exporter/console.js +0 -0
  128. /package/dist/{lib → sdk-node/lib}/exporter/grpc.js +0 -0
  129. /package/dist/{lib → sdk-node/lib}/exporter/http.js +0 -0
  130. /package/dist/{lib → sdk-node/lib}/exporter/index.js +0 -0
  131. /package/dist/{lib → sdk-node/lib}/index.js +0 -0
  132. /package/dist/{lib → sdk-node/lib}/instrumentation.node.d.ts +0 -0
  133. /package/dist/{lib → sdk-node/lib}/internals/hooks.d.ts +0 -0
  134. /package/dist/{lib → sdk-node/lib}/internals/hooks.js +0 -0
  135. /package/dist/{lib → sdk-node/lib}/metrics.js +0 -0
  136. /package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.js +0 -0
  137. /package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.js +0 -0
  138. /package/dist/{lib → sdk-node/lib}/resource.js +0 -0
  139. /package/dist/{lib → sdk-node/lib}/url-sampler.js +0 -0
  140. /package/dist/{lib → sdk-node/lib}/utils.js +0 -0
package/lib/resource.ts DELETED
@@ -1,30 +0,0 @@
1
- import {
2
- ResourceDetector,
3
- DetectedResource,
4
- DetectedResourceAttributes,
5
- } from "@opentelemetry/resources";
6
- import { SignalAttributeValue } from "./index.js";
7
- import packageJson from "../package.json" with { type: "json" };
8
-
9
- export class ObservabilityResourceDetector implements ResourceDetector {
10
- private _resourceAttributes: Record<string, SignalAttributeValue> | undefined;
11
-
12
- constructor(resourceAttributes?: Record<string, SignalAttributeValue>) {
13
- this._resourceAttributes = resourceAttributes;
14
- }
15
-
16
- detect(): DetectedResource {
17
- let attributes: DetectedResourceAttributes = {};
18
-
19
- if (this._resourceAttributes) {
20
- attributes = {
21
- ...this._resourceAttributes,
22
- };
23
- }
24
-
25
- attributes["o11y.sdk.name"] = packageJson.name;
26
- attributes["o11y.sdk.version"] = packageJson.version;
27
-
28
- return { attributes };
29
- }
30
- }
package/lib/traces.ts DELETED
@@ -1,78 +0,0 @@
1
- import { Span, SpanOptions, SpanStatusCode, trace } from "@opentelemetry/api";
2
- import { getNodeSdkConfig } from "./config-manager.js";
3
-
4
- export type WithSpanParams<T> = {
5
- /**
6
- * The name of the trace the span should belong to.
7
- * NOTE: If you want the new span to belong to an already existing trace, you should provide the same tracer name
8
- */
9
- traceName?: string;
10
- spanName: string;
11
- spanOptions?: SpanOptions;
12
- /** A function defining the task you want to be wrapped by this span */
13
- fn: (span: Span) => T | Promise<T>;
14
- };
15
-
16
- /**
17
- * Generates a function wrapping a given Callable `fn` into an error handling block.
18
- * Setting Span status and recording any caught exception before bubbling it up.
19
- *
20
- * Marks the span as ended once the provided callable has ended or an error has been caught.
21
- *
22
- * @returns {Promise<T>} where T is the type returned by the Callable.
23
- * @throws any error thrown by the original Callable `fn` provided.
24
- */
25
- function selfContainedSpanHandlerGenerator<T>(
26
- fn: (span: Span) => T | Promise<T>,
27
- ): (span: Span) => Promise<T> {
28
- return async (span: Span) => {
29
- try {
30
- const fnResult = await fn(span);
31
- span.setStatus({ code: SpanStatusCode.OK });
32
- return fnResult;
33
- } catch (err) {
34
- if (err instanceof Error) {
35
- span.recordException(err);
36
- span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
37
- throw err;
38
- }
39
-
40
- span.recordException({ message: JSON.stringify(err) });
41
- span.setStatus({
42
- code: SpanStatusCode.ERROR,
43
- message: JSON.stringify(err),
44
- });
45
- throw err;
46
- } finally {
47
- span.end();
48
- }
49
- };
50
- }
51
-
52
- /**
53
- * Gets the currently active OpenTelemetry span.
54
- *
55
- * @returns {Span | undefined} The active span with redaction logic applied,
56
- * or `undefined` if there is no active span in context.
57
- */
58
- export function getActiveSpan() {
59
- return trace.getActiveSpan();
60
- }
61
-
62
- export function withSpan<T>({
63
- traceName,
64
- spanName,
65
- spanOptions = {},
66
- fn,
67
- }: WithSpanParams<T>) {
68
- const sdkConfig = getNodeSdkConfig();
69
- const tracer = trace.getTracer(
70
- traceName ?? sdkConfig?.serviceName ?? "o11y-sdk",
71
- sdkConfig?.serviceVersion,
72
- );
73
- return tracer.startActiveSpan(
74
- spanName,
75
- spanOptions,
76
- selfContainedSpanHandlerGenerator<T>(fn),
77
- );
78
- }
@@ -1,52 +0,0 @@
1
- import { Attributes, Context, Link, SpanKind } from "@opentelemetry/api";
2
- import {
3
- Sampler,
4
- SamplingDecision,
5
- SamplingResult,
6
- } from "@opentelemetry/sdk-trace-base";
7
- import { SamplerCondition } from "./index.js";
8
-
9
- export class UrlSampler implements Sampler {
10
- private _samplerCondition: SamplerCondition[];
11
- private _nextSampler: Sampler;
12
-
13
- constructor(samplerCondition: SamplerCondition[] = [], nextSampler: Sampler) {
14
- this._samplerCondition = samplerCondition;
15
- this._nextSampler = nextSampler;
16
- }
17
-
18
- shouldSample(
19
- _context: Context,
20
- traceId: string,
21
- _spanName: string,
22
- _spanKind: SpanKind,
23
- attributes: Attributes,
24
- _links: Link[],
25
- ): SamplingResult {
26
- const url: string | undefined = attributes["http.target"]?.toString();
27
-
28
- if (url) {
29
- for (const condition of this._samplerCondition) {
30
- if (
31
- (condition.type === "equals" && url === condition.url) ||
32
- (condition.type === "endsWith" && url.endsWith(condition.url)) ||
33
- (condition.type === "includes" && url.includes(condition.url))
34
- ) {
35
- return { decision: SamplingDecision.NOT_RECORD };
36
- }
37
- }
38
- }
39
-
40
- return this._nextSampler.shouldSample(
41
- _context,
42
- traceId,
43
- _spanName,
44
- _spanKind,
45
- attributes,
46
- _links,
47
- );
48
- }
49
- toString(): string {
50
- return "UrlSampler";
51
- }
52
- }
package/lib/utils.ts DELETED
@@ -1,22 +0,0 @@
1
- import {
2
- BatchLogRecordProcessor,
3
- SimpleLogRecordProcessor,
4
- } from "@opentelemetry/sdk-logs";
5
- import { SDKCollectorMode } from "./index.js";
6
- import { tracing } from "@opentelemetry/sdk-node";
7
-
8
- export const LogRecordProcessorMap: Record<
9
- SDKCollectorMode,
10
- typeof SimpleLogRecordProcessor | typeof BatchLogRecordProcessor
11
- > = {
12
- single: SimpleLogRecordProcessor,
13
- batch: BatchLogRecordProcessor,
14
- };
15
-
16
- export const SpanProcessorMap: Record<
17
- SDKCollectorMode,
18
- typeof tracing.SimpleSpanProcessor | typeof tracing.BatchSpanProcessor
19
- > = {
20
- single: tracing.SimpleSpanProcessor,
21
- batch: tracing.BatchSpanProcessor,
22
- };
@@ -1,34 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { getNodeSdkConfig, setNodeSdkConfig } from "../lib/config-manager";
3
- import { NodeSDKConfig } from "../lib";
4
-
5
- describe("Config Manager", () => {
6
- it("does not throw if getConfig is called before initialization", () => {
7
- expect(() => getNodeSdkConfig()).not.toThrow();
8
- });
9
-
10
- it("sdk defined config is not pollutable", () => {
11
- const config: NodeSDKConfig = {
12
- collectorUrl: "http://example.com",
13
- serviceName: "MyService",
14
- spanAttributes: {
15
- "my.attribute": "value",
16
- },
17
- };
18
-
19
- setNodeSdkConfig(config);
20
-
21
- const cfg = getNodeSdkConfig();
22
-
23
- // Top level
24
- cfg.collectorUrl = "http://example.com/changed";
25
- // Subfield
26
- cfg.spanAttributes["my.attribute"] = "another-attribute";
27
-
28
- // Ensure config values remain unchanged
29
- expect(getNodeSdkConfig().collectorUrl).toStrictEqual(config.collectorUrl);
30
- expect(getNodeSdkConfig().spanAttributes["my.attribute"]).toStrictEqual(
31
- config.spanAttributes["my.attribute"],
32
- );
33
- });
34
- });
@@ -1,88 +0,0 @@
1
- import { ReadableLogRecord } from "@opentelemetry/sdk-logs";
2
- import { ResourceMetrics } from "@opentelemetry/sdk-metrics";
3
- import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
4
- import { beforeEach, describe, expect, it, vi } from "vitest";
5
- import { PIIExporterDecorator } from "../../lib/exporter/pii-exporter-decorator";
6
-
7
- describe("PIIExporterDecorator", () => {
8
- let exporterMock: any;
9
- let config: any;
10
- let piiExporter: PIIExporterDecorator;
11
-
12
- beforeEach(() => {
13
- exporterMock = {
14
- export: vi.fn(),
15
- shutdown: vi.fn(() => Promise.resolve()),
16
- forceFlush: vi.fn(() => Promise.resolve()),
17
- _delegate: {},
18
- };
19
-
20
- config = { detection: { email: true } };
21
- piiExporter = new PIIExporterDecorator(exporterMock, config);
22
- });
23
-
24
- it("should redact emails in span name and attributes", () => {
25
- const items: ReadableSpan[] = [
26
- {
27
- name: "user@example.com",
28
- kind: 0,
29
- spanContext: () => ({}),
30
- attributes: { email: "user@example.com" },
31
- resource: { attributes: { owner: "user@example.com" } },
32
- events: [
33
- {
34
- name: "Login from user@example.com",
35
- attributes: { email: "user@example.com" },
36
- },
37
- ],
38
- } as any,
39
- ];
40
-
41
- const callback = vi.fn();
42
- piiExporter.export(items, callback);
43
-
44
- const exportedSpan = exporterMock.export.mock.calls[0][0][0];
45
- expect(exportedSpan.name).toBe("[REDACTED EMAIL]");
46
- expect(exportedSpan.attributes.email).toBe("[REDACTED EMAIL]");
47
- expect(exportedSpan.resource.attributes.owner).toBe("[REDACTED EMAIL]");
48
- expect(exportedSpan.events[0].name).toBe("Login from [REDACTED EMAIL]");
49
- expect(exportedSpan.events[0].attributes.email).toBe("[REDACTED EMAIL]");
50
- });
51
-
52
- it("should redact emails in log records", () => {
53
- const items: ReadableLogRecord[] = [
54
- {
55
- body: "Error from user@example.com",
56
- attributes: { email: "user@example.com" },
57
- severityText: "INFO",
58
- severityNumber: 1,
59
- resource: { attributes: { owner: "user@example.com" } },
60
- } as any,
61
- ];
62
-
63
- const callback = vi.fn();
64
- piiExporter.export(items, callback);
65
-
66
- const exportedLog = exporterMock.export.mock.calls[0][0][0];
67
- expect(exportedLog.body).toBe("Error from [REDACTED EMAIL]");
68
- expect(exportedLog.attributes.email).toBe("[REDACTED EMAIL]");
69
- expect(exportedLog.resource.attributes.owner).toBe("[REDACTED EMAIL]");
70
- });
71
-
72
- it("should redact emails in resource metrics", () => {
73
- const metrics: ResourceMetrics = {
74
- resource: {
75
- attributes: { maintainer: "user@example.com" },
76
- },
77
- scopeMetrics: [],
78
- } as any;
79
-
80
- const callback = vi.fn();
81
- piiExporter.export(metrics, callback);
82
-
83
- const exportedMetric = exporterMock.export.mock.calls[0][0];
84
- expect(exportedMetric.resource.attributes.maintainer).toBe(
85
- "[REDACTED EMAIL]",
86
- );
87
- });
88
- });
@@ -1,70 +0,0 @@
1
- import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { NodeSDKConfig } from "../index";
3
- import { instrumentNode } from "../index";
4
- import * as buildNodeInstrumentationModule from "../lib/instrumentation.node";
5
- import { metrics } from "@opentelemetry/sdk-node";
6
-
7
- describe("instrumentNode", () => {
8
- beforeEach(() => {
9
- // @ts-ignore Avoid actually running exporters at any time in tests (overriding private method)
10
- vi.spyOn(
11
- metrics.PeriodicExportingMetricReader.prototype,
12
- "_doRun",
13
- ).mockImplementation(vi.fn());
14
- });
15
-
16
- afterEach(() => {
17
- vi.restoreAllMocks();
18
- });
19
-
20
- test("should call buildNodeInstrumentation with the provided config", async () => {
21
- const instrumentationMock = vi
22
- .spyOn(buildNodeInstrumentationModule, "default")
23
- .mockImplementation(vi.fn());
24
-
25
- const config: NodeSDKConfig = {
26
- serviceName: "custom-service",
27
- collectorUrl: "http://custom-collector.com",
28
- protocol: "grpc",
29
- resourceAttributes: {
30
- "team.infra.cluster": "dev-01",
31
- "team.infra.pod": "01",
32
- "team.service.type": "fastify",
33
- },
34
- spanAttributes: {
35
- "signal.namespace": "example",
36
- "signal.number": () => "callback",
37
- },
38
- };
39
-
40
- await instrumentNode(config);
41
-
42
- expect(instrumentationMock).toHaveBeenCalledWith(config);
43
- });
44
-
45
- test("should not throw when called without arguments", async () => {
46
- await expect(instrumentNode()).resolves.not.toThrow();
47
- });
48
-
49
- test("should invoke instrumentation shutdown on SIGTERM", async () => {
50
- const config: NodeSDKConfig = {
51
- serviceName: "custom-service",
52
- collectorUrl: "http://custom-collector.com",
53
- protocol: "grpc",
54
- resourceAttributes: {
55
- "team.infra.cluster": "dev-01",
56
- "team.infra.pod": "01",
57
- "team.service.type": "fastify",
58
- },
59
- spanAttributes: {
60
- "signal.namespace": "example",
61
- "signal.number": () => "callback",
62
- },
63
- };
64
-
65
- const sdk = await instrumentNode(config);
66
- const shutdownMock = vi.spyOn(sdk, "shutdown");
67
- process.emit("SIGTERM");
68
- expect(shutdownMock).toHaveBeenCalled();
69
- });
70
- });
@@ -1,74 +0,0 @@
1
- # Integration Test Runner
2
-
3
- This script is a **Bash-based test runner** that discovers and executes integration test scripts for the Node SDK (`./packages/sdk-node/test/integration`).
4
- It ensures tests run inside an isolated Docker network and provides a summary of results.
5
-
6
- ---
7
-
8
- ## Usage
9
-
10
- ```bash
11
- ./run-tests.sh <build-id> <root-path> <is-ci?>
12
- ```
13
-
14
- **Arguments**
15
-
16
- <build-id>
17
- A unique identifier for the test run. Used to name the Docker network (<build-id>_testnetwork).
18
-
19
- <root-path>
20
- The root path for the project or build environment.
21
-
22
- [ci-flag] (optional)
23
- Defaults to false.
24
- If set to true, the script will remove all Docker images after the tests complete (intended for CI pipelines to save disk space).
25
-
26
- ## How It Works
27
-
28
- - Ensures at least two arguments are provided. Exits with usage instructions otherwise.
29
- - Creates a temporary Docker network named <build-id>\_testnetwork.
30
- - Finds all integration test scripts under: ./packages/sdk-node/test/integration/test\*/run.sh
31
- - Runs each test script.
32
- - Captures exit codes and tracks failures.
33
- - Prints results per test.
34
- - Removes the Docker network after all tests finish.
35
- - If running in CI mode (ci-flag = true), removes all Docker images.
36
- - Reports whether all tests passed or how many failed.
37
- Exits with:
38
- - 0 if all tests succeeded
39
- - 1 if one or more tests failed
40
-
41
- **Example**
42
-
43
- ```
44
- # Run tests locally
45
- ./run-tests.sh my-build-id /path/to/project
46
-
47
- # Run tests in CI mode (cleanup Docker images after)
48
- ./run-tests.sh my-build-id /path/to/project true
49
- ```
50
-
51
- ## Tests
52
-
53
- ### test_fastify-o11y-pii-enabled
54
-
55
- The `run.sh` script performs the following steps:
56
-
57
- - build a docker image of a fastify app `/examples/fastify`
58
- - run grafana alloy inside a docker container with a test configuration `/alloy/integration-test.alloy`
59
- - ensure is running otherwise exit process
60
- - run fastify app in a docker container
61
- - ensure is running otherwise exit process
62
- - execute some curl to the fastify microservice
63
- - persist alloy log to a file and save to following path `/packages/sdk-node/test/integration/`
64
- - verify pii data are removed and metrics are increased
65
- - docker turn down process (containers/network/image)
66
-
67
- ### test_without-o11y
68
-
69
- The `run.sh` script performs the following steps:
70
-
71
- - build a docker image of a fastify app `/examples/fastify`
72
- - verify service execution with o11y sdk disabled (otel collector empty)
73
- - execute http requests for dummy route with otel trace customization function calls
74
- - assert 200 http status without any error
@@ -1,214 +0,0 @@
1
- #!/bin/bash
2
-
3
- : '
4
- Build a new docker image of a given Dockerfile, if already exists skip build.
5
-
6
- @param container_name docker container name
7
-
8
- @return int error code
9
- 0. success
10
- 1. validation error
11
- '
12
- build_image() {
13
- if [ "$#" -lt 3 ] || [ "$#" -gt 4 ]; then
14
- echo "Error: build_image expected 3 or 4 parameters, got $#." >&2
15
- echo "Usage: build_image <image_name> <dockerfile_path> <context_path> [max_age_minutes]" >&2
16
- return 1
17
- fi
18
-
19
-
20
- local image_name="$1"
21
- local dockerfile_path="$2"
22
- local context_path="$3"
23
- local max_age_minutes="${4:-30}" # default threshold = 30 minutes
24
-
25
- if docker image inspect "$image_name" > /dev/null 2>&1; then
26
- echo "Image $image_name already exists. Checking age..."
27
-
28
- # Extract the Created timestamp from docker inspect (ISO 8601 format)
29
- local created_at
30
- created_at=$(docker image inspect --format '{{.Created}}' "$image_name")
31
- if [ -z "$created_at" ]; then
32
- echo "Warning: Could not determine creation date. Proceeding without rebuild check." >&2
33
- return 0
34
- fi
35
-
36
- # Convert created_at to epoch seconds
37
- local created_epoch
38
- created_epoch=$(date -d "$created_at" +%s 2>/dev/null)
39
-
40
- # Current time in epoch seconds
41
- local now_epoch
42
- now_epoch=$(date +%s)
43
-
44
- # Calculate age in minutes
45
- local age_minutes=$(( (now_epoch - created_epoch) / 60 ))
46
-
47
- echo "Image $image_name is $age_minutes minutes old (threshold: $max_age_minutes minutes)."
48
-
49
- if [ "$age_minutes" -ge "$max_age_minutes" ]; then
50
- echo "Image is too old. Rebuilding..."
51
- docker build -t $image_name -f $dockerfile_path $context_path
52
- return $?
53
- else
54
- echo "Image is recent. Skipping rebuild."
55
- return 0
56
- fi
57
- else
58
- echo "Image $image_name not found. Building..."
59
- docker build -t "$image_name" -f "$dockerfile_path" "$context_path"
60
- return $?
61
- fi
62
- }
63
-
64
- : '
65
- Stop and remove a docker container for the given input container name.
66
-
67
- @param container_name docker container name
68
-
69
- @return int error code
70
- 0. success
71
- 1. validation error
72
- '
73
- container_stop_and_rm() {
74
- local container_name="$1"
75
-
76
- if [[ -z "$container_name" ]]; then
77
- echo "Error: container_name must have a value."
78
- return 1
79
- fi
80
-
81
- docker container stop $container_name
82
- docker container rm -f $container_name
83
-
84
- return $?
85
- }
86
-
87
- : '
88
- Run a new alloy container and check if the status is running.
89
-
90
- @param root_path volume root path for alloy file config
91
- @param network_name docker network name
92
- @param container_name docker container name
93
-
94
- @optional max_retries
95
- @default 10
96
-
97
- @return int error code
98
- 0. success
99
- 1. validation error
100
- 2. execution failed
101
- '
102
- run_and_check_alloy() {
103
-
104
- if [ "$#" -lt 3 ]; then
105
- echo "Error: run_and_check_alloy expected 3 parameters, got $#." >&2
106
- return 1
107
- fi
108
-
109
- local root_path="$1"
110
- local network_name="$2"
111
- local container_name="$3"
112
- local max_retries="${4:-10}"
113
- local counter_retries=0
114
-
115
- if [[ -z "$root_path" || -z "$network_name" || -z "$container_name" ]]; then
116
- echo "Error: root_path, network_name, and container_name must all have values."
117
- return 1
118
- fi
119
-
120
- if ! [[ "$max_retries" =~ ^[0-9]+$ ]]; then
121
- echo "Error: max_retries parameter must be a number." >&2
122
- return 1
123
- fi
124
-
125
- docker run -d \
126
- -v "$root_path/alloy/integration-test.alloy:/etc/alloy/config.alloy:ro" \
127
- --network $network_name \
128
- --name $container_name \
129
- -p 4317:4317 \
130
- -p 4318:4318 \
131
- grafana/alloy:v1.9.2 \
132
- run --server.http.listen-addr=0.0.0.0:12345 --stability.level=experimental /etc/alloy/config.alloy
133
-
134
- echo "$container_name container status"
135
- until [ "$(docker inspect -f {{.State.Running}} $container_name)" = true ]; do
136
- sleep 2
137
- docker inspect -f {{.State.Running}} $container_name
138
- counter_retries=$((counter_retries + 1))
139
- if [ $counter_retries -ge $max_retries ]; then
140
- echo "Exceeded maximum retries. Exiting."
141
- return 2
142
- fi
143
- done
144
-
145
- return 0
146
- }
147
-
148
- : '
149
- Run a new fastify microservice container and check if the status is running.
150
-
151
- @param network_name docker network name
152
- @param collector_url otel collector grpc endpoint
153
- @param container_name docker container name
154
- @param build_id current pipeline build id
155
-
156
- @optional max_retries
157
- @default 10
158
-
159
- @return int error code
160
- 0. success
161
- 1. validation error
162
- 2. execution failed
163
- '
164
- run_and_check_fastify() {
165
-
166
- if [ "$#" -lt 4 ]; then
167
- echo "Error: run_and_check_alloy expected 3 parameters, got $#." >&2
168
- return 1
169
- fi
170
-
171
- local network_name="$1"
172
- local collector_url="$2"
173
- local container_name="$3"
174
- local build_id="$4"
175
- local max_retries="${5:-10}"
176
- local counter_retries=0
177
-
178
- if [[ -z "$build_id" || -z "$network_name" || -z "$container_name" ]]; then
179
- echo "Error: build_id, network_name, and container_name must all have values."
180
- return 1
181
- fi
182
-
183
- if ! [[ "$max_retries" =~ ^[0-9]+$ ]]; then
184
- echo "Error: max_retries parameter must be a number." >&2
185
- return 1
186
- fi
187
-
188
- docker run --detach \
189
- --network $network_name \
190
- --name $container_name \
191
- -e DB_DISABLED="true" \
192
- -e SERVER_HOST="0.0.0.0" \
193
- -e OTEL_COLLECTOR_URL=$collector_url \
194
- --health-cmd="curl -f http://${container_name}:9091/api/health > /dev/null || exit 1" \
195
- --health-start-period=1s \
196
- --health-retries=10 \
197
- --health-interval=1s \
198
- -p 9091:9091 \
199
- $container_name:$build_id
200
-
201
- counter_retries=0
202
- echo "$container_name container status"
203
- until [ "$(docker inspect -f {{.State.Health.Status}} $container_name)" = "healthy" ]; do
204
- sleep 1
205
- docker inspect -f {{.State.Health.Status}} $container_name
206
- counter_retries=$((counter_retries + 1))
207
- if [ $counter_retries -ge $max_retries ]; then
208
- echo "Exceeded maximum retries. Exiting."
209
- return 2
210
- fi
211
- done
212
-
213
- return 0
214
- }