Skip to content

Commit

Permalink
feat: fix all performance test flows and set group thresholds
Browse files Browse the repository at this point in the history
Signed-off-by: David Poltorak <david.poltorak@iohk.io>
  • Loading branch information
davepoltorak committed Oct 9, 2023
1 parent a579aa9 commit ca0592b
Show file tree
Hide file tree
Showing 17 changed files with 230 additions and 261 deletions.
17 changes: 10 additions & 7 deletions .github/workflows/performance-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ concurrency:

on:
pull_request:
paths:
- ".github/workflows/performance-tests.yml"
- "tests/performance-tests/**"
push:
branches:
- "main"
Expand Down Expand Up @@ -90,23 +87,29 @@ jobs:
scope: 'input-output-hk'

- name: Install dependencies
uses: borales/actions-yarn@v4
uses: borales/actions-yarn@v4.2.0
with:
cmd: install
dir: ${{ env.BENCHMARKING_DIR }}

- name: Compile tests to JS
uses: borales/actions-yarn@v4
uses: borales/actions-yarn@v4.2.0
with:
cmd: webpack
dir: ${{ env.BENCHMARKING_DIR }}

- name: Connection Flow Smoke Test
- name: All Smoke Tests
env:
ISSUER_AGENT_API_KEY: default
HOLDER_AGENT_API_KEY: default
run: |
# Have to use manual download because GitHub action doesnt support localhost execution
curl https://github.com/grafana/k6/releases/download/v0.45.0/k6-v0.45.0-linux-amd64.tar.gz -L | tar xvz --strip-components 1
ls -la
./k6 run ${{ env.BENCHMARKING_DIR }}/dist/connection-flow-test.js
./k6 run -e SCENARIO_LABEL=create-prism-did-smoke ${{ env.BENCHMARKING_DIR }}/dist/create-prism-did-test.js
./k6 run -e SCENARIO_LABEL=credential-offer-smoke ${{ env.BENCHMARKING_DIR }}/dist/credential-offer-test.js
./k6 run -e SCENARIO_LABEL=credential-schema-smoke ${{ env.BENCHMARKING_DIR }}/dist/credential-schema-test.js
./k6 run -e SCENARIO_LABEL=did-publishing-smoke ${{ env.BENCHMARKING_DIR }}/dist/did-publishing-test.js
./k6 run -e SCENARIO_LABEL=connection-flow-smoke ${{ env.BENCHMARKING_DIR }}/dist/connection-flow-test.js
./k6 run -e SCENARIO_LABEL=issuance-flow-smoke ${{ env.BENCHMARKING_DIR }}/dist/issuance-flow-test.js
./k6 run -e SCENARIO_LABEL=present-proof-flow-smoke ${{ env.BENCHMARKING_DIR }}/dist/present-proof-flow-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"typescript": "5.2.2",
"webpack": "5.88.2",
"webpack-cli": "5.1.4",
"webpack-glob-entries": "^1.0.1"
"webpack-glob-entries": "^1.0.1",
"ts-deepmerge": "6.2.0"
},
"scripts": {
"start": "webpack"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/*global __ENV*/

import { Connection, ConnectionInvitation, ConnectionStateEnum } from "@input-output-hk/prism-typescript-client";
import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config";
import { HttpService } from "./HttpService";
Expand Down Expand Up @@ -26,7 +28,7 @@ export class ConnectionService extends HttpService {
*/
getConnection(connectionId: string): Connection {
const res = this.get(`connections/${connectionId}`);
const connection = res.json() as unknown as Connection;
const connection = this.toJson(res) as unknown as Connection;
return connection;
}

Expand All @@ -36,7 +38,7 @@ export class ConnectionService extends HttpService {
*/
createConnection(): Connection {
const payload = { label: "test" };
const connection = this.post("connections", payload).json() as unknown as Connection;
const connection = this.toJson(this.post("connections", payload)) as unknown as Connection;
return connection;
}

Expand All @@ -48,7 +50,7 @@ export class ConnectionService extends HttpService {
acceptConnectionInvitation(invitation: ConnectionInvitation): Connection {
const payload = { invitation: this.invitationFromUrl(invitation.invitationUrl) };
const res = this.post("connection-invitations", payload, 200);
return res.json() as unknown as Connection;
return this.toJson(res) as unknown as Connection;
}

/**
Expand All @@ -66,7 +68,7 @@ export class ConnectionService extends HttpService {
sleep(WAITING_LOOP_PAUSE_INTERVAL);
iterations++;
} while (state !== requiredState && iterations < WAITING_LOOP_MAX_ITERATIONS);
if (state != requiredState) {
if (state !== requiredState) {
throw new Error(`Connection state is ${state}, required ${requiredState}`);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { sleep } from "k6";
import { HttpService } from "./HttpService";
import { ISSUER_AGENT_URL, WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config";
import { IssueCredentialRecord, Connection, CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client";
import {v4 as uuidv4} from 'uuid';
import { crypto } from "k6/experimental/webcrypto";


/**
* A service class for managing credentials in the application.
Expand All @@ -18,8 +19,8 @@ export class CredentialsService extends HttpService {
*/
createCredentialOffer(issuingDid: string, connection: Connection, schema: CredentialSchemaResponse): IssueCredentialRecord {
const payload = `{
"claims": {
"emailAddress": "${uuidv4()}-@atala.io",
"claims": {
"emailAddress": "${crypto.randomUUID()}-@atala.io",
"familyName": "Test",
"dateOfIssuance": "${new Date()}",
"drivingLicenseID": "Test",
Expand All @@ -31,13 +32,13 @@ export class CredentialsService extends HttpService {
"automaticIssuance": false
}`;
const res = this.post("issue-credentials/credential-offers", payload);
return res.json() as unknown as IssueCredentialRecord;
return this.toJson(res) as unknown as IssueCredentialRecord;
}

createCredentialSchema(issuingDid: string): CredentialSchemaResponse {
const payload = `
{
"name": "${uuidv4()}}",
"name": "${crypto.randomUUID()}}",
"version": "1.0.0",
"description": "Simple credential schema for the driving licence verifiable credential.",
"type": "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json",
Expand Down Expand Up @@ -85,7 +86,7 @@ export class CredentialsService extends HttpService {
}
`
const res = this.post("schema-registry/schemas", payload);
return res.json() as unknown as CredentialSchemaResponse;
return this.toJson(res) as unknown as CredentialSchemaResponse;
}

/**
Expand All @@ -95,7 +96,7 @@ export class CredentialsService extends HttpService {
*/
getCredentialRecord(record: IssueCredentialRecord): IssueCredentialRecord {
const res = this.get(`issue-credentials/records/${record.recordId}`);
return res.json() as unknown as IssueCredentialRecord;
return this.toJson(res) as unknown as IssueCredentialRecord;
}

/**
Expand All @@ -104,7 +105,7 @@ export class CredentialsService extends HttpService {
*/
getCredentialRecords(thid: string): IssueCredentialRecord[] {
const res = this.get(`issue-credentials/records?thid=${thid}`);
return res.json("contents") as unknown as IssueCredentialRecord[];
return this.toJson(res).contents as unknown as IssueCredentialRecord[];
}

/**
Expand All @@ -116,7 +117,7 @@ export class CredentialsService extends HttpService {
acceptCredentialOffer(record: IssueCredentialRecord, subjectDid: string): IssueCredentialRecord {
const payload = { subjectId: subjectDid };
const res = this.post(`issue-credentials/records/${record.recordId}/accept-offer`, payload, 200);
return res.json() as unknown as IssueCredentialRecord;
return this.toJson(res) as unknown as IssueCredentialRecord;
}

/**
Expand All @@ -126,7 +127,7 @@ export class CredentialsService extends HttpService {
*/
issueCredential(record: IssueCredentialRecord): IssueCredentialRecord {
const res = this.post(`issue-credentials/records/${record.recordId}/issue-credential`, null, 200);
return res.json() as unknown as IssueCredentialRecord;
return this.toJson(res) as unknown as IssueCredentialRecord;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/*global __ENV*/

import { HttpService } from "./HttpService";
import { WAITING_LOOP_MAX_ITERATIONS, WAITING_LOOP_PAUSE_INTERVAL } from "./Config";
import { CreateManagedDIDResponse, DIDDocument, DidOperationSubmission, ManagedDID } from "@input-output-hk/prism-typescript-client";
import { sleep } from "k6";
import {sleep} from "k6";


/**
* A service class for managing decentralized identifiers (DIDs) in the application.
Expand All @@ -16,7 +19,7 @@ export class DidService extends HttpService {
*/
getDid(did: string): ManagedDID {
const res = this.get(`did-registrar/dids/${did}`);
return res.json() as unknown as ManagedDID;
return this.toJson(res) as unknown as ManagedDID;
}

/**
Expand All @@ -26,7 +29,7 @@ export class DidService extends HttpService {
*/
resolveDid(did: string): DIDDocument {
const res = this.get(`dids/${did}`);
return res.json() as unknown as DIDDocument;
return this.toJson(res) as unknown as DIDDocument;
}

/**
Expand All @@ -36,7 +39,7 @@ export class DidService extends HttpService {
*/
publishDid(did: string): DidOperationSubmission {
const res = this.post(`did-registrar/dids/${did}/publications`, null, 202);
return res.json("scheduledOperation") as unknown as DidOperationSubmission;
return this.toJson(res).scheduledOperation as unknown as DidOperationSubmission;
}

/**
Expand All @@ -46,7 +49,7 @@ export class DidService extends HttpService {
*/
createUnpublishedDid(documentTemplate: string): CreateManagedDIDResponse {
const res = this.post("did-registrar/dids", documentTemplate);
return res.json() as unknown as CreateManagedDIDResponse;
return this.toJson(res) as unknown as CreateManagedDIDResponse;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export class HttpService {
};
}

public toJson(response: RefinedResponse<ResponseType>): any {
return JSON.parse(response.body as string)
}

/**
* Performs an HTTP POST request to the specified endpoint with the provided payload.
* @param endpoint The API endpoint to post to.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class ProofsService extends HttpService {
]
}`
const res = this.post("present-proof/presentations", payload);
return res.json("presentationId") as string;
return this.toJson(res).presentationId as string;
}

/**
Expand All @@ -51,7 +51,7 @@ export class ProofsService extends HttpService {
]
}`
const res = this.patch(`present-proof/presentations/${presentation.presentationId}`, payload);
return res.json("presentationId") as string;
return this.toJson(res).presentationId as string;
}

/**
Expand All @@ -61,7 +61,7 @@ export class ProofsService extends HttpService {
*/
getPresentation(presentationId: string): PresentationStatus {
const res = this.get(`present-proof/presentations/${presentationId}`);
return res.json() as unknown as PresentationStatus;
return this.toJson(res) as unknown as PresentationStatus;
}

/**
Expand All @@ -70,7 +70,7 @@ export class ProofsService extends HttpService {
*/
getPresentations(thid: string): PresentationStatus[] {
const res = this.get(`present-proof/presentations?thid=${thid}`);
return res.json("contents") as unknown as PresentationStatus[];
return this.toJson(res).contents as unknown as PresentationStatus[];
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*global __ENV*/

import { Options } from "k6/options";

export const defaultOptions: Options = {
setupTimeout: '120s',
scenarios: {
smoke: {
// a simple test to ensure performance tests work and requests don't fail
executor: "shared-iterations",
vus: 1,
iterations: 1,
tags: { scenario_label: __ENV.SCENARIO_LABEL || "defaultScenarioLabel" }, // add label for filtering in observability platform
},
},
thresholds: {
http_req_failed: [
// fail if any requests fail during smoke test
{
threshold: "rate==0",
abortOnFail: true,
},
],
http_req_duration: [
{ threshold: "p(95)<2000", abortOnFail: true }, // 95% of requests should complete within 2 seconds
{ threshold: "p(99)<5000", abortOnFail: true }, // 99% of requests should complete within 5 seconds
],
checks: [{ threshold: "rate==1", abortOnFail: true }], // fail if any checks fail (the checks are defined in test code which is executed)

}
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
import { group } from 'k6';
import { Options } from 'k6/options';
import { Issuer, Holder } from '../../actors';
import { Connection } from '@input-output-hk/prism-typescript-client';
import { Connection, CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client';
import { defaultOptions } from "../../scenarios/default";
import merge from "ts-deepmerge";

// export let options: Options = {
// stages: [
// { duration: '1m', target: 5 },
// ],
// thresholds: {
// http_req_failed: [{
// threshold: 'rate<=0.05',
// abortOnFail: true,
// }],
// http_req_duration: ['p(95)<=100'],
// checks: ['rate>=0.99'],
// },
// };

export let options: Options = {
scenarios: {
smoke: {
executor: 'constant-vus',
vus: 3,
duration: "1s",
},
},
export const localOptions: Options = {
thresholds: {
'http_req_duration{group:::Issuer creates credential offer}': ['max >= 0'],
'http_reqs{group:::Issuer creates credential offer}': ['count >= 0'],
'group_duration{group:::Issuer creates credential offer}': ['max >= 0'],
},
};

'group_duration{group:::Issuer creates credential offer}': ['avg < 15000']
}
}
export let options: Options = merge(localOptions, defaultOptions)
export const issuer = new Issuer();
export const holder = new Holder();

Expand All @@ -52,22 +31,29 @@ export function setup() {
holder.finalizeConnectionWithIssuer();
});

return {
group("Issuer creates credential schema", function () {
issuer.createCredentialSchema();
});

return {
issuerDid: issuer.did,
holderDid: holder.did,
issuerSchema: issuer.schema,
connectionWithHolder: issuer.connectionWithHolder!,
connectionWithIssuer: holder.connectionWithIssuer!
};
}

export default (data: { issuerDid: string; holderDid: string; connectionWithHolder: Connection, connectionWithIssuer: Connection }) => {
export default (data: { issuerDid: string; holderDid: string; issuerSchema: CredentialSchemaResponse, connectionWithHolder: Connection, connectionWithIssuer: Connection }) => {

// This is the only way to pass data from setup to default
issuer.did = data.issuerDid;
issuer.schema = data.issuerSchema
holder.did = data.holderDid;
issuer.connectionWithHolder = data.connectionWithHolder;
holder.connectionWithIssuer = data.connectionWithIssuer;


group('Issuer creates credential offer', function () {
issuer.createCredentialOffer();
});
Expand Down
Loading

0 comments on commit ca0592b

Please sign in to comment.