Skip to content

Commit

Permalink
fix(integration): integration test dsl with test servers (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
galkleinman authored Feb 19, 2023
1 parent f00c073 commit 6b4b7f7
Show file tree
Hide file tree
Showing 18 changed files with 7,019 additions and 5,746 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/README
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ If you get an error like:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
```

Run `export DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}')` and then re-run `act` as written above.
Run `export DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}')` and then re-run `act` as written above.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.proto
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
"@opentelemetry/semantic-conventions": "^1.9.1",
"@opentelemetry/tracing": "^0.24.0",
"@swc/jest": "^0.2.24",
"deep-equal": "^2.2.0",
"axios": "^1.3.3",
"deep-equal": "^2.2.0",
"eslint-config-prettier": "^8.6.0",
"prettier": "^2.8.4"
}
Expand Down
28 changes: 17 additions & 11 deletions packages/expect-opentelemetry/src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
/* eslint-disable no-use-before-define, no-restricted-syntax, no-await-in-loop */
import { getInstanceType } from './utils';
import { toRecieveHttpRequest } from './matchers/toRecieveHttpRequest';
import { toSendHttpRequest } from './matchers/toSendHttpRequest';
import { toReceiveHttpRequest } from './matchers/service/to-receive-http-request';
import { toSendHttpRequest } from './matchers/service/to-send-http-request';

export { setDefaultOptions, getDefaultOptions } from './options';

const spanMatchers = {};
const spanMatchers = {
not: {},
};

const serviceMatchers = {
toRecieveHttpRequest,
toReceiveHttpRequest,
toSendHttpRequest,
not: {},
};

function createMatcher(matcher, page) {
Expand All @@ -32,14 +35,16 @@ function internalExpect(type, matchers) {
not: {},
};

Object.keys(matchers).forEach((key) => {
if (key === 'not') return;
expectation[key] = createMatcher(matchers[key], type);
});
matchers &&
Object.keys(matchers).forEach((key) => {
if (key === 'not') return;
expectation[key] = createMatcher(matchers[key], type);
});

Object.keys(matchers.not).forEach((key) => {
expectation.not[key] = createMatcher(matchers.not[key], type);
});
matchers?.not &&
Object.keys(matchers.not).forEach((key) => {
expectation.not[key] = createMatcher(matchers.not[key], type);
});

return expectation;
}
Expand All @@ -60,6 +65,7 @@ if (typeof global.expect !== 'undefined') {
const originalExpect = global.expect;
global.expect = (actual, ...args) => {
const type = getInstanceType(actual);

if (type) {
const matchers = expectOpenTelemetry(actual);
const jestMatchers = originalExpect(actual, ...args);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { SpanKind } from '@opentelemetry/api';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
import { HttpRequest } from '../../resources/http-request';
import { Microservice } from '../../resources/microservice';
import { Service } from '../../resources/service';
import { opentelemetry } from '../../../../../proto';

export function toRecieveHttpCall(service: Microservice): HttpRequest {
export function toReceiveHttpRequest(service: Service): HttpRequest {
const { name: serviceName, spans } = service;
const spanKind = SpanKind.SERVER;
const spanKind = opentelemetry.proto.trace.v1.Span.SpanKind.SPAN_KIND_SERVER;

const filteredSpans = spans.filter((span) => {
return (
span.kind === spanKind && span.attributes[SemanticAttributes.HTTP_METHOD]
span.kind === spanKind &&
span.attributes?.find((attribute) => {
return attribute.key === SemanticAttributes.HTTP_METHOD;
})
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { SpanKind } from '@opentelemetry/api';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
import { HttpRequest } from '../../resources/http-request';
import { Microservice } from '../../resources/microservice';
import { Service } from '../../resources/service';
import { opentelemetry } from '../../../../../proto';

export function toRecieveHttpCall(service: Microservice): HttpRequest {
export function toSendHttpRequest(service: Service): HttpRequest {
const { name: serviceName, spans } = service;
const spanKind = SpanKind.CLIENT;
const spanKind = opentelemetry.proto.trace.v1.Span.SpanKind.SPAN_KIND_CLIENT;

const filteredSpans = spans.filter((span) => {
return (
span.kind === spanKind && span.attributes[SemanticAttributes.HTTP_METHOD]
span.kind === spanKind &&
span.attributes?.find((attribute) => {
return attribute.key === SemanticAttributes.HTTP_METHOD;
})
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
let defaultOptionsValue = { timeout: 500 };

export const setDefaultOptions = (options) => {
export const setDefaultOptions = (options: any) => {
defaultOptionsValue = options;
};

export const getDefaultOptions = () => {
return defaultOptionsValue;
};

export const defaultOptions = (options) => ({
export const defaultOptions = (options: any) => ({
...getDefaultOptions(),
...options,
});
33 changes: 23 additions & 10 deletions packages/expect-opentelemetry/src/resources/http-request.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { SpanKind } from '@opentelemetry/api';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
import { ReadableSpan } from '@opentelemetry/tracing';
import deepEqual from 'deep-equal';
import { opentelemetry } from '../../../../proto';

export class HttpRequest {
constructor(
readonly spans: ReadableSpan[],
readonly spans: opentelemetry.proto.trace.v1.ISpan[],
private readonly serviceName: string,
private readonly spanKind: SpanKind,
private readonly spanKind: opentelemetry.proto.trace.v1.Span.SpanKind,
) {}

withBody(body: object) {
const filteredSpans = this.spans.filter((span) => {
const jsonBody = JSON.parse(
span.attributes['http.request.body'] as string,
span.attributes?.find(
(attribute) => attribute.key === 'http.request.body',
)?.value?.stringValue || '',
);

return deepEqual(jsonBody, body);
Expand All @@ -30,7 +31,11 @@ export class HttpRequest {

withHeader(key: string, value: string) {
const filteredSpans = this.spans.filter((span) => {
return span.attributes[`http.request.header.${key}`] === value;
return span.attributes?.find(
(attribute) =>
attribute.key === `http.request.header.${key}` &&
attribute.value?.stringValue === value,
);
});

if (filteredSpans.length === 0) {
Expand All @@ -44,7 +49,11 @@ export class HttpRequest {

ofMethod(method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH') {
const filteredSpans = this.spans.filter((span) => {
return span.attributes[SemanticAttributes.HTTP_METHOD] === method;
return span.attributes?.find(
(attribute) =>
attribute.key === SemanticAttributes.HTTP_METHOD &&
attribute.value?.stringValue === method,
);
});

if (filteredSpans.length === 0) {
Expand All @@ -58,7 +67,11 @@ export class HttpRequest {

withStatusCode(code: number) {
const filteredSpans = this.spans.filter((span) => {
return span.attributes[SemanticAttributes.HTTP_STATUS_CODE] === code;
return span.attributes?.find(
(attribute) =>
attribute.key === SemanticAttributes.HTTP_STATUS_CODE &&
attribute.value?.intValue === code,
);
});

if (filteredSpans.length === 0) {
Expand All @@ -72,9 +85,9 @@ export class HttpRequest {

private serviceErrorBySpanKind() {
switch (this.spanKind) {
case SpanKind.CLIENT:
case opentelemetry.proto.trace.v1.Span.SpanKind.SPAN_KIND_CLIENT:
return `was sent by ${this.serviceName}`;
case SpanKind.SERVER:
case opentelemetry.proto.trace.v1.Span.SpanKind.SPAN_KIND_SERVER:
return `was received by ${this.serviceName}`;
default:
return `was found for ${this.serviceName}`;
Expand Down
8 changes: 0 additions & 8 deletions packages/expect-opentelemetry/src/resources/microservice.ts

This file was deleted.

8 changes: 8 additions & 0 deletions packages/expect-opentelemetry/src/resources/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { opentelemetry } from '../../../../proto';

export class Service {
constructor(
public readonly name: string,
public readonly spans: opentelemetry.proto.trace.v1.ISpan[],
) {}
}
16 changes: 8 additions & 8 deletions packages/expect-opentelemetry/src/trace.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import axios from 'axios';
import { trace } from './trace';
import { traces } from './trace';

jest.setTimeout(30000);

describe('trace', () => {
it('should see orders-service calling emails-service', async () => {
const sequence = await trace(async () =>
axios.post('http://localhost:3000/orders/create'),
);
expect(1).toBe(1);
// expect(sequence.service('orders-service'))
// .toCall('emails-service')
// .withHttpBody({});
const sequence = await traces(async () => {
await axios.post('http://localhost:3000/orders/create');
});

expect(sequence.service('orders-service')).toSendHttpRequest();
});
});
30 changes: 25 additions & 5 deletions packages/expect-opentelemetry/src/trace.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
import axios from 'axios';
import { setTimeout } from 'timers/promises';
import { Service } from './resources/service';
import { opentelemetry } from '../../../proto';

export async function trace(fn: () => Promise<void>) {
export async function traces(fn: () => Promise<void>) {
await setTimeout(5000);
await fn();
await setTimeout(20000);
const t = new Trace();
await t.getTraces();
await t.init();
return t;
}

export class Trace {
async getTraces() {
await setTimeout(2000);
return axios.get('http://localhost:4123/v1/traces');
private tracesData: opentelemetry.proto.trace.v1.ITracesData | undefined;

async init() {
const response = (await axios.get('http://localhost:4123/v1/traces')).data;

this.tracesData = opentelemetry.proto.trace.v1.TracesData.decode(response);
}

service(name: string): Service {
const serviceResourceSpans = this.tracesData?.resourceSpans?.find((rs) =>
rs.resource?.attributes?.find(
(a) => a.key === 'service.name' && a.value?.stringValue === name,
),
);

const serviceSpans =
serviceResourceSpans?.scopeSpans?.flatMap((ss) => ss.spans || []) || [];

return new Service(name, serviceSpans);
}
}
38 changes: 0 additions & 38 deletions packages/expect-opentelemetry/src/utils.js

This file was deleted.

Loading

0 comments on commit 6b4b7f7

Please sign in to comment.