Skip to content

Commit

Permalink
Enforce 95% Coverage Requirement for lines and functions (#24)
Browse files Browse the repository at this point in the history
*Issue #, if available:*

*Description of changes:*
- Add `npm run test:coverage` command to be used in PR Workflow build to
enforce `95%` line and function coverage
- Increase code test coverage to 95% for line and function coverage.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
  • Loading branch information
jj22ee authored Aug 21, 2024
1 parent c46b0c6 commit c9278ee
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Compile all NPM projects
run: npm run compile
- name: Unit tests (Full)
run: npm run test
run: npm run test:coverage
- name: Report Coverage
if: ${{ matrix.code-coverage && !cancelled()}}
uses: codecov/codecov-action@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"main": "build/src/index.js",
"types": "build/src/index.d.ts",
"exports": {
".": "./build/src/index.js",
"./register": "./build/src/register.js"
},
"repository": "aws-observability/aws-otel-js-instrumentation",
Expand All @@ -32,6 +31,7 @@
"prepublishOnly": "npm run compile",
"tdd": "yarn test -- --watch-extensions ts --watch",
"test": "nyc ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
"test:coverage": "nyc --all --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
"watch": "tsc -w"
},
"bugs": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class AwsXRayRemoteSampler implements Sampler {

this.awsProxyEndpoint = samplerConfig.endpoint ? samplerConfig.endpoint : DEFAULT_AWS_PROXY_ENDPOINT;
this.fallbackSampler = new ParentBasedSampler({ root: new FallbackSampler() });
this.clientId = this.generateClientId();
this.clientId = AwsXRayRemoteSampler.generateClientId();
this.ruleCache = new RuleCache(samplerConfig.resource);

this.samplingClient = new AwsXraySamplingClient(this.awsProxyEndpoint, this.samplerDiag);
Expand Down Expand Up @@ -202,7 +202,7 @@ export class AwsXRayRemoteSampler implements Sampler {
}
}

private generateClientId(): string {
private static generateClientId(): string {
const hexChars: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
const clientIdArray: string[] = [];
for (let _: number = 0; _ < 24; _ += 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ describe('AlwaysRecordSamplerTest', () => {
it('testDropSamplingDecision', () => {
validateShouldSample(SamplingDecision.NOT_RECORD, SamplingDecision.RECORD);
});

it('testCreateAlwaysRecordSamplerThrows', () => {
expect(() => AlwaysRecordSampler.create(null as unknown as Sampler)).toThrow();
expect(() => AlwaysRecordSampler.create(undefined as unknown as Sampler)).toThrow();
});
});

function validateShouldSample(rootDecision: SamplingDecision, expectedDecision: SamplingDecision): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { AttributePropagatingSpanProcessor } from '../src/attribute-propagating-span-processor';
import { AttributePropagatingSpanProcessorBuilder } from '../src/attribute-propagating-span-processor-builder';
import expect from 'expect';
import { ReadableSpan, Span } from '@opentelemetry/sdk-trace-base';
import expect from 'expect';
import * as sinon from 'sinon';
import { AttributePropagatingSpanProcessor } from '../src/attribute-propagating-span-processor';
import { AttributePropagatingSpanProcessorBuilder } from '../src/attribute-propagating-span-processor-builder';

describe('AttributePropagatingSpanProcessorBuilderTest', () => {
it('BasicTest', () => {
Expand All @@ -23,4 +23,11 @@ describe('AttributePropagatingSpanProcessorBuilderTest', () => {
expect((spanProcessor as any).propagationDataExtractor(sinon.createStubInstance(Span))).toEqual('test');
expect((spanProcessor as any).attributesKeysToPropagate).toEqual(['test']);
});

it('throws errors when expected to', () => {
const builder: AttributePropagatingSpanProcessorBuilder = AttributePropagatingSpanProcessorBuilder.create();
expect(() => builder.setPropagationDataExtractor(undefined as any)).toThrow();
expect(() => builder.setPropagationDataKey(undefined as any)).toThrow();
expect(() => builder.setAttributesKeysToPropagate(undefined as any)).toThrow();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ describe('AwsOpenTelemetryConfiguratorTest', () => {
awsOtelConfigurator = new AwsOpentelemetryConfigurator([]);
});

// Cleanup any span processors to avoid unit test conflicts
after(() => {
(awsOtelConfigurator as any).spanProcessors.forEach((spanProcessor: SpanProcessor) => {
spanProcessor.shutdown();
});
});

// The probability of this passing once without correct IDs is low, 20 times is inconceivable.
it('ProvideGenerateXrayIdsTest', () => {
const tracerProvider: NodeTracerProvider = new NodeTracerProvider(awsOtelConfigurator.configure());
Expand Down Expand Up @@ -170,7 +177,7 @@ describe('AwsOpenTelemetryConfiguratorTest', () => {

const tmp = (AwsXraySamplingClient.prototype as any).makeSamplingRequest;
(AwsXraySamplingClient.prototype as any).makeSamplingRequest = (
endpoint: string,
url: string,
callback: (responseObject: GetSamplingRulesResponse) => void
) => {
callback({});
Expand Down Expand Up @@ -262,6 +269,11 @@ describe('AwsOpenTelemetryConfiguratorTest', () => {
delete process.env.OTEL_METRIC_EXPORT_INTERVAL;
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED;
}

// shut down exporters for test cleanup
spanProcessors.forEach(spanProcessor => {
spanProcessor.shutdown();
});
});

it('ApplicationSignalsExporterProviderTest', () => {
Expand All @@ -283,6 +295,24 @@ describe('AwsOpenTelemetryConfiguratorTest', () => {
expect('http://localhost:4316/v1/metrics').toEqual((exporter as any)._otlpExporter.url);
});

it('tests getSamplerProbabilityFromEnv() ratio out of bounds', () => {
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True';
process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = '105';
awsOtelConfigurator = new AwsOpentelemetryConfigurator([]);
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED;
delete process.env.OTEL_TRACES_SAMPLER_ARG;
});

it('tests getSamplerProbabilityFromEnv() ratio not a number', () => {
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True';
process.env.OTEL_TRACES_SAMPLER = 'traceidratio';
process.env.OTEL_TRACES_SAMPLER_ARG = 'abc';
awsOtelConfigurator = new AwsOpentelemetryConfigurator([]);
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED;
delete process.env.OTEL_TRACES_SAMPLER_ARG;
});

function validateConfiguratorEnviron() {
// Set by register.ts
expect('http/protobuf').toEqual(process.env.OTEL_EXPORTER_OTLP_PROTOCOL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,52 @@
// SPDX-License-Identifier: Apache-2.0
// Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License.

import { NodeSDK } from '@opentelemetry/sdk-node';
import * as assert from 'assert';
import { spawnSync, SpawnSyncReturns } from 'child_process';
import expect from 'expect';
import { setAwsDefaultEnvironmentVariables } from '../src/register';

// The OpenTelemetry Authors code
// Extend register.test.ts functionality to also test exported span with Application Signals enabled
describe('Register', function () {
it('Requires without error', () => {
const originalPrototypeStart = NodeSDK.prototype.start;
NodeSDK.prototype.start = () => {};
try {
require('../src/register');
} catch (err: unknown) {
assert.fail(`require register unexpectedly failed: ${err}`);
}

NodeSDK.prototype.start = originalPrototypeStart;
});

it('Tests AWS Default Environment Variables', () => {
this.beforeEach(() => {
delete process.env.OTEL_EXPORTER_OTLP_PROTOCOL;
delete process.env.OTEL_PROPAGATORS;
delete process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS;
});

it('sets AWS Default Environment Variables', () => {
setAwsDefaultEnvironmentVariables();
expect(process.env.OTEL_EXPORTER_OTLP_PROTOCOL).toEqual('http/protobuf');
expect(process.env.OTEL_PROPAGATORS).toEqual('xray,tracecontext,b3,b3multi');
expect(process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS).toEqual('fs');
});

it('Does not set AWS Default Environment Variables', () => {
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'customProtocol';
process.env.OTEL_PROPAGATORS = 'customPropagators';
process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS = 'customDisabledInstrumentations';
setAwsDefaultEnvironmentVariables();
expect(process.env.OTEL_EXPORTER_OTLP_PROTOCOL).toEqual('customProtocol');
expect(process.env.OTEL_PROPAGATORS).toEqual('customPropagators');
expect(process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS).toEqual('customDisabledInstrumentations');
});
});

it('can load auto instrumentation from command line', () => {
const proc: SpawnSyncReturns<Buffer> = spawnSync(
process.execPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,10 @@ describe('AwsXrayRemoteSampler', () => {
}, 2000);
}, 100);
});

it('generates valid ClientId', () => {
const clientId: string = (AwsXRayRemoteSampler as any).generateClientId();
const match: RegExpMatchArray | null = clientId.match(/[0-9a-z]{24}/g);
expect(match).not.toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,10 @@ describe('FallBackSampler', () => {
}
expect(sampled).toEqual(1);
});

it('toString()', () => {
expect(new FallbackSampler().toString()).toEqual(
'FallbackSampler{fallback sampling with sampling config of 1 req/sec and 5% of additional requests'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,10 @@ describe('RateLimitingSampler', () => {
}
expect(sampled).toEqual(1);
});

it('toString()', () => {
expect(new RateLimitingSampler(123).toString()).toEqual(
'RateLimitingSampler{rate limiting sampling with sampling config of 123 req/sec and 0% of additional requests}'
);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { expect } from 'expect';
import { Attributes } from '@opentelemetry/api/build/src/common/Attributes';
import { Resource } from '@opentelemetry/resources';
import {
Expand All @@ -13,6 +12,7 @@ import {
SEMRESATTRS_CLOUD_PLATFORM,
SEMRESATTRS_SERVICE_NAME,
} from '@opentelemetry/semantic-conventions';
import { expect } from 'expect';
import { SamplingRule } from '../../src/sampler/sampling-rule';
import { SamplingRuleApplier } from '../../src/sampler/sampling-rule-applier';

Expand Down Expand Up @@ -184,4 +184,33 @@ describe('SamplingRuleApplier', () => {
expect(ruleApplier.matches(attributes, new Resource({}))).toEqual(true);
expect(ruleApplier.matches({}, new Resource({}))).toEqual(true);
});

it('testApplierMatchesWithHttpUrlWithHttpTargetUndefined', () => {
const ruleApplier = new SamplingRuleApplier(
new SamplingRule({
Attributes: {},
FixedRate: 0.11,
HTTPMethod: '*',
Host: '*',
Priority: 20,
ReservoirSize: 1,
ResourceARN: '*',
RuleARN: 'arn:aws:xray:us-east-1:999999999999:sampling-rule/test',
RuleName: 'test',
ServiceName: '*',
ServiceType: '*',
URLPath: '/somerandompath',
Version: 1,
})
);

const attributes: Attributes = {
[SEMATTRS_HTTP_URL]: 'https://somerandomurl.com/somerandompath',
};
const resource = new Resource({});

expect(ruleApplier.matches(attributes, resource)).toEqual(true);
expect(ruleApplier.matches(attributes, Resource.EMPTY)).toEqual(true);
expect(ruleApplier.matches(attributes, new Resource({}))).toEqual(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { expect } from 'expect';
import { Statistics } from '../../src/sampler/statistics';

describe('Statistics', () => {
it('construct statistics and get statistics', () => {
const statistics = new Statistics(12, 3456, 7);
expect(statistics.RequestCount).toEqual(12);
expect(statistics.SampleCount).toEqual(3456);
expect(statistics.BorrowCount).toEqual(7);
const obtainedStatistics = statistics.getStatistics();
expect(obtainedStatistics.RequestCount).toEqual(12);
expect(obtainedStatistics.SampleCount).toEqual(3456);
expect(obtainedStatistics.BorrowCount).toEqual(7);
});
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"compile": "lerna run compile",
"prewatch": "npm run precompile",
"test": "lerna run test",
"test:coverage": "lerna run test:coverage",
"test:ci:changed": "lerna run test --since origin/main",
"test:browser": "lerna run test:browser --concurrency 1",
"test-all-versions": "npm run --if-present --workspaces test-all-versions",
Expand Down

0 comments on commit c9278ee

Please sign in to comment.