From 74082916d9a2b654ca4b8d9a1dfc8b4afd726adb Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 12:32:03 +0530 Subject: [PATCH 01/15] feat: Add jsonld tests --- tests/credential/issue-verify-flow.spec.ts | 50 +++++++++++++++++++ .../credential/credential-issue-jsonld.json | 11 ++++ .../credential/credential-issue-jwt.json | 11 ++++ 3 files changed, 72 insertions(+) create mode 100644 tests/credential/issue-verify-flow.spec.ts create mode 100644 tests/payloads/credential/credential-issue-jsonld.json create mode 100644 tests/payloads/credential/credential-issue-jwt.json diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts new file mode 100644 index 00000000..5b9a87c3 --- /dev/null +++ b/tests/credential/issue-verify-flow.spec.ts @@ -0,0 +1,50 @@ +import { test, expect } from '@playwright/test'; +import { StatusCodes } from 'http-status-codes'; +import * as fs from 'fs'; +import { CheqdNetwork, VerificationMethods } from '@cheqd/sdk'; +import { ID_TYPE } from '../constants'; + +test.use({ storageState: 'playwright/.auth/user.json' }); + +const PAYLOADS_BASE_PATH = './tests/payloads/credential'; + +let issuerDid: string; + +test('Create issuer Did', async ({ request }) => { + // send request to create DID + let response = await request.post(`/did/create`, { + data: + `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}&` + + `verificationMethodType=${VerificationMethods.Ed255192018}`, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + }); + issuerDid = (await response.json()).did; + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); +}); + +test(' Issue a jwt credential', async ({ request }) => { + const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); + credentialData.issuerDid = issuerDid; + const response = await request.post(`/credential/issue`, { + data: credentialData, + headers: { + 'Content-Type': 'application/json', + }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); +}); + +test(' Issue a jsonLD credential', async ({ request }) => { + const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); + credentialData.issuerDid = issuerDid; + const response = await request.post(`/credential/issue`, { + data: JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jsonld.json`, 'utf-8')), + headers: { + 'Content-Type': 'application/json', + }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); +}); diff --git a/tests/payloads/credential/credential-issue-jsonld.json b/tests/payloads/credential/credential-issue-jsonld.json new file mode 100644 index 00000000..0ff172c0 --- /dev/null +++ b/tests/payloads/credential/credential-issue-jsonld.json @@ -0,0 +1,11 @@ +{ + "issuerDid": "did:cheqd:testnet:ae0f3d22-cb16-4515-930c-b9a7c7bdfdc0", + "subjectDid": "did:key:z6MkqJNR1DHxX2qxqDYx9tNDsXoNRVpaVvJkLPeCYqaARz1n", + "attributes": { + "gender": "male", + "name": "Bob" + }, + "@context": ["https://schema.org"], + "type": ["Person"], + "format": "lds" +} diff --git a/tests/payloads/credential/credential-issue-jwt.json b/tests/payloads/credential/credential-issue-jwt.json new file mode 100644 index 00000000..e4306e12 --- /dev/null +++ b/tests/payloads/credential/credential-issue-jwt.json @@ -0,0 +1,11 @@ +{ + "issuerDid": "did:cheqd:testnet:ae0f3d22-cb16-4515-930c-b9a7c7bdfdc0", + "subjectDid": "did:key:z6MkqJNR1DHxX2qxqDYx9tNDsXoNRVpaVvJkLPeCYqaARz1n", + "attributes": { + "gender": "male", + "name": "Bob" + }, + "@context": ["https://schema.org"], + "type": ["Person"], + "format": "jwt" +} From 554a8b313053a315e858818f3ad49daf4117c830 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 14:02:10 +0530 Subject: [PATCH 02/15] Debug --- tests/credential/issue-verify-flow.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index 5b9a87c3..672062e4 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -19,6 +19,7 @@ test('Create issuer Did', async ({ request }) => { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, }); issuerDid = (await response.json()).did; + console.log(`issuerDid: ${issuerDid}`); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); }); @@ -32,6 +33,7 @@ test(' Issue a jwt credential', async ({ request }) => { 'Content-Type': 'application/json', }, }); + console.log(await response.json()); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); }); @@ -45,6 +47,7 @@ test(' Issue a jsonLD credential', async ({ request }) => { 'Content-Type': 'application/json', }, }); + console.log(await response.json()); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); }); From 64653c840b03ab85edd789eb66d16b06920d58d5 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 15:29:01 +0530 Subject: [PATCH 03/15] use did list --- tests/credential/issue-verify-flow.spec.ts | 33 +++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index 672062e4..ee397b02 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -1,8 +1,6 @@ import { test, expect } from '@playwright/test'; import { StatusCodes } from 'http-status-codes'; import * as fs from 'fs'; -import { CheqdNetwork, VerificationMethods } from '@cheqd/sdk'; -import { ID_TYPE } from '../constants'; test.use({ storageState: 'playwright/.auth/user.json' }); @@ -10,25 +8,28 @@ const PAYLOADS_BASE_PATH = './tests/payloads/credential'; let issuerDid: string; -test('Create issuer Did', async ({ request }) => { - // send request to create DID - let response = await request.post(`/did/create`, { - data: - `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}&` + - `verificationMethodType=${VerificationMethods.Ed255192018}`, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - }); - issuerDid = (await response.json()).did; - console.log(`issuerDid: ${issuerDid}`); - expect(response).toBeOK(); - expect(response.status()).toBe(StatusCodes.OK); -}); +// test('Create issuer Did', async ({ request }) => { +// // send request to create DID +// let response = await request.post(`/did/create`, { +// data: +// `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}&` + +// `verificationMethodType=${VerificationMethods.Ed255192018}`, +// headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, +// }); +// issuerDid = (await response.json()).did; +// console.log(`issuerDid: ${issuerDid}`); +// expect(response).toBeOK(); +// expect(response.status()).toBe(StatusCodes.OK); +// }); test(' Issue a jwt credential', async ({ request }) => { + const dids = (await (await request.get(`/did/list`)).json()) as string[]; + issuerDid = dids.find((did) => did.startsWith('did:cheqd:testnet')); + console.log(`issuerDid: ${issuerDid}`); const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); credentialData.issuerDid = issuerDid; const response = await request.post(`/credential/issue`, { - data: credentialData, + data: JSON.stringify(credentialData), headers: { 'Content-Type': 'application/json', }, From 16971977b412a19c69a1b058ac484ab2c0cb9694 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 15:52:08 +0530 Subject: [PATCH 04/15] Use existing did in json --- tests/credential/issue-verify-flow.spec.ts | 2 +- tests/payloads/credential/credential-issue-jsonld.json | 2 +- tests/payloads/credential/credential-issue-jwt.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index ee397b02..30be112d 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -43,7 +43,7 @@ test(' Issue a jsonLD credential', async ({ request }) => { const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); credentialData.issuerDid = issuerDid; const response = await request.post(`/credential/issue`, { - data: JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jsonld.json`, 'utf-8')), + data: JSON.stringify(credentialData), headers: { 'Content-Type': 'application/json', }, diff --git a/tests/payloads/credential/credential-issue-jsonld.json b/tests/payloads/credential/credential-issue-jsonld.json index 0ff172c0..3db61b04 100644 --- a/tests/payloads/credential/credential-issue-jsonld.json +++ b/tests/payloads/credential/credential-issue-jsonld.json @@ -1,5 +1,5 @@ { - "issuerDid": "did:cheqd:testnet:ae0f3d22-cb16-4515-930c-b9a7c7bdfdc0", + "issuerDid": "did:cheqd:testnet:7zy9FJ96jDzjZPLfrsgxqx", "subjectDid": "did:key:z6MkqJNR1DHxX2qxqDYx9tNDsXoNRVpaVvJkLPeCYqaARz1n", "attributes": { "gender": "male", diff --git a/tests/payloads/credential/credential-issue-jwt.json b/tests/payloads/credential/credential-issue-jwt.json index e4306e12..2bb4b86a 100644 --- a/tests/payloads/credential/credential-issue-jwt.json +++ b/tests/payloads/credential/credential-issue-jwt.json @@ -1,5 +1,5 @@ { - "issuerDid": "did:cheqd:testnet:ae0f3d22-cb16-4515-930c-b9a7c7bdfdc0", + "issuerDid": "did:cheqd:testnet:7zy9FJ96jDzjZPLfrsgxqx", "subjectDid": "did:key:z6MkqJNR1DHxX2qxqDYx9tNDsXoNRVpaVvJkLPeCYqaARz1n", "attributes": { "gender": "male", From 336f5f81e947186cc8f2d16bc92dcc0223e74541 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 17:32:23 +0530 Subject: [PATCH 05/15] assert responses --- src/types/shared.ts | 2 +- src/types/swagger-types.ts | 2 +- tests/credential/issue-verify-flow.spec.ts | 20 ++++---------------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/types/shared.ts b/src/types/shared.ts index cfd36232..5111f6cc 100644 --- a/src/types/shared.ts +++ b/src/types/shared.ts @@ -201,7 +201,7 @@ export type CheckStatusListUnsuccessfulResponseBody = { export type VerifyPresentationResponseBody = { verified: false; error: string; -} +}; export type SearchStatusListQuery = { did: string; diff --git a/src/types/swagger-types.ts b/src/types/swagger-types.ts index 24552c53..5b4b683c 100644 --- a/src/types/swagger-types.ts +++ b/src/types/swagger-types.ts @@ -61,7 +61,7 @@ * type: string * enum: * - jwt - * - lds + * - jsonld * example: jwt * credentialStatus: * description: Optional `credentialStatus` properties for VC revocation or suspension. Takes `statusListName` and `statusListPurpose` as inputs. diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index 30be112d..261e6156 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -8,20 +8,6 @@ const PAYLOADS_BASE_PATH = './tests/payloads/credential'; let issuerDid: string; -// test('Create issuer Did', async ({ request }) => { -// // send request to create DID -// let response = await request.post(`/did/create`, { -// data: -// `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}&` + -// `verificationMethodType=${VerificationMethods.Ed255192018}`, -// headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, -// }); -// issuerDid = (await response.json()).did; -// console.log(`issuerDid: ${issuerDid}`); -// expect(response).toBeOK(); -// expect(response.status()).toBe(StatusCodes.OK); -// }); - test(' Issue a jwt credential', async ({ request }) => { const dids = (await (await request.get(`/did/list`)).json()) as string[]; issuerDid = dids.find((did) => did.startsWith('did:cheqd:testnet')); @@ -34,9 +20,10 @@ test(' Issue a jwt credential', async ({ request }) => { 'Content-Type': 'application/json', }, }); - console.log(await response.json()); + const credential = await response.json(); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); + expect(credential.proof.type).toBe('JwtProof2020'); }); test(' Issue a jsonLD credential', async ({ request }) => { @@ -48,7 +35,8 @@ test(' Issue a jsonLD credential', async ({ request }) => { 'Content-Type': 'application/json', }, }); - console.log(await response.json()); + const credential = await response.json(); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); + expect(credential.proof.type).toBe('Ed25519Signature2018'); }); From 09a9ab214081a5a0977e0a31954b4ce07a6108c4 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 18:40:32 +0530 Subject: [PATCH 06/15] Update format in payload --- tests/payloads/credential/credential-issue-jsonld.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/payloads/credential/credential-issue-jsonld.json b/tests/payloads/credential/credential-issue-jsonld.json index 3db61b04..fc58f12a 100644 --- a/tests/payloads/credential/credential-issue-jsonld.json +++ b/tests/payloads/credential/credential-issue-jsonld.json @@ -7,5 +7,5 @@ }, "@context": ["https://schema.org"], "type": ["Person"], - "format": "lds" + "format": "jsonld" } From 9a3213cd30527df899335626b512e3e30ed311f9 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 20:28:02 +0530 Subject: [PATCH 07/15] Minor fix --- tests/credential/issue-verify-flow.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index 261e6156..d6097b0b 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -21,13 +21,14 @@ test(' Issue a jwt credential', async ({ request }) => { }, }); const credential = await response.json(); + console.log(credential); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(credential.proof.type).toBe('JwtProof2020'); }); test(' Issue a jsonLD credential', async ({ request }) => { - const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); + const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jsonld.json`, 'utf-8')); credentialData.issuerDid = issuerDid; const response = await request.post(`/credential/issue`, { data: JSON.stringify(credentialData), @@ -36,6 +37,7 @@ test(' Issue a jsonLD credential', async ({ request }) => { }, }); const credential = await response.json(); + console.log(credential); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(credential.proof.type).toBe('Ed25519Signature2018'); From 82106415f2e0dd2131e9af4703b12b73c3360bf5 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Mon, 11 Dec 2023 20:53:22 +0530 Subject: [PATCH 08/15] Add assertionMethod --- tests/credential/issue-verify-flow.spec.ts | 7 ------- tests/payloads/credential/credential-issue-jsonld.json | 2 +- tests/payloads/credential/credential-issue-jwt.json | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index d6097b0b..5a50d701 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -9,11 +9,7 @@ const PAYLOADS_BASE_PATH = './tests/payloads/credential'; let issuerDid: string; test(' Issue a jwt credential', async ({ request }) => { - const dids = (await (await request.get(`/did/list`)).json()) as string[]; - issuerDid = dids.find((did) => did.startsWith('did:cheqd:testnet')); - console.log(`issuerDid: ${issuerDid}`); const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); - credentialData.issuerDid = issuerDid; const response = await request.post(`/credential/issue`, { data: JSON.stringify(credentialData), headers: { @@ -21,7 +17,6 @@ test(' Issue a jwt credential', async ({ request }) => { }, }); const credential = await response.json(); - console.log(credential); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(credential.proof.type).toBe('JwtProof2020'); @@ -29,7 +24,6 @@ test(' Issue a jwt credential', async ({ request }) => { test(' Issue a jsonLD credential', async ({ request }) => { const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jsonld.json`, 'utf-8')); - credentialData.issuerDid = issuerDid; const response = await request.post(`/credential/issue`, { data: JSON.stringify(credentialData), headers: { @@ -37,7 +31,6 @@ test(' Issue a jsonLD credential', async ({ request }) => { }, }); const credential = await response.json(); - console.log(credential); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(credential.proof.type).toBe('Ed25519Signature2018'); diff --git a/tests/payloads/credential/credential-issue-jsonld.json b/tests/payloads/credential/credential-issue-jsonld.json index fc58f12a..e84a355a 100644 --- a/tests/payloads/credential/credential-issue-jsonld.json +++ b/tests/payloads/credential/credential-issue-jsonld.json @@ -1,5 +1,5 @@ { - "issuerDid": "did:cheqd:testnet:7zy9FJ96jDzjZPLfrsgxqx", + "issuerDid": "did:cheqd:testnet:4JdgsZ4A8LegKXdsKE3v6X", "subjectDid": "did:key:z6MkqJNR1DHxX2qxqDYx9tNDsXoNRVpaVvJkLPeCYqaARz1n", "attributes": { "gender": "male", diff --git a/tests/payloads/credential/credential-issue-jwt.json b/tests/payloads/credential/credential-issue-jwt.json index 2bb4b86a..a2c36598 100644 --- a/tests/payloads/credential/credential-issue-jwt.json +++ b/tests/payloads/credential/credential-issue-jwt.json @@ -1,5 +1,5 @@ { - "issuerDid": "did:cheqd:testnet:7zy9FJ96jDzjZPLfrsgxqx", + "issuerDid": "did:cheqd:testnet:4JdgsZ4A8LegKXdsKE3v6X", "subjectDid": "did:key:z6MkqJNR1DHxX2qxqDYx9tNDsXoNRVpaVvJkLPeCYqaARz1n", "attributes": { "gender": "male", From 81a14f8b2ff3ee3a73448b1f8a37548f3ce726d8 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Tue, 12 Dec 2023 13:35:48 +0530 Subject: [PATCH 09/15] Add verify credential tests --- src/static/swagger.json | 2 +- tests/credential/issue-verify-flow.spec.ts | 56 ++++++++++++++++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/static/swagger.json b/src/static/swagger.json index 08b8df71..0ae8a0b3 100644 --- a/src/static/swagger.json +++ b/src/static/swagger.json @@ -1495,7 +1495,7 @@ "type": "string", "enum": [ "jwt", - "lds" + "jsonld" ], "example": "jwt" }, diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index 5a50d701..8f90e509 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -1,3 +1,5 @@ +import type { VerifiableCredential } from '@veramo/core'; + import { test, expect } from '@playwright/test'; import { StatusCodes } from 'http-status-codes'; import * as fs from 'fs'; @@ -6,7 +8,7 @@ test.use({ storageState: 'playwright/.auth/user.json' }); const PAYLOADS_BASE_PATH = './tests/payloads/credential'; -let issuerDid: string; +let jwtCredential: VerifiableCredential, jsonldCredential: VerifiableCredential; test(' Issue a jwt credential', async ({ request }) => { const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); @@ -16,10 +18,37 @@ test(' Issue a jwt credential', async ({ request }) => { 'Content-Type': 'application/json', }, }); - const credential = await response.json(); + jwtCredential = await response.json(); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); - expect(credential.proof.type).toBe('JwtProof2020'); + expect(jwtCredential.proof.type).toBe('JwtProof2020'); +}); + +test(' Verify a jwt credential', async ({ request }) => { + const response = await request.post(`/credential/verify`, { + data: JSON.stringify({ + credential: jwtCredential.proof.jwt, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const result = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(result.verified).toBe(true); +}); + +test(' Issue a jwt credential with a deactivated DID', async ({ request }) => { + const credentialData = JSON.parse(fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt.json`, 'utf-8')); + credentialData.issuerDid = 'did:cheqd:testnet:edce6dfb-b59c-493b-a4b8-1d16a6184349'; + const response = await request.post(`/credential/issue`, { + data: JSON.stringify(credentialData), + headers: { + 'Content-Type': 'application/json', + }, + }); + expect(response.status()).toBe(StatusCodes.BAD_REQUEST); }); test(' Issue a jsonLD credential', async ({ request }) => { @@ -30,8 +59,25 @@ test(' Issue a jsonLD credential', async ({ request }) => { 'Content-Type': 'application/json', }, }); - const credential = await response.json(); + jsonldCredential = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(jsonldCredential.proof.type).toBe('Ed25519Signature2018'); +}); + +test(' Verify a jsonld credential', async ({ request }) => { + const response = await request.post(`/credential/verify`, { + data: JSON.stringify({ + credential: jsonldCredential, + fetchRemoteContexts: true, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const result = await response.json(); + console.log(result); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); - expect(credential.proof.type).toBe('Ed25519Signature2018'); + expect(result.verified).toBe(true); }); From b2ccba73dfd5c017c2ea34b575f7aefcdf4770c9 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Wed, 13 Dec 2023 12:58:35 +0530 Subject: [PATCH 10/15] Add revocation tests --- tests/credential/issue-verify-flow.spec.ts | 1 - tests/credential/revocation-flow.spec.ts | 72 +++++++++++++ tests/credential/suspension-flow.spec.ts | 102 ++++++++++++++++++ .../credential-issue-jwt-revocation.json | 15 +++ 4 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 tests/credential/revocation-flow.spec.ts create mode 100644 tests/credential/suspension-flow.spec.ts create mode 100644 tests/payloads/credential/credential-issue-jwt-revocation.json diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index 8f90e509..56c3658a 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -76,7 +76,6 @@ test(' Verify a jsonld credential', async ({ request }) => { }, }); const result = await response.json(); - console.log(result); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(result.verified).toBe(true); diff --git a/tests/credential/revocation-flow.spec.ts b/tests/credential/revocation-flow.spec.ts new file mode 100644 index 00000000..0361f289 --- /dev/null +++ b/tests/credential/revocation-flow.spec.ts @@ -0,0 +1,72 @@ +import type { VerifiableCredential } from '@veramo/core'; + +import { test, expect } from '@playwright/test'; +import { StatusCodes } from 'http-status-codes'; +import * as fs from 'fs'; + +test.use({ storageState: 'playwright/.auth/user.json' }); + +const PAYLOADS_BASE_PATH = './tests/payloads/credential'; + +let jwtCredential: VerifiableCredential; + +test(' Issue a jwt credential with revocation statuslist', async ({ request }) => { + const credentialData = JSON.parse( + fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt-revocation.json`, 'utf-8') + ); + const response = await request.post(`/credential/issue`, { + data: JSON.stringify(credentialData), + headers: { + 'Content-Type': 'application/json', + }, + }); + jwtCredential = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(jwtCredential.proof.type).toBe('JwtProof2020'); +}); + +test(" Verify a credential's revocation status", async ({ request }) => { + const response = await request.post(`/credential/verify?verifyStatus=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const result = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(result.verified).toBe(true); + expect(result.revoked).toBe(false); +}); + +test(' Verify a credential status after revocation', async ({ request }) => { + const response = await request.post(`/credential/revoke?publish=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const result = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(result.revoked).toBe(true); + + const verificationResponse = await request.post(`/credential/verify?verifyStatus=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const verificationResult = await response.json(); + expect(verificationResponse).toBeOK(); + expect(verificationResponse.status()).toBe(StatusCodes.OK); + expect(verificationResult.verified).toBe(true); + expect(result.revoked).toBe(true); +}); diff --git a/tests/credential/suspension-flow.spec.ts b/tests/credential/suspension-flow.spec.ts new file mode 100644 index 00000000..de9cc3d3 --- /dev/null +++ b/tests/credential/suspension-flow.spec.ts @@ -0,0 +1,102 @@ +import type { VerifiableCredential } from '@veramo/core'; + +import { test, expect } from '@playwright/test'; +import { StatusCodes } from 'http-status-codes'; +import * as fs from 'fs'; + +test.use({ storageState: 'playwright/.auth/user.json' }); + +const PAYLOADS_BASE_PATH = './tests/payloads/credential'; + +let jwtCredential: VerifiableCredential; + +test(' Issue a jwt credential with suspension statuslist', async ({ request }) => { + const credentialData = JSON.parse( + fs.readFileSync(`${PAYLOADS_BASE_PATH}/credential-issue-jwt-revocation.json`, 'utf-8') + ); + credentialData.credentialStatus.statusPurpose = 'suspension'; + const response = await request.post(`/credential/issue`, { + data: JSON.stringify(credentialData), + headers: { + 'Content-Type': 'application/json', + }, + }); + jwtCredential = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(jwtCredential.proof.type).toBe('JwtProof2020'); +}); + +test(" Verify a credential's suspension status", async ({ request }) => { + const response = await request.post(`/credential/verify?verifyStatus=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const result = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(result.verified).toBe(true); + expect(result.suspended).toBe(false); +}); + +test(' Verify a credential status after suspension', async ({ request }) => { + const response = await request.post(`/credential/suspend?publish=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const result = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(result.suspended).toBe(true); + + const verificationResponse = await request.post(`/credential/verify?verifyStatus=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const verificationResult = await response.json(); + expect(verificationResponse).toBeOK(); + expect(verificationResponse.status()).toBe(StatusCodes.OK); + expect(verificationResult.verified).toBe(true); + expect(result.suspended).toBe(true); +}); + +test(' Verify a credential status after reinstating', async ({ request }) => { + const response = await request.post(`/credential/reinstate?publish=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const result = await response.json(); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + expect(result.suspended).toBe(false); + + const verificationResponse = await request.post(`/credential/verify?verifyStatus=true`, { + data: JSON.stringify({ + credential: jwtCredential, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + const verificationResult = await response.json(); + expect(verificationResponse).toBeOK(); + expect(verificationResponse.status()).toBe(StatusCodes.OK); + expect(verificationResult.verified).toBe(true); + expect(result.suspended).toBe(false); +}); diff --git a/tests/payloads/credential/credential-issue-jwt-revocation.json b/tests/payloads/credential/credential-issue-jwt-revocation.json new file mode 100644 index 00000000..050d1f9f --- /dev/null +++ b/tests/payloads/credential/credential-issue-jwt-revocation.json @@ -0,0 +1,15 @@ +{ + "issuerDid": "did:cheqd:testnet:4JdgsZ4A8LegKXdsKE3v6X", + "subjectDid": "did:key:z6MkqJNR1DHxX2qxqDYx9tNDsXoNRVpaVvJkLPeCYqaARz1n", + "attributes": { + "gender": "male", + "name": "Bob" + }, + "@context": ["https://schema.org"], + "type": ["Person"], + "format": "jwt", + "credentialStatus": { + "statusPurpose": "revocation", + "statusListName": "testingstatuslist" + } +} From b5a3edfe772d9d0bcb69736bcee03fd1bb7504bc Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Wed, 13 Dec 2023 13:58:59 +0530 Subject: [PATCH 11/15] Run tests with one worker --- playwright.config.ts | 114 +++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index a8e16dd8..d9e39377 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -4,81 +4,81 @@ import { defineConfig, devices } from '@playwright/test'; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - // Directory where all tests are located - testDir: './tests', - - // Start local dev server before starting the tests - webServer: { - command: 'npm run start', - url: 'http://localhost:3000', - reuseExistingServer: !process.env.CI, - }, - - // Shared settings for all the projects below. - // Docs: https://playwright.dev/docs/api/class-testoptions - use: { - // Base URL to use in actions like `await page.goto('/')` - baseURL: 'http://localhost:3000', - - // Set whether to record traces + // Directory where all tests are located + testDir: './tests', + + // Start local dev server before starting the tests + webServer: { + command: 'npm run start', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI, + }, + + // Shared settings for all the projects below. + // Docs: https://playwright.dev/docs/api/class-testoptions + use: { + // Base URL to use in actions like `await page.goto('/')` + baseURL: 'http://localhost:3000', + + // Set whether to record traces // Docs: https://playwright.dev/docs/api/class-testoptions#test-options-trace trace: 'retain-on-failure', - // Set whether to record screenshots + // Set whether to record screenshots // Docs: https://playwright.dev/docs/api/class-testoptions#test-options-screenshot screenshot: 'off', - // Set whether to record videos + // Set whether to record videos // Docs: https://playwright.dev/docs/api/class-testoptions#test-options-video video: 'off', - }, + }, - // Reporter to use for test results + // Reporter to use for test results // Uses GitHub Actions reporter on CI, otherwise uses HTML reporter // Docs: https://playwright.dev/docs/test-reporters reporter: process.env.CI ? 'github' : 'html', - // Fail the build on CI if you accidentally left test.only in the source code + // Fail the build on CI if you accidentally left test.only in the source code // Docs: https://playwright.dev/docs/api/class-testconfig#test-config-forbid-only - forbidOnly: !!process.env.CI, - - // The maximum number of retry attempts given to failed tests + forbidOnly: !!process.env.CI, + + // The maximum number of retry attempts given to failed tests // Docs: https://playwright.dev/docs/api/class-testconfig#test-config-retries - retries: process.env.CI ? 2 : 0, + retries: process.env.CI ? 2 : 0, + + // Whether to run tests in parallel + // Docs: https://playwright.dev/docs/api/class-testconfig#test-config-fully-parallel + fullyParallel: false, - // Whether to run tests in parallel - // Docs: https://playwright.dev/docs/api/class-testconfig#test-config-fully-parallel - fullyParallel: false, - - // Number of parallel workers OR %age of logical CPUs to use + // Number of parallel workers OR %age of logical CPUs to use // Github Actions runners have 2 logical CPU cores - // Defaults to half of the logical CPU cores available + // Defaults to half of the logical CPU cores available // Docs: https://playwright.dev/docs/api/class-testconfig#test-config-workers - workers: process.env.CI ? 2 : undefined, - - // Limit the numbers of failures to set a fail-fast strategy on CI - // Docs: https://playwright.dev/docs/api/class-testconfig#test-config-max-failures - maxFailures: process.env.CI ? 5 : undefined, - - // Configure project specific settings - // Docs: https://playwright.dev/docs/test-projects - projects: [ - { - name: 'setup', - testMatch: /.*\.setup\.ts/ - }, - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - // Use prepared auth state. - storageState: 'playwright/.auth/user.json', - }, - dependencies: ['setup'], - }, - ], - - // Timeout for each test in milliseconds + workers: process.env.CI ? 1 : undefined, + + // Limit the numbers of failures to set a fail-fast strategy on CI + // Docs: https://playwright.dev/docs/api/class-testconfig#test-config-max-failures + maxFailures: process.env.CI ? 5 : undefined, + + // Configure project specific settings + // Docs: https://playwright.dev/docs/test-projects + projects: [ + { + name: 'setup', + testMatch: /.*\.setup\.ts/, + }, + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + // Use prepared auth state. + storageState: 'playwright/.auth/user.json', + }, + dependencies: ['setup'], + }, + ], + + // Timeout for each test in milliseconds // Docs: https://playwright.dev/docs/test-timeouts timeout: 60 * 1000, From a7c47eb388bd9186c52e8f8baa18623fbe309722 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Wed, 13 Dec 2023 14:09:55 +0530 Subject: [PATCH 12/15] fix minor issue --- tests/credential/revocation-flow.spec.ts | 5 +++-- tests/credential/suspension-flow.spec.ts | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/credential/revocation-flow.spec.ts b/tests/credential/revocation-flow.spec.ts index 0361f289..6133fa17 100644 --- a/tests/credential/revocation-flow.spec.ts +++ b/tests/credential/revocation-flow.spec.ts @@ -55,6 +55,7 @@ test(' Verify a credential status after revocation', async ({ request }) => { expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(result.revoked).toBe(true); + expect(result.published).toBe(true); const verificationResponse = await request.post(`/credential/verify?verifyStatus=true`, { data: JSON.stringify({ @@ -64,9 +65,9 @@ test(' Verify a credential status after revocation', async ({ request }) => { 'Content-Type': 'application/json', }, }); - const verificationResult = await response.json(); + const verificationResult = await verificationResponse.json(); expect(verificationResponse).toBeOK(); expect(verificationResponse.status()).toBe(StatusCodes.OK); expect(verificationResult.verified).toBe(true); - expect(result.revoked).toBe(true); + expect(verificationResult.revoked).toBe(true); }); diff --git a/tests/credential/suspension-flow.spec.ts b/tests/credential/suspension-flow.spec.ts index de9cc3d3..1b87cace 100644 --- a/tests/credential/suspension-flow.spec.ts +++ b/tests/credential/suspension-flow.spec.ts @@ -56,6 +56,7 @@ test(' Verify a credential status after suspension', async ({ request }) => { expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(result.suspended).toBe(true); + expect(result.published).toBe(true); const verificationResponse = await request.post(`/credential/verify?verifyStatus=true`, { data: JSON.stringify({ @@ -65,11 +66,11 @@ test(' Verify a credential status after suspension', async ({ request }) => { 'Content-Type': 'application/json', }, }); - const verificationResult = await response.json(); + const verificationResult = await verificationResponse.json(); expect(verificationResponse).toBeOK(); expect(verificationResponse.status()).toBe(StatusCodes.OK); expect(verificationResult.verified).toBe(true); - expect(result.suspended).toBe(true); + expect(verificationResult.suspended).toBe(true); }); test(' Verify a credential status after reinstating', async ({ request }) => { @@ -84,7 +85,7 @@ test(' Verify a credential status after reinstating', async ({ request }) => { const result = await response.json(); expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); - expect(result.suspended).toBe(false); + expect(result.unsuspended).toBe(true); const verificationResponse = await request.post(`/credential/verify?verifyStatus=true`, { data: JSON.stringify({ @@ -94,9 +95,9 @@ test(' Verify a credential status after reinstating', async ({ request }) => { 'Content-Type': 'application/json', }, }); - const verificationResult = await response.json(); + const verificationResult = await verificationResponse.json(); expect(verificationResponse).toBeOK(); expect(verificationResponse.status()).toBe(StatusCodes.OK); expect(verificationResult.verified).toBe(true); - expect(result.suspended).toBe(false); + expect(verificationResult.suspended).toBe(false); }); From 7ea7902108b2b6a6f31241a719f1c53b8db0f8e4 Mon Sep 17 00:00:00 2001 From: DaevMithran Date: Tue, 19 Dec 2023 13:35:17 +0530 Subject: [PATCH 13/15] Match complete responses --- tests/credential/issue-verify-flow.spec.ts | 18 ++++++++++++++++++ tests/credential/revocation-flow.spec.ts | 15 +++++++++++++++ tests/credential/suspension-flow.spec.ts | 15 +++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/tests/credential/issue-verify-flow.spec.ts b/tests/credential/issue-verify-flow.spec.ts index 56c3658a..62cbff06 100644 --- a/tests/credential/issue-verify-flow.spec.ts +++ b/tests/credential/issue-verify-flow.spec.ts @@ -22,6 +22,15 @@ test(' Issue a jwt credential', async ({ request }) => { expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(jwtCredential.proof.type).toBe('JwtProof2020'); + expect(jwtCredential.proof).toHaveProperty('jwt'); + expect(typeof jwtCredential.issuer === 'string' ? jwtCredential.issuer : jwtCredential.issuer.id).toBe( + credentialData.issuerDid + ); + expect(jwtCredential.type).toContain('VerifiableCredential'); + expect(jwtCredential.credentialSubject).toMatchObject({ + ...credentialData.attributes, + id: credentialData.subjectDid, + }); }); test(' Verify a jwt credential', async ({ request }) => { @@ -63,6 +72,15 @@ test(' Issue a jsonLD credential', async ({ request }) => { expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(jsonldCredential.proof.type).toBe('Ed25519Signature2018'); + expect(jsonldCredential.proof).toHaveProperty('jws'); + expect(typeof jwtCredential.issuer === 'string' ? jwtCredential.issuer : jwtCredential.issuer.id).toBe( + credentialData.issuerDid + ); + expect(jwtCredential.type).toContain('VerifiableCredential'); + expect(jwtCredential.credentialSubject).toMatchObject({ + ...credentialData.attributes, + id: credentialData.subjectDid, + }); }); test(' Verify a jsonld credential', async ({ request }) => { diff --git a/tests/credential/revocation-flow.spec.ts b/tests/credential/revocation-flow.spec.ts index 6133fa17..932ddf49 100644 --- a/tests/credential/revocation-flow.spec.ts +++ b/tests/credential/revocation-flow.spec.ts @@ -24,6 +24,21 @@ test(' Issue a jwt credential with revocation statuslist', async ({ request }) = expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(jwtCredential.proof.type).toBe('JwtProof2020'); + expect(jwtCredential.proof).toHaveProperty('jwt'); + expect(typeof jwtCredential.issuer === 'string' ? jwtCredential.issuer : jwtCredential.issuer.id).toBe( + credentialData.issuerDid + ); + expect(jwtCredential.type).toContain('VerifiableCredential'); + expect(jwtCredential.credentialSubject).toMatchObject({ + ...credentialData.attributes, + id: credentialData.subjectDid, + }); + expect(jwtCredential.credentialStatus).toMatchObject({ + type: 'StatusList2021Entry', + statusPurpose: 'revocation', + }); + expect(jwtCredential.credentialStatus).toHaveProperty('statusListIndex'); + expect(jwtCredential.credentialStatus).toHaveProperty('id'); }); test(" Verify a credential's revocation status", async ({ request }) => { diff --git a/tests/credential/suspension-flow.spec.ts b/tests/credential/suspension-flow.spec.ts index 1b87cace..08b86dc0 100644 --- a/tests/credential/suspension-flow.spec.ts +++ b/tests/credential/suspension-flow.spec.ts @@ -25,6 +25,21 @@ test(' Issue a jwt credential with suspension statuslist', async ({ request }) = expect(response).toBeOK(); expect(response.status()).toBe(StatusCodes.OK); expect(jwtCredential.proof.type).toBe('JwtProof2020'); + expect(jwtCredential.proof).toHaveProperty('jwt'); + expect(typeof jwtCredential.issuer === 'string' ? jwtCredential.issuer : jwtCredential.issuer.id).toBe( + credentialData.issuerDid + ); + expect(jwtCredential.type).toContain('VerifiableCredential'); + expect(jwtCredential.credentialSubject).toMatchObject({ + ...credentialData.attributes, + id: credentialData.subjectDid, + }); + expect(jwtCredential.credentialStatus).toMatchObject({ + type: 'StatusList2021Entry', + statusPurpose: 'suspension', + }); + expect(jwtCredential.credentialStatus).toHaveProperty('statusListIndex'); + expect(jwtCredential.credentialStatus).toHaveProperty('id'); }); test(" Verify a credential's suspension status", async ({ request }) => { From b2364ae2845d53e9d81c8c7399f8112b30a78d0f Mon Sep 17 00:00:00 2001 From: Ankur Banerjee Date: Wed, 20 Dec 2023 14:00:08 +0100 Subject: [PATCH 14/15] Update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 804fdc53..a512f31d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cheqd/credential-service", - "version": "2.11.0", + "version": "2.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cheqd/credential-service", - "version": "2.11.0", + "version": "2.12.0", "license": "Apache-2.0", "dependencies": { "@cheqd/did-provider-cheqd": "^3.6.13", From 1fe74903cabff35cd67864a25d551a8561fd0725 Mon Sep 17 00:00:00 2001 From: Andrew Nikitin Date: Fri, 22 Dec 2023 17:40:44 +0100 Subject: [PATCH 15/15] MAke the same style for tests --- ...no-auth.spec.ts => search-no-auth.spec.ts} | 0 ...no-auth.spec.ts => verify-no-auth.spec.ts} | 0 tests/e2e/did/create-negative.spec.ts | 185 ++++++++++++++++++ tests/e2e/did/create-positive.spec.ts | 178 +++++++++++++++++ ...ve.spec.ts => deactivate-negative.spec.ts} | 0 ...no-auth.spec.ts => search-no-auth.spec.ts} | 0 tests/e2e/did/update-negative.spec.ts | 18 ++ .../e2e/presentation/verify-negative.spec.ts | 63 ++++++ .../e2e/presentation/verify-positive.spec.ts | 95 +++++++++ ...no-auth.spec.ts => search-no-auth.spec.ts} | 0 10 files changed, 539 insertions(+) rename tests/e2e/credential-status/{search.no-auth.spec.ts => search-no-auth.spec.ts} (100%) rename tests/e2e/credential/{verify.no-auth.spec.ts => verify-no-auth.spec.ts} (100%) create mode 100644 tests/e2e/did/create-negative.spec.ts create mode 100644 tests/e2e/did/create-positive.spec.ts rename tests/e2e/did/{deactivate.negative.spec.ts => deactivate-negative.spec.ts} (100%) rename tests/e2e/did/{search.no-auth.spec.ts => search-no-auth.spec.ts} (100%) create mode 100644 tests/e2e/did/update-negative.spec.ts create mode 100644 tests/e2e/presentation/verify-negative.spec.ts create mode 100644 tests/e2e/presentation/verify-positive.spec.ts rename tests/e2e/resource/{search.no-auth.spec.ts => search-no-auth.spec.ts} (100%) diff --git a/tests/e2e/credential-status/search.no-auth.spec.ts b/tests/e2e/credential-status/search-no-auth.spec.ts similarity index 100% rename from tests/e2e/credential-status/search.no-auth.spec.ts rename to tests/e2e/credential-status/search-no-auth.spec.ts diff --git a/tests/e2e/credential/verify.no-auth.spec.ts b/tests/e2e/credential/verify-no-auth.spec.ts similarity index 100% rename from tests/e2e/credential/verify.no-auth.spec.ts rename to tests/e2e/credential/verify-no-auth.spec.ts diff --git a/tests/e2e/did/create-negative.spec.ts b/tests/e2e/did/create-negative.spec.ts new file mode 100644 index 00000000..eef5c053 --- /dev/null +++ b/tests/e2e/did/create-negative.spec.ts @@ -0,0 +1,185 @@ +import { + ID_TYPE, + INVALID_ID, + INVALID_DID, + NOT_EXISTENT_KEY, + DEFAULT_DOES_NOT_HAVE_PERMISSIONS, + NOT_SUPPORTED_VERIFICATION_METHOD_TYPE, + PAYLOADS_PATH, + CONTENT_TYPE, +} from '../constants'; +import * as fs from 'fs'; +import { v4 } from 'uuid'; +import { test, expect } from '@playwright/test'; +import { StatusCodes } from 'http-status-codes'; +import { CheqdNetwork, VerificationMethods } from '@cheqd/sdk'; + + +test.use({ storageState: 'playwright/.auth/user.json' }); + +test('[Negative] It cannot create DID with missed verificationMethodType field in request body (Form based)', async ({ + request, +}) => { + const response = await request.post(`/did/create`, { + data: `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}`, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + }); + expect(response.status()).toBe(StatusCodes.BAD_REQUEST); + expect(await response.text()).toEqual( + expect.stringContaining('Provide a DID Document or the VerificationMethodType to create a DID') + ); +}); + +test('[Negative] It cannot create DID with not existent key in request body (Form based)', async ({ request }) => { + const response = await request.post(`/did/create`, { + data: + `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}&` + + `verificationMethodType=${VerificationMethods.Ed255192020}&` + + `key=${NOT_EXISTENT_KEY}`, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + }); + expect(response.status()).toBe(StatusCodes.INTERNAL_SERVER_ERROR); + expect(await response.text()).toEqual(expect.stringContaining('Key not found')); +}); + +test('[Negative] It cannot create DID with not existent key in request body (JSON based)', async ({ request }) => { + const did = `did:cheqd:testnet:${v4()}`; + const response = await request.post('/did/create', { + data: { + network: CheqdNetwork.Testnet, + identifierFormatType: ID_TYPE.UUID, + options: { + verificationMethodType: VerificationMethods.Ed255192020, + key: NOT_EXISTENT_KEY, + }, + didDocument: { + id: did, + controller: [did], + authentication: [`${did}#key-1`], + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response.status()).toBe(StatusCodes.INTERNAL_SERVER_ERROR); + expect(await response.text()).toEqual(expect.stringContaining('Key not found')); +}); + +test('[Negative] It cannot create DID with an invalid VerificationMethodType in request body (JSON based)', async ({ + request, +}) => { + const did = `did:cheqd:testnet:${v4()}`; + const response = await request.post('/did/create', { + data: { + network: CheqdNetwork.Testnet, + identifierFormatType: ID_TYPE.UUID, + options: { + verificationMethodType: NOT_SUPPORTED_VERIFICATION_METHOD_TYPE, + }, + didDocument: { + id: did, + controller: [did], + authentication: [`${did}#key-1`], + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response.status()).toBe(StatusCodes.INTERNAL_SERVER_ERROR); + expect(await response.text()).toEqual(expect.stringContaining('Unsupported verificationMethod type')); +}); + +test('[Negative] It cannot create DID with an invalid length of id in DIDDocument in request body (JSON based)', async ({ + request, +}) => { + const invalidDidLength = `did:cheqd:testnet:${INVALID_ID}`; + const response = await request.post('/did/create', { + data: { + network: CheqdNetwork.Testnet, + identifierFormatType: ID_TYPE.UUID, + options: { + verificationMethodType: VerificationMethods.Ed255192018, + }, + didDocument: { + id: invalidDidLength, + controller: [invalidDidLength], + authentication: [`${invalidDidLength}#key-1`], + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response.status()).toBe(StatusCodes.BAD_REQUEST); + expect(await response.text()).toEqual(expect.stringContaining('Cheqd DID identifier is not valid')); +}); + +test('[Negative] It cannot create DID with an invalid id format in DIDDocument in request body (JSON based)', async ({ + request, +}) => { + const response = await request.post('/did/create', { + data: { + network: CheqdNetwork.Testnet, + identifierFormatType: ID_TYPE.UUID, + options: { + verificationMethodType: VerificationMethods.Ed255192018, + }, + didDocument: { + id: INVALID_DID, + controller: [INVALID_DID], + authentication: [`${INVALID_DID}#key-1`], + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response.status()).toBe(StatusCodes.BAD_REQUEST); + expect(await response.text()).toEqual( + expect.stringContaining('Invalid format of DID. Expected to start with did:') + ); +}); + +test('[Negative] It cannot create DID without VerificationMethodType in request body (JSON based)', async ({ + request, +}) => { + const response = await request.post('/did/create', { + data: { + network: CheqdNetwork.Testnet, + identifierFormatType: ID_TYPE.UUID, + didDocument: { + id: INVALID_DID, + controller: [INVALID_DID], + authentication: [`${INVALID_DID}#key-1`], + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response.status()).toBe(StatusCodes.BAD_REQUEST); + expect(await response.text()).toEqual( + expect.stringContaining('Invalid format of DID. Expected to start with did:') + ); +}); + +test('[Negative] It cannot create DID without DidDocument in request body (JSON based)', async ({ request }) => { + const response = await request.post('/did/create', { + data: { + network: CheqdNetwork.Testnet, + identifierFormatType: ID_TYPE.UUID, + options: { + verificationMethodType: VerificationMethods.Ed255192020, + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response.status()).toBe(StatusCodes.BAD_REQUEST); + expect(await response.text()).toEqual( + expect.stringContaining('Provide a DID Document or the VerificationMethodType to create a DID') + ); +}); + +test('[Negative] It cannot create DID in mainnet network for user with testnet role', async ({ request }) => { + const response = await request.post(`/did/create`, { + data: JSON.parse(fs.readFileSync(`${PAYLOADS_PATH.DID}/did-create-without-permissions.json`, 'utf-8')), + headers: { + 'Content-Type': CONTENT_TYPE.APPLICATION_JSON, + }, + }); + expect(response).not.toBeOK(); + expect(response.status()).toBe(StatusCodes.FORBIDDEN); + expect(await response.text()).toEqual(expect.stringContaining(DEFAULT_DOES_NOT_HAVE_PERMISSIONS)); +}); diff --git a/tests/e2e/did/create-positive.spec.ts b/tests/e2e/did/create-positive.spec.ts new file mode 100644 index 00000000..0a11d99e --- /dev/null +++ b/tests/e2e/did/create-positive.spec.ts @@ -0,0 +1,178 @@ +import { ID_TYPE, DEFAULT_CONTEXT, CONTENT_TYPE } from '../constants'; +import { v4 } from 'uuid'; +import { buildSimpleService } from 'helpers'; +import { test, expect } from '@playwright/test'; +import { StatusCodes } from 'http-status-codes'; + +import { CheqdNetwork, MethodSpecificIdAlgo, VerificationMethods, createVerificationKeys } from '@cheqd/sdk'; + +test.use({ storageState: 'playwright/.auth/user.json' }); + +test('[Positive] It can create DID with mandatory properties (Form based + Indy style)', async ({ request }) => { + // send request to create DID + let response = await request.post(`/did/create`, { + data: + `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}&` + + `verificationMethodType=${VerificationMethods.Ed255192018}`, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_X_WWW_FORM_URLENCODED }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + // resolve a created DID + response = await request.get(`/did/search/${(await response.json()).did}`, { + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const didDocument = (await response.json()).didDocument; + + // Check mandatory properties + expect(didDocument.id.split(':')[2]).toBe(CheqdNetwork.Testnet); + expect(didDocument.verificationMethod[0].type).toBe(VerificationMethods.Ed255192018); +}); + +test('[Positive] It can create DID with mandatory and optional properties (Form based + UUID style)', async ({ + request, +}) => { + // send request to create key + let response = await request.post('/key/create', { + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const kid = (await response.json()).kid; + + // send request to create DID + response = await request.post(`/did/create`, { + data: + `network=${CheqdNetwork.Testnet}&identifierFormatType=${ID_TYPE.BASE58BTC}&` + + `verificationMethodType=${VerificationMethods.Ed255192018}&` + + `service=[${JSON.stringify(buildSimpleService())}]` + + `&key=${kid}&@context=${DEFAULT_CONTEXT}`, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_X_WWW_FORM_URLENCODED }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const body = await response.json(); + + // Check mandatory properties + expect(body.did.split(':')[2]).toBe(CheqdNetwork.Testnet); + expect(body.controllerKeyId).toBe(kid); + + // resolve a created DID + response = await request.get(`/did/search/${body.did}`, { + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const didDocument = (await response.json()).didDocument; + + // check optional properties + expect(didDocument.verificationMethod[0].type).toBe(VerificationMethods.Ed255192018); + expect(didDocument.service[0].id).toBe(`${body.did}#service-1`); + expect(didDocument.service[0].type).toBe('LinkedDomains'); + expect(didDocument.service[0].serviceEndpoint[0]).toBe('https://example.com'); +}); + +test('[Positive] It can create DID with mandatory properties (JSON based + Indy style)', async ({ request }) => { + // send request to create key + let response = await request.post('/key/create', { + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const did = createVerificationKeys((await response.json()).kid, MethodSpecificIdAlgo.Base58, 'key-1').didUrl; + + // send request to create DID + response = await request.post('/did/create', { + data: { + options: { + verificationMethodType: VerificationMethods.Ed255192018, + }, + didDocument: { + id: did, + controller: [did], + authentication: [`${did}#key-1`], + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + // resolve a created DID + response = await request.get(`/did/search/${(await response.json()).did}`, { + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const didDocument = (await response.json()).didDocument; + + // Check mandatory properties + expect(didDocument.id.split(':')[2]).toBe(CheqdNetwork.Testnet); + expect(didDocument.verificationMethod[0].type).toBe(VerificationMethods.Ed255192018); +}); + +test('[Positive] It can create DID with mandatory and optional properties (JSON based + UUID style)', async ({ + request, +}) => { + // send request to create key + let response = await request.post('/key/create', { + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const kid = (await response.json()).kid; + + const did = `did:cheqd:testnet:${v4()}`; + response = await request.post('/did/create', { + data: { + network: CheqdNetwork.Testnet, + identifierFormatType: ID_TYPE.UUID, + assertionMethod: true, + options: { + verificationMethodType: VerificationMethods.JWK, + key: kid, + }, + didDocument: { + '@context': ['https://www.w3.org/ns/did/v1'], + id: did, + controller: [did], + authentication: [`${did}#key-1`], + service: [ + { + id: `${did}#service-1`, + type: 'LinkedDomains', + serviceEndpoint: ['https://example.com'], + }, + ], + }, + }, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + // resolve a created DID + response = await request.get(`/did/search/${did}`, { + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + }); + expect(response).toBeOK(); + expect(response.status()).toBe(StatusCodes.OK); + + const didDocument = (await response.json()).didDocument; + + // check optional properties + expect(didDocument.verificationMethod[0].type).toBe(VerificationMethods.JWK); + expect(didDocument.service[0].id).toBe(`${did}#service-1`); + expect(didDocument.service[0].type).toBe('LinkedDomains'); + expect(didDocument.service[0].serviceEndpoint[0]).toBe('https://example.com'); +}); diff --git a/tests/e2e/did/deactivate.negative.spec.ts b/tests/e2e/did/deactivate-negative.spec.ts similarity index 100% rename from tests/e2e/did/deactivate.negative.spec.ts rename to tests/e2e/did/deactivate-negative.spec.ts diff --git a/tests/e2e/did/search.no-auth.spec.ts b/tests/e2e/did/search-no-auth.spec.ts similarity index 100% rename from tests/e2e/did/search.no-auth.spec.ts rename to tests/e2e/did/search-no-auth.spec.ts diff --git a/tests/e2e/did/update-negative.spec.ts b/tests/e2e/did/update-negative.spec.ts new file mode 100644 index 00000000..3f622338 --- /dev/null +++ b/tests/e2e/did/update-negative.spec.ts @@ -0,0 +1,18 @@ +import * as fs from 'fs'; +import { test, expect } from '@playwright/test'; +import { StatusCodes } from 'http-status-codes'; +import { CONTENT_TYPE, DEFAULT_DOES_NOT_HAVE_PERMISSIONS, PAYLOADS_PATH } from '../constants'; + +test.use({ storageState: 'playwright/.auth/user.json' }); + +test('[Negative] It cannot update DID in mainnet network for user with testnet role', async ({ request }) => { + const response = await request.post(`/did/update`, { + data: JSON.parse(fs.readFileSync(`${PAYLOADS_PATH.DID}/did-update-without-permissions.json`, 'utf-8')), + headers: { + 'Content-Type': CONTENT_TYPE.APPLICATION_JSON + }, + }); + expect(response).not.toBeOK(); + expect(response.status()).toBe(StatusCodes.FORBIDDEN); + expect(await response.text()).toEqual(expect.stringContaining(DEFAULT_DOES_NOT_HAVE_PERMISSIONS)); +}); diff --git a/tests/e2e/presentation/verify-negative.spec.ts b/tests/e2e/presentation/verify-negative.spec.ts new file mode 100644 index 00000000..846668e2 --- /dev/null +++ b/tests/e2e/presentation/verify-negative.spec.ts @@ -0,0 +1,63 @@ +import * as fs from 'fs'; +import { StatusCodes } from 'http-status-codes'; +import { test, expect } from '@playwright/test'; +import { CONTENT_TYPE, PAYLOADS_PATH } from '../constants'; + + +test.use({ storageState: 'playwright/.auth/user.json' }); + +for (const presentationType of ['jwt', 'object']) { + for (const verifyStatus of [true]) { + test(`[Negative] It should return verify: False. + Presenation format is ${presentationType}. + Encrypted statusList2021. + VerifyStatus: ${verifyStatus}, + makeFeePayments: false`, async ({ request }) => { + const verifyRequest = JSON.parse( + fs.readFileSync(`${PAYLOADS_PATH.PRESENTATION}/verify-negative-${presentationType}-encrypted.json`, 'utf-8') + ); + verifyRequest.makeFeePayment = false; + const response = await request.post( + `/presentation/verify?verifyStatus=${verifyStatus}&fetchRemoteContexts=false&allowDeactivatedDid=false`, + { + data: verifyRequest, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + } + ); + expect(response).not.toBeOK(); + expect(response.status()).toBe(StatusCodes.UNAUTHORIZED); + const body = await response.json(); + expect(body.error).toContain('check: error: unauthorised: decryption conditions are not met'); + }); + } +} + +for (const presentationType of ['jwt', 'object']) { + for (const verifyStatus of [true]) { + test(`[Negative] Presentation with 2 credentials. One has encrypted statusList another one - no + It should return verify: False. + Presenation format is ${presentationType}. + Encrypted and unencrypted statusList2021. + VerifyStatus: ${verifyStatus}, + makeFeePayments: false`, async ({ request }) => { + const verifyRequest = JSON.parse( + fs.readFileSync( + `${PAYLOADS_PATH.PRESENTATION}/verify-negative-${presentationType}-un-and-encrypted.json`, + 'utf-8' + ) + ); + verifyRequest.makeFeePayment = false; + const response = await request.post( + `/presentation/verify?verifyStatus=${verifyStatus}&fetchRemoteContexts=false&allowDeactivatedDid=false`, + { + data: verifyRequest, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + } + ); + expect(response).not.toBeOK(); + expect(response.status()).toBe(StatusCodes.UNAUTHORIZED); + const body = await response.json(); + expect(body.error).toContain('check: error: unauthorised: decryption conditions are not met'); + }); + } +} diff --git a/tests/e2e/presentation/verify-positive.spec.ts b/tests/e2e/presentation/verify-positive.spec.ts new file mode 100644 index 00000000..d82cc72d --- /dev/null +++ b/tests/e2e/presentation/verify-positive.spec.ts @@ -0,0 +1,95 @@ +import * as fs from 'fs'; +import { test, expect } from '@playwright/test'; +import { CONTENT_TYPE, PAYLOADS_PATH } from '../constants'; + +test.use({ storageState: 'playwright/.auth/user.json' }); + +for (const presentationType of ['jwt', 'object']) { + for (const verifyStatus of [true, false]) { + test(`[Positive] It should return verify: True. + Presentation format is ${presentationType}. + Encrypted statusList2021. + VerifyStatus: ${verifyStatus}, + makeFeePayments: true`, async ({ request }) => { + const verifyRequest = JSON.parse( + fs.readFileSync(`${PAYLOADS_PATH.PRESENTATION}/verify-positive-${presentationType}-encrypted.json`, 'utf-8') + ); + verifyRequest.makeFeePayment = true; + const response = await request.post( + `/presentation/verify?verifyStatus=${verifyStatus}&fetchRemoteContexts=false&allowDeactivatedDid=false`, + { + data: verifyRequest, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + } + ); + expect(response).toBeOK(); + expect(await response.json()).toEqual( + expect.objectContaining({ + verified: true, + }) + ); + }); + } +} + +for (const presentationType of ['jwt', 'object']) { + for (const verifyStatus of [true, false]) { + for (const makeFeePayments of [true, false]) { + test(`[Positive] It should return verify: True. + Presenation format is ${presentationType}. + Uncrypted statusList2021. + VerifyStatus: ${verifyStatus}, + makeFeePayments: ${makeFeePayments}`, async ({ request }) => { + const verifyRequest = JSON.parse( + fs.readFileSync( + `${PAYLOADS_PATH.PRESENTATION}/verify-positive-${presentationType}-unencrypted.json`, + 'utf-8' + ) + ); + verifyRequest.makeFeePayments = makeFeePayments; + const response = await request.post( + `/presentation/verify?verifyStatus=${verifyStatus}&fetchRemoteContexts=false&allowDeactivatedDid=false`, + { + data: verifyRequest, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + } + ); + expect(response).toBeOK(); + expect(await response.json()).toEqual( + expect.objectContaining({ + verified: true, + }) + ); + }); + } + } +} + +for (const presentationType of ['jwt', 'object']) { + for (const verifyStatus of [true, false]) { + test(`[Positive] It should return verify: True. + Presenation format is ${presentationType}. + Encrypted and Unencrypted statusList2021. + VerifyStatus: ${verifyStatus}}`, async ({ request }) => { + const verifyRequest = JSON.parse( + fs.readFileSync( + `${PAYLOADS_PATH.PRESENTATION}/verify-positive-${presentationType}-un-and-encrypted.json`, + 'utf-8' + ) + ); + const response = await request.post( + `/presentation/verify?verifyStatus=${verifyStatus}&fetchRemoteContexts=false&allowDeactivatedDid=false`, + { + data: verifyRequest, + headers: { 'Content-Type': CONTENT_TYPE.APPLICATION_JSON }, + } + ); + expect(response).toBeOK(); + expect(await response.json()).toEqual( + expect.objectContaining({ + verified: true, + }) + ); + }); + } +} diff --git a/tests/e2e/resource/search.no-auth.spec.ts b/tests/e2e/resource/search-no-auth.spec.ts similarity index 100% rename from tests/e2e/resource/search.no-auth.spec.ts rename to tests/e2e/resource/search-no-auth.spec.ts