From 684e10d745d9617be16f7b3b59d2672e1d1a8bc9 Mon Sep 17 00:00:00 2001 From: Yen Le Date: Mon, 11 Nov 2024 17:40:19 +0700 Subject: [PATCH 1/8] test: add test for qr link verification unencrypted data --- packages/vc-test-suite/config.ts | 16 +++++- .../QrlinkEncrypted/qrlink-encrypted.test.ts | 56 ++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/packages/vc-test-suite/config.ts b/packages/vc-test-suite/config.ts index 4ed808e8..24661c4f 100644 --- a/packages/vc-test-suite/config.ts +++ b/packages/vc-test-suite/config.ts @@ -1,11 +1,23 @@ export default { - implementationName: 'VCkit', + implementationName: 'Tier 1 Test Suite', testSuites: { QrLinkEncrypted: { - url: 'http://localhost:3001/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2Fc2eb5fee-da3b-411b-9b03-e8f7fb20dc3d.json%22%2C%22key%22%3A%22e5e0d20e35e2786773720cbba011ca26cca0d6fd279a6c3fd00980a5ed1fb5c8%22%2C%22hash%22%3A%2231e96f8b6896b9fc6f9b86267574b2926b5637e1f65027b437119cfd2033f3d7%22%7D%7D', + url: 'http://localhost:3001/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F7a07aa7c-c961-4c74-a714-a6188cadd8f6.json%22%2C%22key%22%3A%222e2a9af227350b73733988ccb93581a5449cafa64394c13ffc7142b3b0b280b6%22%2C%22hash%22%3A%22a813f8aa1cdb5f391ec227d5a1f76a2764df7112bb26cd035b547a30ade03c8e%22%7D%7D', headers: {}, method: 'GET', }, + QrLink: { + unencrypted: { + qrLinkUrls: { + credentialObject: + 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F545bc89a-395b-4c0e-be7c-2a48b35f3ad5.json%22%7D%7D', + credentialJWT: + 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F61bdef77-0bcf-47d8-a6e6-00074e530e31.json%22%7D%7D', + }, + headers: {}, + method: 'GET', + }, + }, RenderTemplate2024: { url: 'http://localhost:3332/agent/renderCredential', headers: { Authorization: 'Bearer test123' }, diff --git a/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts b/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts index 17eeacd5..c95f0001 100644 --- a/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts +++ b/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts @@ -9,7 +9,7 @@ import jwt from 'jsonwebtoken'; const expect = chai.expect; -describe('QR Link Verification', function () { +describe('QR Link Verification with encrypted data', function () { const { url: qrLink, method, headers } = config.testSuites.QrLinkEncrypted; const parsedLink = parseQRLink(qrLink); @@ -89,3 +89,57 @@ describe('QR Link Verification', function () { vcData.should.have.property('credentialSubject'); }); }); + +describe('QR Link Verification with unencrypted data', function () { + const { qrLinkUrls, method, headers } = config.testSuites.QrLink.unencrypted; + + reportRow('QR link of credential JWT MUST be URL encoded', config.implementationName, function () { + const data = isURLEncoded(qrLinkUrls.credentialJWT); + data.should.be.true; + }); + + reportRow('QR link of credential Object MUST be URL encoded', config.implementationName, function () { + const data = isURLEncoded(qrLinkUrls.credentialObject); + data.should.be.true; + }); + + describe('Validates the format of Credentials JWT', function () { + const parsedLink = parseQRLink(qrLinkUrls.credentialJWT); + reportRow('URI MUST be resolvable and contain JWT value', config.implementationName, async function () { + const { data } = await request({ + url: parsedLink.q.payload.uri, + method, + headers, + }); + + // This expectation follows the response from the vc-api after successfully issuing a credential in EnvelopedVerifiableCredential type - https://w3c-ccg.github.io/vc-api/#issue-credential + data.should.be.an('object'); + data['@context'].should.be.an('array'); + data.type.should.be.a('string'); + data.type.should.include('EnvelopedVerifiableCredential'); + data.id.should.be.a('string'); + data.id.should.include('data:application/vc'); + + const jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/; + data.id.split(',')[1].should.match(jwtRegex); + }); + }); + + describe('Validates the format of Credentials Object', function () { + const parsedLink = parseQRLink(qrLinkUrls.credentialObject); + reportRow('URI MUST be resolvable and contain JSON-LD value', config.implementationName, async function () { + const { data } = await request({ + url: parsedLink.q.payload.uri, + method, + headers, + }); + + // This expectation follows the response from the vc-api after successfully issuing a credential in JSON-LD format - https://w3c-ccg.github.io/vc-api/#issue-credential + data.should.be.an('object'); + data['@context'].should.be.an('array'); + data.type.should.be.an('array'); + data.type.should.include('VerifiableCredential'); + data.issuer.should.not.be.empty; + }); + }); +}); From 9d4c5589aa6f89c66f6a94f849763ee27f337539 Mon Sep 17 00:00:00 2001 From: Yen Le Date: Tue, 12 Nov 2024 14:04:56 +0700 Subject: [PATCH 2/8] feat: update qr link unencrypted data --- .../untp-extensions/qr-link-encryption.md | 5 ++ packages/vc-test-suite/README.md | 11 ++- packages/vc-test-suite/config.ts | 17 ++-- .../QrlinkEncrypted/qrlink-encrypted.test.ts | 90 ++++++------------- 4 files changed, 45 insertions(+), 78 deletions(-) diff --git a/documentation/docs/test-suites/technical-interoperability/untp-extensions/qr-link-encryption.md b/documentation/docs/test-suites/technical-interoperability/untp-extensions/qr-link-encryption.md index fe33986a..b2178bc5 100644 --- a/documentation/docs/test-suites/technical-interoperability/untp-extensions/qr-link-encryption.md +++ b/documentation/docs/test-suites/technical-interoperability/untp-extensions/qr-link-encryption.md @@ -31,6 +31,11 @@ To test your QR Link / Encryption implementation, follow these steps: headers: {}, method: 'GET', }, + QrLinkUnencrypted: { + url: 'https://example.com/credential-verifier?q=%7B%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fapi.vckit.showthething.com%2Fencrypted-storage%2Fencrypted-data%2F0a6031a9-2740-49cd-b12b-1ed02820f01d%22%7D%7D', + headers: {}, + method: 'GET', + }, }, }; ``` diff --git a/packages/vc-test-suite/README.md b/packages/vc-test-suite/README.md index db5c6e81..392a6c03 100644 --- a/packages/vc-test-suite/README.md +++ b/packages/vc-test-suite/README.md @@ -8,10 +8,14 @@ yarn install ## Setup -There are two parts of the test suite: `Render Template 2024` and `QR Link Encryption`. To add your implementation to this test suite you will need to add 2 endpoints to your implementation manifest in `config.ts`: +There are three parts of the test suite: `Render Template 2024`, `QR Link Encryption` and `Storage`. To add your implementation to this test suite you will need to add 2 endpoints to your implementation manifest in `config.ts`: - A Render Template 2024 endpoint in the `RenderTemplate2024` property. - An Encrypted QR Link in the `QrLinkEncrypted` property. +- An Unencrypted QR Link in the `QrLinkUnencrypted` property. +- A Storage endpoint in the `Storage` property. + +> **IMPORTANT**: Please change the url in the `config.ts` file to match your implementation. ## Usage @@ -40,3 +44,8 @@ Open the generated report HTML file in the reports folder to check the results. ```bash yarn test:package ``` + +### Additional Documentation + +- [Technical Interoperability](https://uncefact.github.io/tests-untp/docs/test-suites/technical-interoperability/) +- [Verify Link](https://uncefact.github.io/tests-untp/docs/mock-apps/common/verify-link) diff --git a/packages/vc-test-suite/config.ts b/packages/vc-test-suite/config.ts index 24661c4f..e25ae5a8 100644 --- a/packages/vc-test-suite/config.ts +++ b/packages/vc-test-suite/config.ts @@ -2,21 +2,14 @@ export default { implementationName: 'Tier 1 Test Suite', testSuites: { QrLinkEncrypted: { - url: 'http://localhost:3001/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F7a07aa7c-c961-4c74-a714-a6188cadd8f6.json%22%2C%22key%22%3A%222e2a9af227350b73733988ccb93581a5449cafa64394c13ffc7142b3b0b280b6%22%2C%22hash%22%3A%22a813f8aa1cdb5f391ec227d5a1f76a2764df7112bb26cd035b547a30ade03c8e%22%7D%7D', + url: 'http://localhost:3003/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F7a07aa7c-c961-4c74-a714-a6188cadd8f6.json%22%2C%22key%22%3A%222e2a9af227350b73733988ccb93581a5449cafa64394c13ffc7142b3b0b280b6%22%2C%22hash%22%3A%22a813f8aa1cdb5f391ec227d5a1f76a2764df7112bb26cd035b547a30ade03c8e%22%7D%7D', headers: {}, method: 'GET', }, - QrLink: { - unencrypted: { - qrLinkUrls: { - credentialObject: - 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F545bc89a-395b-4c0e-be7c-2a48b35f3ad5.json%22%7D%7D', - credentialJWT: - 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F61bdef77-0bcf-47d8-a6e6-00074e530e31.json%22%7D%7D', - }, - headers: {}, - method: 'GET', - }, + QrLinkUnencrypted: { + url: 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F61bdef77-0bcf-47d8-a6e6-00074e530e31.json%22%7D%7D', + headers: {}, + method: 'GET', }, RenderTemplate2024: { url: 'http://localhost:3332/agent/renderCredential', diff --git a/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts b/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts index c95f0001..69b219af 100644 --- a/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts +++ b/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts @@ -1,11 +1,12 @@ -import { decryptCredential } from '@mock-app/services'; import chai from 'chai'; -import config from '../../config'; + +import { HashAlgorithm, decryptCredential } from '@mock-app/services'; +import { computeHash } from '@mock-app/services'; + import { reportRow, setupMatrix } from '../../helpers'; import { request } from '../../httpService'; import { isURLEncoded, parseQRLink } from './helper'; -import { computeHash } from '@mock-app/services'; -import jwt from 'jsonwebtoken'; +import config from '../../config'; const expect = chai.expect; @@ -23,11 +24,9 @@ describe('QR Link Verification with encrypted data', function () { reportRow('Verification page link MUST exist and be a string', config.implementationName, function () { expect(parsedLink.verify_app_address).to.be.a('string'); }); - reportRow('Payload MUST exist and be an object', config.implementationName, function () { expect(parsedLink.q.payload).to.be.an('object'); }); - reportRow('URI MUST exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.uri).to.be.a('string'); }); @@ -39,107 +38,68 @@ describe('QR Link Verification with encrypted data', function () { headers, }); - data.should.be.an('object'); + data.should.not.be.null; + data.should.not.be.undefined; + data.should.not.be.empty; }); - reportRow('Hash MUST exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.hash).to.be.a('string'); }); - reportRow('Hash MUST match the credential hash', config.implementationName, async function () { const { data } = await request({ url: parsedLink.q.payload.uri, method, headers, }); - const stringifyVC = decryptCredential({ ...data, key: parsedLink.q.payload.key, }); - const vc = JSON.parse(stringifyVC); - const credentialHash = computeHash(vc); + const credentialHash = computeHash(vc, HashAlgorithm.SHA256); expect(parsedLink.q.payload.hash).to.equal(credentialHash); }); - reportRow('Key exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.key).to.be.a('string'); }); - reportRow('Key MUST decrypt the encrypted credential', config.implementationName, async function () { const { data } = await request({ url: parsedLink.q.payload.uri, method, headers, }); - const stringifyVC = decryptCredential({ ...data, key: parsedLink.q.payload.key, }); - const vc = JSON.parse(stringifyVC); - - // Handle both JWT and regular JSON VC formats - const vcData = vc.id?.startsWith('data:application/vc-ld+jwt,') ? jwt.decode(vc.id.split(',')[1]) : vc; - - expect(vcData).to.be.an('object'); - vcData.should.have.property('issuer'); - vcData.should.have.property('credentialSubject'); + // Assert that stringifyVC is not null, empty, or undefined + stringifyVC.should.not.be.null; + stringifyVC.should.not.be.undefined; + stringifyVC.should.not.be.empty; }); }); describe('QR Link Verification with unencrypted data', function () { - const { qrLinkUrls, method, headers } = config.testSuites.QrLink.unencrypted; + const { url: qrLink } = config.testSuites.QrLinkUnencrypted; + const parsedLink = parseQRLink(qrLink); + + setupMatrix.call(this, [config.implementationName], 'Implementer'); - reportRow('QR link of credential JWT MUST be URL encoded', config.implementationName, function () { - const data = isURLEncoded(qrLinkUrls.credentialJWT); + reportRow('QR link MUST be URL encoded', config.implementationName, function () { + const data = isURLEncoded(qrLink); data.should.be.true; }); - reportRow('QR link of credential Object MUST be URL encoded', config.implementationName, function () { - const data = isURLEncoded(qrLinkUrls.credentialObject); - data.should.be.true; + reportRow('Verification page link MUST exist and be a string', config.implementationName, function () { + expect(parsedLink.verify_app_address).to.be.a('string'); }); - describe('Validates the format of Credentials JWT', function () { - const parsedLink = parseQRLink(qrLinkUrls.credentialJWT); - reportRow('URI MUST be resolvable and contain JWT value', config.implementationName, async function () { - const { data } = await request({ - url: parsedLink.q.payload.uri, - method, - headers, - }); - - // This expectation follows the response from the vc-api after successfully issuing a credential in EnvelopedVerifiableCredential type - https://w3c-ccg.github.io/vc-api/#issue-credential - data.should.be.an('object'); - data['@context'].should.be.an('array'); - data.type.should.be.a('string'); - data.type.should.include('EnvelopedVerifiableCredential'); - data.id.should.be.a('string'); - data.id.should.include('data:application/vc'); - - const jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/; - data.id.split(',')[1].should.match(jwtRegex); - }); + reportRow('Payload MUST exist and be an object', config.implementationName, function () { + expect(parsedLink.q.payload).to.be.an('object'); }); - describe('Validates the format of Credentials Object', function () { - const parsedLink = parseQRLink(qrLinkUrls.credentialObject); - reportRow('URI MUST be resolvable and contain JSON-LD value', config.implementationName, async function () { - const { data } = await request({ - url: parsedLink.q.payload.uri, - method, - headers, - }); - - // This expectation follows the response from the vc-api after successfully issuing a credential in JSON-LD format - https://w3c-ccg.github.io/vc-api/#issue-credential - data.should.be.an('object'); - data['@context'].should.be.an('array'); - data.type.should.be.an('array'); - data.type.should.include('VerifiableCredential'); - data.issuer.should.not.be.empty; - }); + reportRow('URI MUST exist and be a string', config.implementationName, function () { + expect(parsedLink.q.payload.uri).to.be.a('string'); }); }); From b04603c0a7a294e036d683d8dda60c4ea3fa0b6d Mon Sep 17 00:00:00 2001 From: Yen Le Date: Tue, 12 Nov 2024 15:17:34 +0700 Subject: [PATCH 3/8] refactor: change name of folder qrlink test --- packages/vc-test-suite/config.ts | 2 +- .../helper.ts | 0 .../qrlink-verification.test.ts} | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) rename packages/vc-test-suite/tests/{QrlinkEncrypted => QrLinkVerification}/helper.ts (100%) rename packages/vc-test-suite/tests/{QrlinkEncrypted/qrlink-encrypted.test.ts => QrLinkVerification/qrlink-verification.test.ts} (87%) diff --git a/packages/vc-test-suite/config.ts b/packages/vc-test-suite/config.ts index e25ae5a8..e8d9c889 100644 --- a/packages/vc-test-suite/config.ts +++ b/packages/vc-test-suite/config.ts @@ -7,7 +7,7 @@ export default { method: 'GET', }, QrLinkUnencrypted: { - url: 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F61bdef77-0bcf-47d8-a6e6-00074e530e31.json%22%7D%7D', + url: 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F61bdef77-0bcf-47d8-a6e6-00074e530e31.json%22%2C%20%22hash%22%3A%229db052ff7b36aa3be34c4a92fb9444314b786380815984d7aba81af97123ce16%22%7D%7D', headers: {}, method: 'GET', }, diff --git a/packages/vc-test-suite/tests/QrlinkEncrypted/helper.ts b/packages/vc-test-suite/tests/QrLinkVerification/helper.ts similarity index 100% rename from packages/vc-test-suite/tests/QrlinkEncrypted/helper.ts rename to packages/vc-test-suite/tests/QrLinkVerification/helper.ts diff --git a/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts similarity index 87% rename from packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts rename to packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts index 69b219af..0559e430 100644 --- a/packages/vc-test-suite/tests/QrlinkEncrypted/qrlink-encrypted.test.ts +++ b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts @@ -102,4 +102,19 @@ describe('QR Link Verification with unencrypted data', function () { reportRow('URI MUST exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.uri).to.be.a('string'); }); + + reportRow('Hash MUST exist and be a string', config.implementationName, function () { + expect(parsedLink.q.payload.hash).to.be.a('string'); + }); + + reportRow('Hash MUST match the credential hash', config.implementationName, async function () { + const { data } = await request({ + url: parsedLink.q.payload.uri, + method: 'GET', + headers: {}, + }); + + const credentialHash = computeHash(data, HashAlgorithm.SHA256); + expect(parsedLink.q.payload.hash).to.equal(credentialHash); + }); }); From 62207bca0488f116f5e703771a165777c90a5ee5 Mon Sep 17 00:00:00 2001 From: Yen Le Date: Tue, 12 Nov 2024 15:46:38 +0700 Subject: [PATCH 4/8] refactor: change doc in readme of vc test suite --- packages/vc-test-suite/README.md | 2 +- .../tests/QrLinkVerification/qrlink-verification.test.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/vc-test-suite/README.md b/packages/vc-test-suite/README.md index 392a6c03..72d6cacc 100644 --- a/packages/vc-test-suite/README.md +++ b/packages/vc-test-suite/README.md @@ -8,7 +8,7 @@ yarn install ## Setup -There are three parts of the test suite: `Render Template 2024`, `QR Link Encryption` and `Storage`. To add your implementation to this test suite you will need to add 2 endpoints to your implementation manifest in `config.ts`: +There are three parts of the test suite: `Render Template 2024`, `QR Link Encryption` and `Storage`. To add your implementation to this test suite you will need to add some endpoints to your implementation manifest in `config.ts`: - A Render Template 2024 endpoint in the `RenderTemplate2024` property. - An Encrypted QR Link in the `QrLinkEncrypted` property. diff --git a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts index 0559e430..7d2ade17 100644 --- a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts +++ b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts @@ -1,7 +1,6 @@ import chai from 'chai'; -import { HashAlgorithm, decryptCredential } from '@mock-app/services'; -import { computeHash } from '@mock-app/services'; +import { computeHash, HashAlgorithm, decryptCredential } from '@mock-app/services'; import { reportRow, setupMatrix } from '../../helpers'; import { request } from '../../httpService'; From cd4d95af7402b5106d52e8343a9544f61225b249 Mon Sep 17 00:00:00 2001 From: Yen Le Date: Tue, 12 Nov 2024 16:00:24 +0700 Subject: [PATCH 5/8] style: separate line in qrlink verification test for easy to read --- .../tests/QrLinkVerification/qrlink-verification.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts index 7d2ade17..2b18083d 100644 --- a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts +++ b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts @@ -23,9 +23,11 @@ describe('QR Link Verification with encrypted data', function () { reportRow('Verification page link MUST exist and be a string', config.implementationName, function () { expect(parsedLink.verify_app_address).to.be.a('string'); }); + reportRow('Payload MUST exist and be an object', config.implementationName, function () { expect(parsedLink.q.payload).to.be.an('object'); }); + reportRow('URI MUST exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.uri).to.be.a('string'); }); @@ -41,9 +43,11 @@ describe('QR Link Verification with encrypted data', function () { data.should.not.be.undefined; data.should.not.be.empty; }); + reportRow('Hash MUST exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.hash).to.be.a('string'); }); + reportRow('Hash MUST match the credential hash', config.implementationName, async function () { const { data } = await request({ url: parsedLink.q.payload.uri, @@ -58,9 +62,11 @@ describe('QR Link Verification with encrypted data', function () { const credentialHash = computeHash(vc, HashAlgorithm.SHA256); expect(parsedLink.q.payload.hash).to.equal(credentialHash); }); + reportRow('Key exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.key).to.be.a('string'); }); + reportRow('Key MUST decrypt the encrypted credential', config.implementationName, async function () { const { data } = await request({ url: parsedLink.q.payload.uri, From 174a837fc5fa8545c76998c7890acbf90d8cfda1 Mon Sep 17 00:00:00 2001 From: Yen Le Date: Tue, 12 Nov 2024 16:09:36 +0700 Subject: [PATCH 6/8] refactor: rename variable in qr link verification --- packages/vc-test-suite/README.md | 2 +- .../qrlink-verification.test.ts | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/vc-test-suite/README.md b/packages/vc-test-suite/README.md index 72d6cacc..5595fd26 100644 --- a/packages/vc-test-suite/README.md +++ b/packages/vc-test-suite/README.md @@ -8,7 +8,7 @@ yarn install ## Setup -There are three parts of the test suite: `Render Template 2024`, `QR Link Encryption` and `Storage`. To add your implementation to this test suite you will need to add some endpoints to your implementation manifest in `config.ts`: +There are three parts of the test suite: `Render Template 2024`, `QR Link Verification` and `Storage`. To add your implementation to this test suite you will need to add some endpoints to your implementation manifest in `config.ts`: - A Render Template 2024 endpoint in the `RenderTemplate2024` property. - An Encrypted QR Link in the `QrLinkEncrypted` property. diff --git a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts index 2b18083d..6d55e2a9 100644 --- a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts +++ b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts @@ -39,9 +39,7 @@ describe('QR Link Verification with encrypted data', function () { headers, }); - data.should.not.be.null; - data.should.not.be.undefined; - data.should.not.be.empty; + data.should.be.an('object'); }); reportRow('Hash MUST exist and be a string', config.implementationName, function () { @@ -66,22 +64,21 @@ describe('QR Link Verification with encrypted data', function () { reportRow('Key exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.key).to.be.a('string'); }); - + reportRow('Key MUST decrypt the encrypted credential', config.implementationName, async function () { const { data } = await request({ url: parsedLink.q.payload.uri, method, headers, }); - const stringifyVC = decryptCredential({ + const credential = decryptCredential({ ...data, key: parsedLink.q.payload.key, }); - // Assert that stringifyVC is not null, empty, or undefined - stringifyVC.should.not.be.null; - stringifyVC.should.not.be.undefined; - stringifyVC.should.not.be.empty; + credential.should.not.be.null; + credential.should.not.be.undefined; + credential.should.not.be.empty; }); }); From f80f6db7fab48db63ebf22f7a25b090416d45ee6 Mon Sep 17 00:00:00 2001 From: Yen Le Date: Tue, 12 Nov 2024 16:38:06 +0700 Subject: [PATCH 7/8] test: add test case to check key exist in qrlink --- .../QrLinkVerification/helper.test.ts | 60 +++++++++++++++++++ packages/vc-test-suite/jest.config.js | 2 +- .../qrlink-verification.test.ts | 18 +++++- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 packages/vc-test-suite/__tests__/QrLinkVerification/helper.test.ts diff --git a/packages/vc-test-suite/__tests__/QrLinkVerification/helper.test.ts b/packages/vc-test-suite/__tests__/QrLinkVerification/helper.test.ts new file mode 100644 index 00000000..87410a15 --- /dev/null +++ b/packages/vc-test-suite/__tests__/QrLinkVerification/helper.test.ts @@ -0,0 +1,60 @@ +import { parseQRLink, isURLEncoded } from '../../tests/QrLinkVerification/helper'; + +describe('Helper Functions', () => { + describe('parseQRLink', () => { + it('should parse the QR code link correctly', () => { + const qrcodeLink = + 'https://example.com/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fexample.com%2Fcredential%22%2C%22key%22%3A%22some-key%22%2C%22hash%22%3A%22some-hash%22%7D%7D'; + const result = parseQRLink(qrcodeLink); + + expect(result).toEqual({ + verify_app_address: 'https://example.com/verify', + q: { + payload: { + uri: 'https://example.com/credential', + key: 'some-key', + hash: 'some-hash', + }, + }, + }); + }); + + it('should handle missing query parameter gracefully', () => { + JSON.parse = jest.fn().mockImplementation((value) => { + return { + payload: { + uri: undefined, + key: undefined, + hash: undefined, + }, + }; + }); + + const qrcodeLink = 'https://example.com/verify'; + const result = parseQRLink(qrcodeLink); + + expect(result).toEqual({ + verify_app_address: 'https://example.com/verify', + q: { + payload: { + uri: undefined, + key: undefined, + hash: undefined, + }, + }, + }); + }); + }); + + describe('isURLEncoded', () => { + it('should return true for URL encoded strings', () => { + expect(isURLEncoded('%20')).toBe(true); + expect(isURLEncoded('%7B')).toBe(true); + }); + + it('should return false for non-URL encoded strings', () => { + expect(isURLEncoded(' ')).toBe(false); + expect(isURLEncoded('{')).toBe(false); + }); + }); +}); diff --git a/packages/vc-test-suite/jest.config.js b/packages/vc-test-suite/jest.config.js index 396b375f..c68b31e4 100644 --- a/packages/vc-test-suite/jest.config.js +++ b/packages/vc-test-suite/jest.config.js @@ -1,7 +1,7 @@ import base from '../../jest.config.base.js'; const jestConfig = { ...base, - collectCoverageFrom: ['/tests/**/*.{ts,tsx}', '!**/*.d.ts'], + collectCoverageFrom: ['/tests/**/*.{ts,tsx}', '!**/*.d.ts', '!/tests/**/*.test.{ts,tsx}'], preset: 'ts-jest', testEnvironment: 'node', roots: [''], diff --git a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts index 6d55e2a9..25662b8d 100644 --- a/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts +++ b/packages/vc-test-suite/tests/QrLinkVerification/qrlink-verification.test.ts @@ -83,11 +83,15 @@ describe('QR Link Verification with encrypted data', function () { }); describe('QR Link Verification with unencrypted data', function () { - const { url: qrLink } = config.testSuites.QrLinkUnencrypted; + const { url: qrLink, method, headers } = config.testSuites.QrLinkUnencrypted; const parsedLink = parseQRLink(qrLink); setupMatrix.call(this, [config.implementationName], 'Implementer'); + reportRow('Fails if decryption key exists', config.implementationName, function () { + expect(parsedLink.q.payload.key).to.be.undefined; + }); + reportRow('QR link MUST be URL encoded', config.implementationName, function () { const data = isURLEncoded(qrLink); data.should.be.true; @@ -105,6 +109,18 @@ describe('QR Link Verification with unencrypted data', function () { expect(parsedLink.q.payload.uri).to.be.a('string'); }); + reportRow('URI MUST be fetched', config.implementationName, async function () { + const { data } = await request({ + url: parsedLink.q.payload.uri, + method, + headers, + }); + + data.should.not.be.null; + data.should.not.be.undefined; + data.should.not.be.empty; + }); + reportRow('Hash MUST exist and be a string', config.implementationName, function () { expect(parsedLink.q.payload.hash).to.be.a('string'); }); From 007672886fb74b79d3eea99131e4456039206b5a Mon Sep 17 00:00:00 2001 From: Yen Le Date: Tue, 12 Nov 2024 17:00:37 +0700 Subject: [PATCH 8/8] chore: update jest config in untp test suite --- packages/untp-test-suite/jest.config.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/untp-test-suite/jest.config.js b/packages/untp-test-suite/jest.config.js index ea2315dd..d9033500 100644 --- a/packages/untp-test-suite/jest.config.js +++ b/packages/untp-test-suite/jest.config.js @@ -6,7 +6,14 @@ export default { rootDir: './', moduleFileExtensions: [...defaults.moduleFileExtensions, 'mts'], collectCoverage: false, - collectCoverageFrom: ['!**/examples/**', '!**/types/**', '!**/build/**', '!**/node_modules/**', '!**/**/index.ts'], + collectCoverageFrom: [ + '!**/examples/**', + '!**/types/**', + '!**/build/**', + '!**/node_modules/**', + '!**/**/index.ts', + '/src/**/*.ts', + ], coverageReporters: ['text', 'lcov', 'json', 'json-summary'], coverageProvider: 'v8', coverageDirectory: './coverage',