diff --git a/tests/performance-tests/atala-performance-tests-k6/src/actors/Issuer.ts b/tests/performance-tests/atala-performance-tests-k6/src/actors/Issuer.ts index fa377774fb..2d51f89aad 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/actors/Issuer.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/actors/Issuer.ts @@ -79,10 +79,14 @@ export class Issuer extends Actor { this.didService.waitForDidState(this.longFormDid!, "PUBLISHED"); } - createCredentialSchema() { - this.schema = this.credentialsService.createCredentialSchema(this.did!); + createCredentialSchema(schemaType: string = "json") { + this.schema = this.credentialsService.createCredentialSchema(this.did!, schemaType); } - + + createCredentialDefinition() { + this.credentialsService.createCredentialDefinition(this.did!, this.schema!); + } + /** * Creates a credential offer for the holder. */ diff --git a/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts b/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts index 5adfab1e37..bf4d6e8994 100644 --- a/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts +++ b/tests/performance-tests/atala-performance-tests-k6/src/common/Config.ts @@ -20,7 +20,7 @@ export const ISSUER_AGENT_URL = __ENV.ISSUER_AGENT_URL || "http://localhost:8080 * API key for the Issuer agent. * If not provided, the default value is an empty string. */ -export const ISSUER_AGENT_API_KEY = __ENV.ISSUER_AGENT_API_KEY || ""; +export const ISSUER_AGENT_API_KEY = __ENV.ISSUER_AGENT_API_KEY || "default"; /** * URL for the Holder agent. @@ -32,7 +32,7 @@ export const HOLDER_AGENT_URL = __ENV.HOLDER_AGENT_URL || "http://localhost:8090 * API key for the Holder agent. * If not provided, the default value is an empty string. */ -export const HOLDER_AGENT_API_KEY = __ENV.HOLDER_AGENT_API_KEY || ""; +export const HOLDER_AGENT_API_KEY = __ENV.HOLDER_AGENT_API_KEY || "default"; /** * URL for the Verifier agent. @@ -44,4 +44,4 @@ export const VERIFIER_AGENT_URL = __ENV.VERIFIER_AGENT_URL || "http://localhost: * API key for the Verifier agent. * If not provided, the default value is an empty string. */ -export const VERIFIER_AGENT_API_KEY = __ENV.VERIFIER_AGENT_API_KEY || ""; +export const VERIFIER_AGENT_API_KEY = __ENV.VERIFIER_AGENT_API_KEY || "default"; 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 c55a37ca1d..82316f6980 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 @@ -1,8 +1,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 { crypto } from "k6/experimental/webcrypto"; +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 {crypto} from "k6/experimental/webcrypto"; /** @@ -11,14 +11,14 @@ import { crypto } from "k6/experimental/webcrypto"; */ export class CredentialsService extends HttpService { - /** - * Creates a credential offer for a specific issuing DID and connection. - * @param {string} issuingDid - The issuing DID. - * @param {Connection} connection - The connection object. - * @returns {IssueCredentialRecord} The created credential offer record. - */ - createCredentialOffer(issuingDid: string, connection: Connection, schema: CredentialSchemaResponse): IssueCredentialRecord { - const payload = `{ + /** + * Creates a credential offer for a specific issuing DID and connection. + * @param {string} issuingDid - The issuing DID. + * @param {Connection} connection - The connection object. + * @returns {IssueCredentialRecord} The created credential offer record. + */ + createCredentialOffer(issuingDid: string, connection: Connection, schema: CredentialSchemaResponse): IssueCredentialRecord { + const payload = `{ "claims": { "emailAddress": "${crypto.randomUUID()}-@atala.io", "familyName": "Test", @@ -31,145 +31,186 @@ export class CredentialsService extends HttpService { "connectionId": "${connection.connectionId}", "automaticIssuance": false }`; - const res = this.post("issue-credentials/credential-offers", payload); - return this.toJson(res) as unknown as IssueCredentialRecord; - } + const res = this.post("issue-credentials/credential-offers", payload); + return this.toJson(res) as unknown as IssueCredentialRecord; + } - createCredentialSchema(issuingDid: string): CredentialSchemaResponse { - const payload = ` + createCredentialDefinition(issuingDid: string, schema: CredentialSchemaResponse) { + const payload = ` { "name": "${crypto.randomUUID()}}", + "description": "Birth certificate Anoncred Credential Definition", "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", - "schema": { - "$id": "https://example.com/driving-license-1.0", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "Driving License", - "type": "object", - "properties": { - "emailAddress": { - "type": "string", - "format": "email" - }, - "givenName": { - "type": "string" - }, - "familyName": { - "type": "string" - }, - "dateOfIssuance": { - "type": "string" - }, - "drivingLicenseID": { - "type": "string" - }, - "drivingClass": { - "type": "integer" - } - }, - "required": [ - "emailAddress", - "familyName", - "dateOfIssuance", - "drivingLicenseID", - "drivingClass" - ], - "additionalProperties": false - }, - "tags": [ - "driving", - "licence", - "id" - ], - "author": "${issuingDid}" + "tag": "Licence", + "author": "${issuingDid}", + "schemaId": "${ISSUER_AGENT_URL.replace("localhost", "host.docker.internal")}/schema-registry/schemas/${schema.guid}", + "signatureType": "CL", + "supportRevocation": false + } + `; + this.post("credential-definition-registry/definitions", payload); + } + + createCredentialSchema(issuingDid: string, schemaType: string = "json") { + const payload = (function () { + switch (schemaType) { + case "json": + return `{ + "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", + "schema": { + "$id": "https://example.com/driving-license-1.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Driving License", + "type": "object", + "properties": { + "emailAddress": { + "type": "string", + "format": "email" + }, + "givenName": { + "type": "string" + }, + "familyName": { + "type": "string" + }, + "dateOfIssuance": { + "type": "string" + }, + "drivingLicenseID": { + "type": "string" + }, + "drivingClass": { + "type": "integer" + } + }, + "required": [ + "emailAddress", + "familyName", + "dateOfIssuance", + "drivingLicenseID", + "drivingClass" + ], + "additionalProperties": false + }, + "tags": [ + "driving", + "licence", + "id" + ], + "author": "${issuingDid}" + }`; + case "anoncred": + return `{ + "name": "${crypto.randomUUID()}", + "version": "1.0.0", + "description": "Simple credential schema for the driving licence verifiable credential.", + "type": "AnoncredSchemaV1", + "schema": { + "name": "${crypto.randomUUID()}", + "version": "1.0.0", + "attrNames": [ + "emailAddress", + "familyName" + ], + "issuerId": "${issuingDid}" + }, + "author": "${issuingDid}", + "tags": [ + "Licence" + ] + }`; + default: + throw new Error(`Schema type ${schemaType} is not supported`); + } + })(); + const res = this.post("schema-registry/schemas", payload); + return this.toJson(res) as unknown as CredentialSchemaResponse; } - ` - const res = this.post("schema-registry/schemas", payload); - return this.toJson(res) as unknown as CredentialSchemaResponse; - } - /** - * Retrieves a specific credential record by ID. - * @param {IssueCredentialRecord} record - The credential record. - * @returns {IssueCredentialRecord} The credential record. - */ - getCredentialRecord(record: IssueCredentialRecord): IssueCredentialRecord { - const res = this.get(`issue-credentials/records/${record.recordId}`); - return this.toJson(res) as unknown as IssueCredentialRecord; - } + /** + * Retrieves a specific credential record by ID. + * @param {IssueCredentialRecord} record - The credential record. + * @returns {IssueCredentialRecord} The credential record. + */ + getCredentialRecord(record: IssueCredentialRecord): IssueCredentialRecord { + const res = this.get(`issue-credentials/records/${record.recordId}`); + return this.toJson(res) as unknown as IssueCredentialRecord; + } - /** - * Retrieves all credential records. - * @returns {IssueCredentialRecord[]} An array of credential records. - */ - getCredentialRecords(thid: string): IssueCredentialRecord[] { - const res = this.get(`issue-credentials/records?thid=${thid}`); - return this.toJson(res).contents as unknown as IssueCredentialRecord[]; - } + /** + * Retrieves all credential records. + * @returns {IssueCredentialRecord[]} An array of credential records. + */ + getCredentialRecords(thid: string): IssueCredentialRecord[] { + const res = this.get(`issue-credentials/records?thid=${thid}`); + return this.toJson(res).contents as unknown as IssueCredentialRecord[]; + } - /** - * Accepts a credential offer and associates it with a subject DID. - * @param {IssueCredentialRecord} record - The credential record. - * @param {string} subjectDid - The subject DID. - * @returns {IssueCredentialRecord} The updated credential record. - */ - acceptCredentialOffer(record: IssueCredentialRecord, subjectDid: string): IssueCredentialRecord { - const payload = { subjectId: subjectDid }; - const res = this.post(`issue-credentials/records/${record.recordId}/accept-offer`, payload, 200); - return this.toJson(res) as unknown as IssueCredentialRecord; - } + /** + * Accepts a credential offer and associates it with a subject DID. + * @param {IssueCredentialRecord} record - The credential record. + * @param {string} subjectDid - The subject DID. + * @returns {IssueCredentialRecord} The updated credential record. + */ + acceptCredentialOffer(record: IssueCredentialRecord, subjectDid: string): IssueCredentialRecord { + const payload = {subjectId: subjectDid}; + const res = this.post(`issue-credentials/records/${record.recordId}/accept-offer`, payload, 200); + return this.toJson(res) as unknown as IssueCredentialRecord; + } - /** - * Issues a credential for a specific credential record. - * @param {IssueCredentialRecord} record - The credential record. - * @returns {IssueCredentialRecord} The updated credential record. - */ - issueCredential(record: IssueCredentialRecord): IssueCredentialRecord { - const res = this.post(`issue-credentials/records/${record.recordId}/issue-credential`, null, 200); - return this.toJson(res) as unknown as IssueCredentialRecord; - } + /** + * Issues a credential for a specific credential record. + * @param {IssueCredentialRecord} record - The credential record. + * @returns {IssueCredentialRecord} The updated credential record. + */ + issueCredential(record: IssueCredentialRecord): IssueCredentialRecord { + const res = this.post(`issue-credentials/records/${record.recordId}/issue-credential`, null, 200); + return this.toJson(res) as unknown as IssueCredentialRecord; + } - /** - * Waits for a credential offer to be received. - * @returns {IssueCredentialRecord} The received credential offer record. - * @throws {Error} If the credential offer is not received within the maximum iterations. - */ - waitForCredentialOffer(thid: string): IssueCredentialRecord { - let iterations = 0; - let record: IssueCredentialRecord | undefined; - do { - // console.log(`Waiting for credential offer with thid=${thid}`) - record = this.getCredentialRecords(thid).find( - r => r.thid === thid && r.protocolState === "OfferReceived" - ); - if (record) { - return record; - } - sleep(WAITING_LOOP_PAUSE_INTERVAL); - iterations++; - } while (iterations < WAITING_LOOP_MAX_ITERATIONS); - throw new Error(`Record with thid=${thid} not achieved during the waiting loop`); - } + /** + * Waits for a credential offer to be received. + * @returns {IssueCredentialRecord} The received credential offer record. + * @throws {Error} If the credential offer is not received within the maximum iterations. + */ + waitForCredentialOffer(thid: string): IssueCredentialRecord { + let iterations = 0; + let record: IssueCredentialRecord | undefined; + do { + // console.log(`Waiting for credential offer with thid=${thid}`) + record = this.getCredentialRecords(thid).find( + r => r.thid === thid && r.protocolState === "OfferReceived" + ); + if (record) { + return record; + } + sleep(WAITING_LOOP_PAUSE_INTERVAL); + iterations++; + } while (iterations < WAITING_LOOP_MAX_ITERATIONS); + throw new Error(`Record with thid=${thid} not achieved during the waiting loop`); + } - /** - * Waits for a credential to reach a specific state. - * @param {IssueCredentialRecord} credentialRecord - The credential record. - * @param {string} state - The required state. - * @throws {Error} If the credential state does not reach the required state within the maximum iterations. - */ - waitForCredentialState(credentialRecord: IssueCredentialRecord, state: string) { - let iterations = 0; - let currentState; - do { - const response = this.getCredentialRecord(credentialRecord); - currentState = response.protocolState; - sleep(WAITING_LOOP_PAUSE_INTERVAL); - iterations++; - } while (currentState !== state && iterations < WAITING_LOOP_MAX_ITERATIONS); - if (currentState !== state) { - throw new Error(`Credential is not ${state} after the waiting loop`); + /** + * Waits for a credential to reach a specific state. + * @param {IssueCredentialRecord} credentialRecord - The credential record. + * @param {string} state - The required state. + * @throws {Error} If the credential state does not reach the required state within the maximum iterations. + */ + waitForCredentialState(credentialRecord: IssueCredentialRecord, state: string) { + let iterations = 0; + let currentState; + do { + const response = this.getCredentialRecord(credentialRecord); + currentState = response.protocolState; + sleep(WAITING_LOOP_PAUSE_INTERVAL); + iterations++; + } while (currentState !== state && iterations < WAITING_LOOP_MAX_ITERATIONS); + if (currentState !== state) { + throw new Error(`Credential is not ${state} after the waiting loop`); + } } - } } diff --git a/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-definition-test.ts b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-definition-test.ts new file mode 100644 index 0000000000..3439f423a4 --- /dev/null +++ b/tests/performance-tests/atala-performance-tests-k6/src/tests/credentials/credential-definition-test.ts @@ -0,0 +1,41 @@ +import { group } from "k6"; +import { Options } from "k6/options"; +import { Issuer } from "../../actors"; +import { defaultOptions } from "../../scenarios/default"; +import merge from "ts-deepmerge"; +import { CredentialSchemaResponse } from "@input-output-hk/prism-typescript-client"; + +export const localOptions: Options = { + // Important to have this threshold to have a special line for this group in the report + thresholds: { + 'group_duration{group:::Issuer creates credential definition}': ['avg > 0'] + } +} +export let options: Options = merge(localOptions, defaultOptions) + +export const issuer = new Issuer(); + +export function setup() { + group("Issuer publishes DID", function () { + issuer.createUnpublishedDid(); + issuer.publishDid(); + }); + + group("Issuer creates credential schema", function () { + issuer.createCredentialSchema("anoncred"); + }); + + return { + issuerDid: issuer.did, schema: issuer.schema + }; +} + +export default (data: { issuerDid: string, schema: CredentialSchemaResponse }) => { + // This is the only way to pass data from setup to default + issuer.did = data.issuerDid; + issuer.schema = data.schema; + + group("Issuer creates credential definition", function () { + issuer.createCredentialDefinition(); + }); +};