Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ensure all defined performance tests compile and run #732

Closed
wants to merge 10 commits into from
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidpoltorak-io, you need to make the length of the key at least 16 bytes. I will not work with the current Agent with default value.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @yshyn-iohk - I've been struggling to get this branch ready for merging - between performance thresholds being passed and some failed runs - this really helps - I'll make the change and retest

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
@@ -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()}[email protected]",
"claims": {
"emailAddress": "${crypto.randomUUID()}[email protected]",
"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,10 @@
import { group } from 'k6';
import { Options } from 'k6/options';
import { Issuer, Holder } from '../../actors';
import { Connection } from '@input-output-hk/prism-typescript-client';

// 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",
},
},
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'],
},
};
import { Connection, CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client';
import { defaultOptions } from "../../scenarios/default";

export let options: Options = defaultOptions
export const issuer = new Issuer();
export const holder = new Holder();

Expand All @@ -52,22 +25,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
Loading