diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts index 59e1cea9a6..19f0f524d1 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ConnectionService.ts @@ -26,7 +26,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; } @@ -36,7 +36,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; } @@ -48,7 +48,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; } /** diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts index 8961b64b9a..c55a37ca1d 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/CredentialsService.ts @@ -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. @@ -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", @@ -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", @@ -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; } /** @@ -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; } /** @@ -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[]; } /** @@ -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; } /** @@ -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; } /** diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts index c203bf0af6..2f7807c4a2 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/DidService.ts @@ -1,7 +1,8 @@ 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 {check, sleep} from "k6"; +import http from "k6/http"; /** * A service class for managing decentralized identifiers (DIDs) in the application. @@ -16,7 +17,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; } /** @@ -26,7 +27,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; } /** @@ -36,7 +37,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; } /** @@ -46,7 +47,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; } /** diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts index 422652c11a..235e3a8ff4 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/HttpService.ts @@ -1,6 +1,6 @@ import http from 'k6/http'; -import { check } from 'k6'; +import {bytes, check} from 'k6'; import { RefinedResponse, ResponseType, RequestBody } from 'k6/http'; /** @@ -34,6 +34,10 @@ export class HttpService { }; } + public toJson(response: RefinedResponse): 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. diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts index b52c723a25..f274b8f0de 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/ProofsService.ts @@ -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; } /** @@ -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; } /** @@ -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; } /** @@ -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[]; } /** diff --git a/tests/performance-tests/atala-performance-tests-k6/src/scenarios/default.ts b/tests/performance-tests/atala-performance-tests-k6/src/scenarios/default.ts new file mode 100644 index 0000000000..8c2470f57b --- /dev/null +++ b/tests/performance-tests/atala-performance-tests-k6/src/scenarios/default.ts @@ -0,0 +1,27 @@ +import { Scenario, Threshold } from "k6/options"; + +export const defaultScenarios: { [name: string]: Scenario } = { + smoke: { + // a simple test to ensure performance tests work and requests don't fail + executor: "constant-vus", + vus: 3, + duration: "10s", + tags: { scenario_label: __ENV.SCENARIO_LABEL || "defaultScenarioLabel" }, // add label for filtering in observability platform + }, +}; + +export const defaultThresholds: { [name: string]: Threshold[] } = { + http_req_failed: [ + // fail if any requests fail during smoke test + { + threshold: "rate==0", + abortOnFail: true, + }, + ], + http_req_duration: [ + // fail if any requests take longer than 500ms on smoke test + { threshold: "p(95)<500", abortOnFail: true }, // 95% of requests should complete within 500ms + { threshold: "p(99)<1500", abortOnFail: true }, // 99% of requests should complete within 1500ms + ], + checks: ["rate==1"], // fail if any checks fail (the checks are defined in test code which is executed) +}; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts index 27b86d07ff..30e87e1628 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-offer-test.ts @@ -2,36 +2,15 @@ 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'], -// }, -// }; - +import { defaultScenarios, defaultThresholds } from '../../scenarios/default'; export let options: Options = { scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1s", - }, + ...defaultScenarios }, 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'], - }, -}; - + ...defaultThresholds + } +} export const issuer = new Issuer(); export const holder = new Holder(); @@ -52,7 +31,7 @@ export function setup() { holder.finalizeConnectionWithIssuer(); }); - return { + return { issuerDid: issuer.did, holderDid: holder.did, connectionWithHolder: issuer.connectionWithHolder!, diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts index 095eee806f..6510448d48 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-schema-test.ts @@ -1,43 +1,33 @@ -import { group } from 'k6'; -import { Options } from 'k6/options'; -import { Issuer } from '../../actors'; - +import { group } from "k6"; +import { Options } from "k6/options"; +import { Issuer } from "../../actors"; +import {defaultScenarios, defaultThresholds} from "../../scenarios/default"; export let options: Options = { scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1s", - }, + ...defaultScenarios }, thresholds: { - 'http_req_duration{group:::Issuer creates credential schema}': ['max >= 0'], - 'http_reqs{group:::Issuer creates credential schema}': ['count >= 0'], - 'group_duration{group:::Issuer creates credential schema}': ['max >= 0'], - checks: ['rate==1'], - http_req_duration: ['p(95)<=100'], - }, -}; - + ...defaultThresholds + } +} export const issuer = new Issuer(); export function setup() { - group('Issuer publishes DID', function () { + group("Issuer publishes DID", function () { issuer.createUnpublishedDid(); issuer.publishDid(); }); - return { + return { issuerDid: issuer.did, }; } -export default (data: { issuerDid: string; }) => { - +export default (data: { issuerDid: string }) => { // This is the only way to pass data from setup to default issuer.did = data.issuerDid; - group('Issuer creates credential schema', function () { + group("Issuer creates credential schema", function () { issuer.createCredentialSchema(); }); }; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts index dfd9350ca4..a3fe3a3507 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/create-prism-did-test.ts @@ -1,23 +1,14 @@ import { Options } from 'k6/options'; import { Issuer } from '../../actors'; - +import {defaultScenarios, defaultThresholds} from "../../scenarios/default"; export let options: Options = { - scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1m", + scenarios: { + ...defaultScenarios }, - }, - thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], - }, -}; + thresholds: { + ...defaultThresholds + } +} const issuer = new Issuer(); diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts index 0fd5eb4644..177401414a 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/dids/did-publishing-test.ts @@ -1,20 +1,14 @@ import { Options } from 'k6/options'; import { Issuer } from '../../actors'; - +import {defaultScenarios, defaultThresholds} from "../../scenarios/default"; 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'], + scenarios: { + ...defaultScenarios }, - }; - + thresholds: { + ...defaultThresholds + } +} const issuer = new Issuer(); export default () => { diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts index 563a36bf11..c349baf18d 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/connection-flow-test.ts @@ -1,23 +1,14 @@ import { Options } from 'k6/options'; import { connectionFlow } from '../common'; - +import {defaultScenarios, defaultThresholds} from "../../scenarios/default"; export let options: Options = { scenarios: { - smoke: { - executor: 'constant-vus', - vus: 3, - duration: "1m", - }, + ...defaultScenarios }, thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], - }, -}; + ...defaultThresholds + } +} export default() => { connectionFlow(); diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/full-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/full-flow-test.ts deleted file mode 100644 index fe17b7148e..0000000000 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/full-flow-test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { group } from 'k6'; -import { Options } from 'k6/options'; -import { Issuer, Holder, Verifier } from '../../actors'; - -export let options: Options = { - vus: 1, - iterations: 1 -}; - -const issuer = new Issuer(); -const holder = new Holder(); -const verifier = new Verifier(); - -export function setup() { - group('Issuer publishes DID', function () { - issuer.createUnpublishedDid(); - issuer.publishDid(); - }); - - group('Holder creates unpublished DID', function () { - holder.createUnpublishedDid(); - }); - - return { issuerDid: issuer.did, holderDid: holder.did }; -} - -export default (data: { issuerDid: string; holderDid: string; }) => { - - issuer.did = data.issuerDid; - holder.did = data.holderDid; - - group('Holder connects with Issuer', function () { - issuer.createHolderConnection(); - holder.acceptIssuerConnection(issuer.connectionWithHolder!.invitation); - issuer.finalizeConnectionWithHolder(); - holder.finalizeConnectionWithIssuer(); - }); - - group('Issuer creates credential offer for Holder', function () { - issuer.createCredentialOffer(); - issuer.waitForCredentialOfferToBeSent(); - holder.waitAndAcceptCredentialOffer(); - issuer.receiveCredentialRequest(); - issuer.issueCredential(); - issuer.waitForCredentialToBeSent(); - holder.receiveCredential(); - }); - - group('Holder connects with Verifier', function () { - verifier.createHolderConnection(); - holder.acceptVerifierConnection(verifier.connectionWithHolder!.invitation); - verifier.finalizeConnectionWithHolder(); - holder.finalizeConnectionWithVerifier(); - }); - - group('Verifier requests proof from Holder', function () { - verifier.requestProof(); - holder.waitAndAcceptProofRequest(); - verifier.acknowledgeProof(); - }); - -}; diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts index c4f52a0094..7c1076e503 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/issuance-flow-test.ts @@ -2,25 +2,15 @@ import { group } from 'k6'; import { Options } from 'k6/options'; import {issuer, holder} from '../common'; import { CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client'; - +import {defaultScenarios, defaultThresholds} from "../../scenarios/default"; export let options: Options = { scenarios: { - smoke: { - executor: 'constant-vus', - vus: 5, - duration: "3m", - }, + ...defaultScenarios }, thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], - }, -}; - + ...defaultThresholds + } +} // This is setup code. It runs once at the beginning of the test, regardless of the number of VUs. export function setup() { diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts index 667c0d4c90..c99e964f59 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/flows/present-proof-flow-test.ts @@ -2,24 +2,15 @@ import { group } from 'k6'; import { Options } from 'k6/options'; import { Issuer, Holder, Verifier } from '../../actors'; import { CredentialSchemaResponse } from '@input-output-hk/prism-typescript-client'; - +import {defaultScenarios, defaultThresholds} from "../../scenarios/default"; export let options: Options = { scenarios: { - smoke: { - executor: 'constant-vus', - vus: 5, - duration: "3m", - }, + ...defaultScenarios }, thresholds: { - http_req_failed: [{ - threshold: 'rate==0', - abortOnFail: true, - }], - http_req_duration: ['p(95)<=500'], - checks: ['rate==1'], - }, -}; + ...defaultThresholds + } +} const issuer = new Issuer(); const holder = new Holder();