Skip to content

Commit

Permalink
feat: #82 Adding methods for retrieving messages and events exchanged…
Browse files Browse the repository at this point in the history
… during tests

Signed-off-by: Laurent Broudoux <[email protected]>
  • Loading branch information
lbroudoux committed Jan 1, 2025
1 parent 825ff40 commit 0d04fa1
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 3 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Want to see this extension in action? Check out our [sample application](https:/

Latest released version is `0.2.6`.

Current development version is `0.2.7`.
Current development version is `0.3.0`.

#### Fossa license and security scans

Expand Down Expand Up @@ -140,6 +140,8 @@ expect(testResult.testCaseResults[0].testStepResults[0].message).toContain("obje

The `TestResult` gives you access to all details regarding success of failure on different test cases.

In addition, you can use the `getMessagesForTestCase()` method to retrieve the messages exchanged during the test.

A comprehensive NestJS demo application illustrating both usages is available here: [nest-order-service](https://github.com/microcks/api-lifecycle/tree/master/shift-left-demo/nest-order-service).

### Using authentication Secrets
Expand Down Expand Up @@ -278,3 +280,5 @@ let testResultPromise: Promise<TestResult> = ensemble.getMicrocksContainer().tes
let testResult = await testResultPromise;
expect(testResult.success).toBe(true);
```

In addition, you can use the `getEventMessagesForTestCase()` method to retrieve the events received during the test.
13 changes: 13 additions & 0 deletions src/microcks-container.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ describe("MicrocksContainer", () => {
expect(testResult.testCaseResults.length).toBe(3);
expect(testResult.testCaseResults[0].testStepResults[0].message).toContain("object has missing required properties");

// Retrieve messages for the failing test case.
const messages = await container.getMessagesForTestCase(testResult, "GET /pastries");
expect(messages.length).toBe(3);
messages.forEach(message => {
expect(message.request).not.toBeNull();
expect(message.response).not.toBeNull();
expect(message.request.content).not.toBeNull();
// Check these are the correct requests.
expect(message.request.queryParameters).not.toBeNull();
expect(message.request.queryParameters.length).toBe(1);
expect(message.request.queryParameters[0].name).toBe('size');
});

testRequest = {
serviceId: "API Pastries:0.0.1",
runnerType: TestRunnerType.OPEN_API_SCHEMA,
Expand Down
103 changes: 103 additions & 0 deletions src/microcks-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,54 @@ export enum TestRunnerType {
GRAPHQL_SCHEMA = "GRAPHQL_SCHEMA"
}

export interface MicrocksHeader {
name: string;
values: string[];
}
export interface MicrocksParameter {
name: string;
value: string;
}

export interface Message {
name: string;
content: string;
operationId: string;
testCaseId: string;
sourceArtifact: string;
headers: MicrocksHeader[];
}

export interface MicrocksRequest extends Message {
id: string;
responseId: string;
queryParameters: MicrocksParameter[];
}

export interface MicrocksResponse extends Message {
id: string;
status: string;
mediaType: string;
dispatchCriteria: string;
isFault: boolean;
}

export interface EventMessage extends Message {
id: string;
mediaType: string;
dispatchCriteria: string;
}

export interface RequestResponsePair {
request: MicrocksRequest;
response: MicrocksResponse;
}

export interface UnidirectionalEvent {
eventMessage: EventMessage;
}


export class StartedMicrocksContainer extends AbstractStartedContainer {
private readonly httpPort: number;
private readonly grpcPort: number;
Expand Down Expand Up @@ -387,6 +435,56 @@ export class StartedMicrocksContainer extends AbstractStartedContainer {
throw new Error("Couldn't launch on new test on Microcks. Please check Microcks container logs.");
}

/**
* Retrieve messages exchanged during a test on an endpoint.
* @param {TestResult} testResult The TestResult containing information on success/failure as well as details on test cases.
* @param {string} operationName The name of the operation to get messages corresponding to test case
* @returns A list of RequestResponsePairs containing the request/response of test
*/
public async getMessagesForTestCase(testResult: TestResult, operationName: string): Promise<RequestResponsePair[]> {
// Build the test case identfier.
const operation = this.encode(operationName)
const testCaseId = `${testResult.id}-${testResult.testNumber}-${operation}`;
// Request the test case messages.
const response = await fetch(this.getHttpEndpoint() + "/api/tests/" + testResult.id + "/messages/" + testCaseId, {
method: 'GET',
headers: {
'Accept': 'application/json'
},
});

if (response.status == 200) {
const responseJson = await response.json();
return responseJson as RequestResponsePair[];
}
throw new Error("Couldn't retrieve messages on test on Microcks. Please check Microcks container logs");
}

/**
* Retrieve event messages received during a test on an endpoint.
* @param {TestResult} testResult The TestResult containing information on success/failure as well as details on test cases.
* @param {string} operationName The name of the operation to get messages corresponding to test case
* @returns A list of RequestResponsePairs containing the request/response of test
*/
public async getEventMessagesForTestCase(testResult: TestResult, operationName: string): Promise<UnidirectionalEvent[]> {
// Build the test case identfier.
const operation = this.encode(operationName)
const testCaseId = `${testResult.id}-${testResult.testNumber}-${operation}`;
// Request the test case event messages.
const response = await fetch(this.getHttpEndpoint() + "/api/tests/" + testResult.id + "/events/" + testCaseId, {
method: 'GET',
headers: {
'Accept': 'application/json'
},
});

if (response.status == 200) {
const responseJson = await response.json();
return responseJson as UnidirectionalEvent[];
}
throw new Error("Couldn't retrieve event messages on test on Microcks. Please check Microcks container logs");
}


private async importArtifact(artifactPath: string, mainArtifact: boolean): Promise<void> {
const isFile = await this.isFile(artifactPath);
Expand Down Expand Up @@ -496,4 +594,9 @@ export class StartedMicrocksContainer extends AbstractStartedContainer {
setTimeout(resolve, ms);
});
}

private encode(operation: string): string {
operation = operation.replace(/\//g, '!');
return encodeURIComponent(operation);
}
}
22 changes: 20 additions & 2 deletions src/microcks-containers-ensemble.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,21 @@ describe("MicrocksContainersEnsemble", () => {
expect(testResult.testCaseResults.length).toBeGreaterThan(0);
expect(testResult.testCaseResults[0].testStepResults[0].message).toContain("object has missing required properties");

// Retrieve event messages for the failing test case.
const events = await ensemble.getMicrocksContainer().getEventMessagesForTestCase(testResult,
"SUBSCRIBE pastry/orders");

// We should have at least 1 event.
expect(events.length).toBeGreaterThan(0);
events.forEach(message => {
expect(message.eventMessage).not.toBeNull();
expect(message.eventMessage.content).not.toBeNull();

// Check these are the correct content.
const content = JSON.parse(message.eventMessage.content);
expect(content['productQuantities'].length).toBe(2);
});

testRequest = {
serviceId: "Pastry orders API:0.1.0",
runnerType: TestRunnerType.ASYNC_API_SCHEMA,
Expand Down Expand Up @@ -341,7 +356,7 @@ describe("MicrocksContainersEnsemble", () => {
const network = await new Network().start();

// Start ensemble, load artifacts and start other containers.
const localstack = await new LocalstackContainer("localstack/localstack:latest")
const localstack = await new LocalstackContainer("localstack/localstack:4.0.3")
.withNetwork(network)
.withNetworkAliases("localstack")
.withEnvironment({
Expand Down Expand Up @@ -426,7 +441,7 @@ describe("MicrocksContainersEnsemble", () => {
const network = await new Network().start();

// Start ensemble, load artifacts and start other containers.
const localstack = await new LocalstackContainer("localstack/localstack:latest")
const localstack = await new LocalstackContainer("localstack/localstack:4.0.3")
.withNetwork(network)
.withNetworkAliases("localstack")
.start();
Expand Down Expand Up @@ -499,6 +514,9 @@ describe("MicrocksContainersEnsemble", () => {

expect(testResult.success).toBe(false);
expect(testResult.testedEndpoint).toBe("sqs://us-east-1/pastry-orders?overrideUrl=http://localstack:4566");

console.log("TestResult: " + JSON.stringify(testResult));

expect(testResult.testCaseResults.length).toBeGreaterThan(0);
expect(testResult.testCaseResults[0].testStepResults[0].message).toContain("object has missing required properties");
testResult.testCaseResults.forEach(tcr => {
Expand Down

0 comments on commit 0d04fa1

Please sign in to comment.