Skip to content

Commit

Permalink
chore: api tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Joozty committed Jan 8, 2025
1 parent 0c5c251 commit 3129228
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 45 deletions.
7 changes: 6 additions & 1 deletion packages/web/src/exporters/otlp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@

import { diag } from '@opentelemetry/api'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { NOOP_ATTRIBUTES_TRANSFORMER, NATIVE_XHR_SENDER, NATIVE_BEACON_SENDER, SplunkExporterConfig } from './common'
import {
NOOP_ATTRIBUTES_TRANSFORMER,
NATIVE_XHR_SENDER,
NATIVE_BEACON_SENDER,
type SplunkExporterConfig,
} from './common'
import { ReadableSpan } from '@opentelemetry/sdk-trace-base'

export class SplunkOTLPTraceExporter extends OTLPTraceExporter {
Expand Down
7 changes: 6 additions & 1 deletion packages/web/src/exporters/zipkin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import {
import { ExportResult, ExportResultCode } from '@opentelemetry/core'
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'
import { limitLen } from '../utils'
import { NOOP_ATTRIBUTES_TRANSFORMER, NATIVE_XHR_SENDER, NATIVE_BEACON_SENDER, SplunkExporterConfig } from './common'
import {
NOOP_ATTRIBUTES_TRANSFORMER,
NATIVE_XHR_SENDER,
NATIVE_BEACON_SENDER,
type SplunkExporterConfig,
} from './common'

const MAX_VALUE_LIMIT = 4096
const SERVICE_NAME = 'browser'
Expand Down
4 changes: 2 additions & 2 deletions packages/web/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
DEFAULT_AUTO_INSTRUMENTED_EVENT_NAMES,
UserInteractionEventsConfig,
} from './SplunkUserInteractionInstrumentation'
import { SplunkExporterConfig } from './exporters/common'
import { type SplunkExporterConfig } from './exporters/common'
import { SplunkZipkinExporter } from './exporters/zipkin'
import { ERROR_INSTRUMENTATION_NAME, SplunkErrorInstrumentation } from './SplunkErrorInstrumentation'
import { generateId, getPluginConfig } from './utils'
Expand Down Expand Up @@ -67,7 +67,7 @@ import { BrowserInstanceService } from './services/BrowserInstanceService'
import { SessionId } from './session'
import { SplunkOtelWebConfig, SplunkOtelWebExporterOptions, SplunkOtelWebOptionsInstrumentations } from './types'

export { SplunkExporterConfig } from './exporters/common'
export { type SplunkExporterConfig } from './exporters/common'
export { SplunkZipkinExporter } from './exporters/zipkin'
export * from './SplunkWebTracerProvider'
export * from './SessionBasedSampler'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright 2024 Splunk Inc.
* Copyright 2025 Splunk Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,12 +16,12 @@
*
*/

import { expect } from 'chai'
import { context, trace, SpanStatusCode } from '@opentelemetry/api'

import SplunkOtelWeb, { INSTRUMENTATIONS_ALL_DISABLED } from '../src/index'
import SplunkOtelWeb, { INSTRUMENTATIONS_ALL_DISABLED } from '../index'
import { SpanCapturer } from './utils'
import { SpanProcessor } from '@opentelemetry/sdk-trace-base'

import { describe, it, expect, beforeEach, afterEach } from 'vitest'

// note: we've added these tests mainly to keep track of substantial changes in the Open Telemetry API
describe('Transitive API', () => {
Expand All @@ -36,107 +36,127 @@ describe('Transitive API', () => {
})

spanCapturer = new SpanCapturer()
SplunkOtelWeb.provider?.addSpanProcessor(spanCapturer as any as SpanProcessor)
SplunkOtelWeb.provider?.addSpanProcessor(spanCapturer)
})

afterEach(() => {
SplunkOtelWeb.deinit()
})

describe('Tracer', () => {
function subject() {
return SplunkOtelWeb.provider.getTracer('test')
function getTestTracer() {
const testTracer = SplunkOtelWeb.provider?.getTracer('test')
if (!testTracer) {
throw new TypeError('Tracer not found')
}

return testTracer
}

it('should return a tracer', () => {
const tracer = subject()
expect(typeof tracer.startSpan).to.eql('function')
expect(typeof tracer.getActiveSpanProcessor).to.eql('function')
const tracer = getTestTracer()
expect(typeof tracer.startSpan).toBe('function')
expect(typeof tracer.getActiveSpanProcessor).toBe('function')
})

it('can start a span', () => {
const span = subject().startSpan('span.test')
const span = getTestTracer().startSpan('span.test')

expect(typeof span.end).to.eql('function')
expect(typeof span.spanContext).to.eql('function')
expect(typeof span.recordException).to.eql('function')
expect(typeof span.setAttributes).to.eql('function')
expect(typeof span.setStatus).to.eql('function')
expect(typeof span.end).toBe('function')
expect(typeof span.spanContext).toBe('function')
expect(typeof span.recordException).toBe('function')
expect(typeof span.setAttributes).toBe('function')
expect(typeof span.setStatus).toBe('function')

span.end()
})
})

describe('Span', () => {
const startTime = new Date(2021, 1, 1, 0, 0, 0, 0)
function subject() {
return SplunkOtelWeb.provider.getTracer('test').startSpan('test.span', { startTime })
function getTestSpan() {
const testTracer = SplunkOtelWeb.provider?.getTracer('test')
if (!testTracer) {
throw new TypeError('Tracer not found')
}

return testTracer.startSpan('test.span', { startTime })
}

it('can set duration', () => {
const span = subject()
const span = getTestSpan()
span.end(new Date(2021, 1, 1, 0, 1, 1, 0))

expect(spanCapturer.spans[0].duration).to.deep.eq([61, 0])
expect(spanCapturer.spans[0].duration).toStrictEqual([61, 0])
})

it('can set attributes', () => {
const span = subject()
const span = getTestSpan()
span.setAttributes({
attr1: 'val1',
attr2: 'val2',
})
span.end()

expect(spanCapturer.spans[0].attributes['attr1']).to.eq('val1')
expect(spanCapturer.spans[0].attributes['attr2']).to.eq('val2')
expect(spanCapturer.spans[0].attributes['attr1']).toBe('val1')
expect(spanCapturer.spans[0].attributes['attr2']).toBe('val2')
})

it('can set status', () => {
const span = subject()
const span = getTestSpan()
span.setStatus({ code: SpanStatusCode.UNSET })
span.setStatus({ code: SpanStatusCode.OK })
span.setStatus({ code: SpanStatusCode.ERROR })
span.end()

expect(spanCapturer.spans[0].status.code).to.eq(SpanStatusCode.ERROR)
expect(spanCapturer.spans[0].status.code).toBe(SpanStatusCode.ERROR)
})

it('can set record an exception', () => {
const span = subject()
const span = getTestSpan()
class CustomError extends Error {}
span.recordException(new CustomError('Exception to be recorded.'))
span.end()

expect(spanCapturer.spans[0].status.code).to.eq(SpanStatusCode.UNSET)
expect(spanCapturer.spans[0].events[0].name).to.eq('exception')
expect(spanCapturer.spans[0].events[0].attributes['exception.type']).to.eq('Error')
expect(spanCapturer.spans[0].events[0].attributes['exception.message']).to.eq('Exception to be recorded.')
expect(spanCapturer.spans[0].status.code).toBe(SpanStatusCode.UNSET)
expect(spanCapturer.spans[0].events[0].name).toBe('exception')
expect(spanCapturer.spans[0].events[0]?.attributes?.['exception.type']).toBe('Error')
expect(spanCapturer.spans[0].events[0]?.attributes?.['exception.message']).toBe('Exception to be recorded.')
})
})

describe('api.context', () => {
it('can set span as active', () => {
const tracer = SplunkOtelWeb.provider.getTracer('test')
const span = tracer.startSpan('test-span')
const tracer = SplunkOtelWeb.provider?.getTracer('test')
const span = tracer?.startSpan('test-span')

if (!span) {
throw new TypeError('Span not found')
}

context.with(trace.setSpan(context.active(), span), () => {
expect(trace.getSpan(context.active())).to.eq(span)
expect(trace.getSpan(context.active())).toBe(span)
})
})

it('can create a child of an active span', () => {
const tracer = SplunkOtelWeb.provider.getTracer('test')
const span = tracer.startSpan('test-span')
const tracer = SplunkOtelWeb.provider?.getTracer('test')
const span = tracer?.startSpan('test-span')

if (!span || !tracer) {
throw new TypeError('Tracer or Span not found')
}

context.with(trace.setSpan(context.active(), span), () => {
tracer.startSpan('child-span').end()
})
span.end()

expect(spanCapturer.spans).to.have.length(2)
expect(spanCapturer.spans).toHaveLength(2)

const [childSpan, parentSpan] = spanCapturer.spans
expect(childSpan.parentSpanId).to.eq(parentSpan.spanContext().spanId)
expect(childSpan.spanContext().traceId).to.eq(parentSpan.spanContext().traceId)
expect(childSpan.parentSpanId).toBe(parentSpan.spanContext().spanId)
expect(childSpan.spanContext().traceId).toBe(parentSpan.spanContext().traceId)
})
})
})
41 changes: 41 additions & 0 deletions packages/web/src/tests/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
*
* Copyright 2025 Splunk Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base'

export class SpanCapturer implements SpanProcessor {
public readonly spans: ReadableSpan[] = []

clear(): void {
this.spans.length = 0
}

forceFlush(): Promise<void> {
return Promise.resolve()
}

onEnd(span: ReadableSpan): void {
this.spans.push(span)
}

onStart(): void {}

shutdown(): Promise<void> {
return Promise.resolve()
}
}
3 changes: 0 additions & 3 deletions packages/web/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,10 @@ import 'mocha'

// Manually maintain this list, as old webpack require-based mechanism isn't working under rollup
import './init.test'
import '../src/servertiming.test'
import '../src/utils.test'
import './session.test'
import './websockets.test'
import './SessionBasedSampler.test'
import './SplunkExporter.test'
import './api.test'
import './SplunkContextManager.test'
import './SplunkSpanAttributesProcessor.test'
import './SplunkOtelWeb.test'
Expand Down

0 comments on commit 3129228

Please sign in to comment.