diff --git a/test/mocha/30-backwards-compatibility.js b/test/mocha/30-backwards-compatibility.js index 711ff191..ae1f85de 100644 --- a/test/mocha/30-backwards-compatibility.js +++ b/test/mocha/30-backwards-compatibility.js @@ -114,173 +114,167 @@ describe('issue APIs - Reference ID `assertionMethod:foo` backwards ' + should.exist(config.zcaps['assertionMethod:Ed25519']); } }); - describe('/credentials/issue', () => { - it('issues a valid credential w/no "credentialStatus"', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - result = await zcapClient.write({ - url: `${noStatusListIssuerId}/credentials/issue`, - capability: noStatusListIssuerRootZcap, - json: { - credential - } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - should.exist(verifiableCredential.issuer); - should.exist(verifiableCredential.issuanceDate); - should.exist(verifiableCredential.credentialSubject); - verifiableCredential.credentialSubject.should.be.an('object'); - should.not.exist(verifiableCredential.credentialStatus); - should.exist(verifiableCredential.proof); - verifiableCredential.proof.should.be.an('object'); - }); - it('issues a valid credential w/ SL 2021 "credentialStatus" and ' + - 'suspension status purpose', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - result = await zcapClient.write({ - url: `${sl2021Suspension.issuerId}/credentials/issue`, - capability: sl2021Suspension.rootZcap, - json: { - credential - } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - should.exist(verifiableCredential.issuer); - should.exist(verifiableCredential.issuanceDate); - should.exist(verifiableCredential.credentialSubject); - verifiableCredential.credentialSubject.should.be.an('object'); - should.exist(verifiableCredential.credentialStatus); - should.exist(verifiableCredential.proof); - verifiableCredential.proof.should.be.an('object'); - }); + it('issues a valid credential w/no "credentialStatus"', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + result = await zcapClient.write({ + url: `${noStatusListIssuerId}/credentials/issue`, + capability: noStatusListIssuerRootZcap, + json: { + credential + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + should.exist(verifiableCredential.issuer); + should.exist(verifiableCredential.issuanceDate); + should.exist(verifiableCredential.credentialSubject); + verifiableCredential.credentialSubject.should.be.an('object'); + should.not.exist(verifiableCredential.credentialStatus); + should.exist(verifiableCredential.proof); + verifiableCredential.proof.should.be.an('object'); + }); + it('issues a valid credential w/ SL 2021 "credentialStatus" and ' + + 'suspension status purpose', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + result = await zcapClient.write({ + url: `${sl2021Suspension.issuerId}/credentials/issue`, + capability: sl2021Suspension.rootZcap, + json: { + credential + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + should.exist(verifiableCredential.issuer); + should.exist(verifiableCredential.issuanceDate); + should.exist(verifiableCredential.credentialSubject); + verifiableCredential.credentialSubject.should.be.an('object'); + should.exist(verifiableCredential.credentialStatus); + should.exist(verifiableCredential.proof); + verifiableCredential.proof.should.be.an('object'); }); - describe('/credentials/status', () => { - it('updates a StatusList2021 revocation credential status', - async () => { - // first issue VC - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {data: {verifiableCredential}} = await zcapClient.write({ - url: `${sl2021Revocation.issuerId}/credentials/issue`, - capability: sl2021Revocation.rootZcap, - json: {credential} - }); + it('updates a StatusList2021 revocation credential status', async () => { + // first issue VC + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {data: {verifiableCredential}} = await zcapClient.write({ + url: `${sl2021Revocation.issuerId}/credentials/issue`, + capability: sl2021Revocation.rootZcap, + json: {credential} + }); - // get VC status - const statusInfo = await helpers.getCredentialStatus( - {verifiableCredential}); - let {status} = statusInfo; - status.should.equal(false); + // get VC status + const statusInfo = await helpers.getCredentialStatus( + {verifiableCredential}); + let {status} = statusInfo; + status.should.equal(false); - // then revoke VC - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - sl2021Revocation.issuerConfig; - await zcapClient.write({ - url: `${sl2021Revocation.statusId}/credentials/status`, - capability: sl2021Revocation.statusRootZcap, - json: { - credentialId: verifiableCredential.id, - indexAllocator, - credentialStatus: verifiableCredential.credentialStatus, - status: true - } - }); - } catch(e) { - error = e; + // then revoke VC + let error; + try { + const {statusListOptions: [{indexAllocator}]} = + sl2021Revocation.issuerConfig; + await zcapClient.write({ + url: `${sl2021Revocation.statusId}/credentials/status`, + capability: sl2021Revocation.statusRootZcap, + json: { + credentialId: verifiableCredential.id, + indexAllocator, + credentialStatus: verifiableCredential.credentialStatus, + status: true } - assertNoError(error); + }); + } catch(e) { + error = e; + } + assertNoError(error); - // force refresh of new SLC - await zcapClient.write({ - url: `${statusInfo.statusListCredential}?refresh=true`, - capability: sl2021Revocation.statusRootZcap, - json: {} - }); + // force refresh of new SLC + await zcapClient.write({ + url: `${statusInfo.statusListCredential}?refresh=true`, + capability: sl2021Revocation.statusRootZcap, + json: {} + }); - // check status of VC has changed - ({status} = await helpers.getCredentialStatus( - {verifiableCredential})); - status.should.equal(true); - }); - it('updates a StatusList2021 suspension credential status', - async () => { - // first issue VC - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {data: {verifiableCredential}} = await zcapClient.write({ - url: `${sl2021Suspension.issuerId}/credentials/issue`, - capability: sl2021Suspension.rootZcap, - json: {credential} - }); + // check status of VC has changed + ({status} = await helpers.getCredentialStatus( + {verifiableCredential})); + status.should.equal(true); + }); + it('updates a StatusList2021 suspension credential status', async () => { + // first issue VC + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {data: {verifiableCredential}} = await zcapClient.write({ + url: `${sl2021Suspension.issuerId}/credentials/issue`, + capability: sl2021Suspension.rootZcap, + json: {credential} + }); - // get VC status - const statusInfo = await helpers.getCredentialStatus( - {verifiableCredential}); - let {status} = statusInfo; - status.should.equal(false); + // get VC status + const statusInfo = await helpers.getCredentialStatus( + {verifiableCredential}); + let {status} = statusInfo; + status.should.equal(false); - // then revoke VC - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - sl2021Suspension.issuerConfig; - await zcapClient.write({ - url: `${sl2021Suspension.statusId}/credentials/status`, - capability: sl2021Suspension.statusRootZcap, - json: { - credentialId: verifiableCredential.id, - indexAllocator, - credentialStatus: verifiableCredential.credentialStatus, - status: true - } - }); - } catch(e) { - error = e; + // then revoke VC + let error; + try { + const {statusListOptions: [{indexAllocator}]} = + sl2021Suspension.issuerConfig; + await zcapClient.write({ + url: `${sl2021Suspension.statusId}/credentials/status`, + capability: sl2021Suspension.statusRootZcap, + json: { + credentialId: verifiableCredential.id, + indexAllocator, + credentialStatus: verifiableCredential.credentialStatus, + status: true } - assertNoError(error); + }); + } catch(e) { + error = e; + } + assertNoError(error); - // force refresh of new SLC - await zcapClient.write({ - url: `${statusInfo.statusListCredential}?refresh=true`, - capability: sl2021Suspension.statusRootZcap, - json: {} - }); + // force refresh of new SLC + await zcapClient.write({ + url: `${statusInfo.statusListCredential}?refresh=true`, + capability: sl2021Suspension.statusRootZcap, + json: {} + }); - // check status of VC has changed - ({status} = await helpers.getCredentialStatus( - {verifiableCredential})); - status.should.equal(true); - }); + // check status of VC has changed + ({status} = await helpers.getCredentialStatus( + {verifiableCredential})); + status.should.equal(true); }); }); }); diff --git a/test/mocha/40-did-web-issuer.js b/test/mocha/40-did-web-issuer.js index 2cf209cd..e3f590b6 100644 --- a/test/mocha/40-did-web-issuer.js +++ b/test/mocha/40-did-web-issuer.js @@ -102,85 +102,83 @@ describe('issue using "did:web" issuer', () => { noStatusListIssuerRootZcap = `urn:zcap:root:${encodeURIComponent(noStatusListIssuerId)}`; }); - describe('/credentials/issue', () => { - it('issues a VC 1.1 credential with a proof set', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - result = await zcapClient.write({ - url: `${noStatusListIssuerId}/credentials/issue`, - capability: noStatusListIssuerRootZcap, - json: { - credential, - options: { - extraInformation: 'abc' - } + it('issues a VC 1.1 credential with a proof set', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + result = await zcapClient.write({ + url: `${noStatusListIssuerId}/credentials/issue`, + capability: noStatusListIssuerRootZcap, + json: { + credential, + options: { + extraInformation: 'abc' } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - should.exist(verifiableCredential.issuer); - should.exist(verifiableCredential.issuanceDate); - should.exist(verifiableCredential.credentialSubject); - verifiableCredential.credentialSubject.should.be.an('object'); - should.not.exist(verifiableCredential.credentialStatus); - should.exist(verifiableCredential.proof); - verifiableCredential.proof.should.be.an('array'); - verifiableCredential.proof.length.should.equal(suites.length); - const parsedCryptosuites = verifiableCredential.proof.map( - ({type, cryptosuite}) => cryptosuite ?? type); - const expectedCryptosuites = suites.map(({name}) => name); - parsedCryptosuites.should.deep.equal(expectedCryptosuites); - }); - it('issues a VC 2.0 credential with a proof set', async () => { - const credential = klona(mockCredentialV2); - let error; - let result; - try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - result = await zcapClient.write({ - url: `${noStatusListIssuerId}/credentials/issue`, - capability: noStatusListIssuerRootZcap, - json: { - credential, - options: { - extraInformation: 'abc' - } + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + should.exist(verifiableCredential.issuer); + should.exist(verifiableCredential.issuanceDate); + should.exist(verifiableCredential.credentialSubject); + verifiableCredential.credentialSubject.should.be.an('object'); + should.not.exist(verifiableCredential.credentialStatus); + should.exist(verifiableCredential.proof); + verifiableCredential.proof.should.be.an('array'); + verifiableCredential.proof.length.should.equal(suites.length); + const parsedCryptosuites = verifiableCredential.proof.map( + ({type, cryptosuite}) => cryptosuite ?? type); + const expectedCryptosuites = suites.map(({name}) => name); + parsedCryptosuites.should.deep.equal(expectedCryptosuites); + }); + it('issues a VC 2.0 credential with a proof set', async () => { + const credential = klona(mockCredentialV2); + let error; + let result; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + result = await zcapClient.write({ + url: `${noStatusListIssuerId}/credentials/issue`, + capability: noStatusListIssuerRootZcap, + json: { + credential, + options: { + extraInformation: 'abc' } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - should.exist(verifiableCredential.issuer); - should.exist(verifiableCredential.credentialSubject); - verifiableCredential.credentialSubject.should.be.an('object'); - should.not.exist(verifiableCredential.credentialStatus); - should.exist(verifiableCredential.proof); - verifiableCredential.proof.should.be.an('array'); - verifiableCredential.proof.length.should.equal(suites.length); - const parsedCryptosuites = verifiableCredential.proof.map( - ({type, cryptosuite}) => cryptosuite ?? type); - const expectedCryptosuites = suites.map(({name}) => name); - parsedCryptosuites.should.deep.equal(expectedCryptosuites); - }); + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + should.exist(verifiableCredential.issuer); + should.exist(verifiableCredential.credentialSubject); + verifiableCredential.credentialSubject.should.be.an('object'); + should.not.exist(verifiableCredential.credentialStatus); + should.exist(verifiableCredential.proof); + verifiableCredential.proof.should.be.an('array'); + verifiableCredential.proof.length.should.equal(suites.length); + const parsedCryptosuites = verifiableCredential.proof.map( + ({type, cryptosuite}) => cryptosuite ?? type); + const expectedCryptosuites = suites.map(({name}) => name); + parsedCryptosuites.should.deep.equal(expectedCryptosuites); }); }); diff --git a/test/mocha/50-vc-jwt-issuer.js b/test/mocha/50-vc-jwt-issuer.js index aa641810..7d70a1ba 100644 --- a/test/mocha/50-vc-jwt-issuer.js +++ b/test/mocha/50-vc-jwt-issuer.js @@ -81,58 +81,56 @@ describe('issue using VC-JWT format', () => { noStatusListIssuerRootZcap = `urn:zcap:root:${encodeURIComponent(noStatusListIssuerId)}`; }); - describe('/credentials/issue', () => { - it('issues a VC-JWT VC 1.1 credential', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - result = await zcapClient.write({ - url: `${noStatusListIssuerId}/credentials/issue`, - capability: noStatusListIssuerRootZcap, - json: { - credential, - options: { - extraInformation: 'abc' - } + it('issues a VC-JWT VC 1.1 credential', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + result = await zcapClient.write({ + url: `${noStatusListIssuerId}/credentials/issue`, + capability: noStatusListIssuerRootZcap, + json: { + credential, + options: { + extraInformation: 'abc' } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - verifiableCredential.type.should.equal('EnvelopedVerifiableCredential'); - verifiableCredential.id.should.be.a('string'); - verifiableCredential.id.should.include('data:application/jwt,'); + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + verifiableCredential.type.should.equal('EnvelopedVerifiableCredential'); + verifiableCredential.id.should.be.a('string'); + verifiableCredential.id.should.include('data:application/jwt,'); - // assert JWT contents - const jwt = verifiableCredential.id.slice('data:application/jwt,'.length); - const split = jwt.split('.'); - split.length.should.equal(3); - const header = JSON.parse( - new TextDecoder().decode(base64url.decode(split[0]))); - const payload = JSON.parse( - new TextDecoder().decode(base64url.decode(split[1]))); - header.kid.should.equal(assertionMethodKeyId); - header.alg.should.equal('ES256'); - payload.iss.should.equal(did); - payload.jti.should.equal(credential.id); - payload.sub.should.equal(credential.credentialSubject.id); - should.exist(payload.vc); - const expectedCredential = { - ...credential, - issuer: did, - issuanceDate: payload.vc.issuanceDate ?? 'error: missing date' - }; - payload.vc.should.deep.equal(expectedCredential); - }); + // assert JWT contents + const jwt = verifiableCredential.id.slice('data:application/jwt,'.length); + const split = jwt.split('.'); + split.length.should.equal(3); + const header = JSON.parse( + new TextDecoder().decode(base64url.decode(split[0]))); + const payload = JSON.parse( + new TextDecoder().decode(base64url.decode(split[1]))); + header.kid.should.equal(assertionMethodKeyId); + header.alg.should.equal('ES256'); + payload.iss.should.equal(did); + payload.jti.should.equal(credential.id); + payload.sub.should.equal(credential.credentialSubject.id); + should.exist(payload.vc); + const expectedCredential = { + ...credential, + issuer: did, + issuanceDate: payload.vc.issuanceDate ?? 'error: missing date' + }; + payload.vc.should.deep.equal(expectedCredential); }); }); diff --git a/test/mocha/60-vc-jwt-issuer-status.js b/test/mocha/60-vc-jwt-issuer-status.js index 12addae7..72545724 100644 --- a/test/mocha/60-vc-jwt-issuer-status.js +++ b/test/mocha/60-vc-jwt-issuer-status.js @@ -103,125 +103,120 @@ describe('issue using VC-JWT format w/status list support', () => { statusId = statusConfig.id; statusRootZcap = `urn:zcap:root:${encodeURIComponent(statusConfig.id)}`; }); - describe('/credentials/issue', () => { - it('issues a VC-JWT VC 1.1 credential', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - result = await zcapClient.write({ - url: `${issuerId}/credentials/issue`, - capability: issuerRootZcap, - json: { - credential, - options: { - extraInformation: 'abc' - } + it('issues a VC-JWT VC 1.1 credential', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + result = await zcapClient.write({ + url: `${issuerId}/credentials/issue`, + capability: issuerRootZcap, + json: { + credential, + options: { + extraInformation: 'abc' } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - verifiableCredential.type.should.equal('EnvelopedVerifiableCredential'); - verifiableCredential.id.should.be.a('string'); - verifiableCredential.id.should.include('data:application/jwt,'); + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + verifiableCredential.type.should.equal('EnvelopedVerifiableCredential'); + verifiableCredential.id.should.be.a('string'); + verifiableCredential.id.should.include('data:application/jwt,'); - // assert JWT contents - const jwt = verifiableCredential.id.slice('data:application/jwt,'.length); - const split = jwt.split('.'); - split.length.should.equal(3); - const header = JSON.parse( - new TextDecoder().decode(base64url.decode(split[0]))); - const payload = JSON.parse( - new TextDecoder().decode(base64url.decode(split[1]))); - header.kid.should.equal(assertionMethodKeyId); - header.alg.should.equal('ES256'); - payload.iss.should.equal(did); - payload.jti.should.equal(credential.id); - payload.sub.should.equal(credential.credentialSubject.id); - should.exist(payload.vc); - // assert credential status is set in payload - should.exist(payload.vc.credentialStatus); - // assert other properties - const expectedCredential = { - ...credential, - '@context': [ - ...credential['@context'], - 'https://www.w3.org/ns/credentials/status/v1' - ], - issuer: did, - issuanceDate: payload.vc.issuanceDate ?? 'error: missing date' - }; - const moduloStatus = {...payload.vc}; - delete moduloStatus.credentialStatus; - moduloStatus.should.deep.equal(expectedCredential); - }); + // assert JWT contents + const jwt = verifiableCredential.id.slice('data:application/jwt,'.length); + const split = jwt.split('.'); + split.length.should.equal(3); + const header = JSON.parse( + new TextDecoder().decode(base64url.decode(split[0]))); + const payload = JSON.parse( + new TextDecoder().decode(base64url.decode(split[1]))); + header.kid.should.equal(assertionMethodKeyId); + header.alg.should.equal('ES256'); + payload.iss.should.equal(did); + payload.jti.should.equal(credential.id); + payload.sub.should.equal(credential.credentialSubject.id); + should.exist(payload.vc); + // assert credential status is set in payload + should.exist(payload.vc.credentialStatus); + // assert other properties + const expectedCredential = { + ...credential, + '@context': [ + ...credential['@context'], + 'https://www.w3.org/ns/credentials/status/v1' + ], + issuer: did, + issuanceDate: payload.vc.issuanceDate ?? 'error: missing date' + }; + const moduloStatus = {...payload.vc}; + delete moduloStatus.credentialStatus; + moduloStatus.should.deep.equal(expectedCredential); }); - describe('/credentials/status', () => { - it('updates a BitstringStatusList revocation credential status', - async () => { - // first issue VC - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - let {data: {verifiableCredential}} = await zcapClient.write({ - url: `${issuerId}/credentials/issue`, - capability: issuerRootZcap, - json: {credential} - }); + it('updates a BitstringStatusList revocation credential status', async () => { + // first issue VC + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + let {data: {verifiableCredential}} = await zcapClient.write({ + url: `${issuerId}/credentials/issue`, + capability: issuerRootZcap, + json: {credential} + }); - // parse enveloped VC as needed - if(verifiableCredential.type === 'EnvelopedVerifiableCredential') { - verifiableCredential = helpers.parseEnvelope({verifiableCredential}); - } + // parse enveloped VC as needed + if(verifiableCredential.type === 'EnvelopedVerifiableCredential') { + verifiableCredential = helpers.parseEnvelope({verifiableCredential}); + } - // get VC status - const statusInfo = await helpers.getCredentialStatus( - {verifiableCredential}); - let {status} = statusInfo; - status.should.equal(false); + // get VC status + const statusInfo = await helpers.getCredentialStatus( + {verifiableCredential}); + let {status} = statusInfo; + status.should.equal(false); - // then revoke VC - let error; - try { - const {statusListOptions: [{indexAllocator}]} = issuerConfig; - await zcapClient.write({ - url: `${statusId}/credentials/status`, - capability: statusRootZcap, - json: { - credentialId: verifiableCredential.id, - indexAllocator, - credentialStatus: verifiableCredential.credentialStatus, - status: true - } - }); - } catch(e) { - error = e; + // then revoke VC + let error; + try { + const {statusListOptions: [{indexAllocator}]} = issuerConfig; + await zcapClient.write({ + url: `${statusId}/credentials/status`, + capability: statusRootZcap, + json: { + credentialId: verifiableCredential.id, + indexAllocator, + credentialStatus: verifiableCredential.credentialStatus, + status: true } - assertNoError(error); + }); + } catch(e) { + error = e; + } + assertNoError(error); - // force refresh of new SLC - await zcapClient.write({ - url: `${statusInfo.statusListCredential}?refresh=true`, - capability: statusRootZcap, - json: {} - }); + // force refresh of new SLC + await zcapClient.write({ + url: `${statusInfo.statusListCredential}?refresh=true`, + capability: statusRootZcap, + json: {} + }); - // check status of VC has changed - ({status} = await helpers.getCredentialStatus( - {verifiableCredential})); - status.should.equal(true); + // check status of VC has changed + ({status} = await helpers.getCredentialStatus( + {verifiableCredential})); + status.should.equal(true); - // FIXME: check returned status VC envelope and ensure it is - // JWT-encoded - }); + // FIXME: check returned status VC envelope and ensure it is + // JWT-encoded }); }); diff --git a/test/mocha/assertions/issueWithOAuth2.js b/test/mocha/assertions/issueWithOAuth2.js index 722a3e0e..cfc49ee8 100644 --- a/test/mocha/assertions/issueWithOAuth2.js +++ b/test/mocha/assertions/issueWithOAuth2.js @@ -38,75 +38,41 @@ export function testIssueWithOAuth2({suiteName, algorithm, issueOptions}) { oauth2IssuerConfig = await helpers.createIssuerConfig( {capabilityAgent, zcaps, oauth2: true, suiteName}); }); - describe('/credentials/issue', () => { - it('issues a valid credential w/oauth2 w/root scope', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const configId = oauth2IssuerConfig.id; - const url = `${configId}/credentials/issue`; - const accessToken = await helpers.getOAuth2AccessToken( - {configId, action: 'write', target: '/'}); - result = await httpClient.post(url, { - agent, - headers: {authorization: `Bearer ${accessToken}`}, - json: {credential, options: issueOptions} - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - should.exist(verifiableCredential.issuer); - should.exist(verifiableCredential.issuanceDate); - should.exist(verifiableCredential.credentialSubject); - verifiableCredential.credentialSubject.should.be.an('object'); - should.not.exist(verifiableCredential.credentialStatus); - should.exist(verifiableCredential.proof); - verifiableCredential.proof.should.be.an('object'); - }); - it('issues a valid credential w/oauth2 w/credentials scope', - async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const configId = oauth2IssuerConfig.id; - const url = `${configId}/credentials/issue`; - const accessToken = await helpers.getOAuth2AccessToken( - {configId, action: 'write', target: '/credentials'}); - result = await httpClient.post(url, { - agent, - headers: {authorization: `Bearer ${accessToken}`}, - json: {credential, options: issueOptions} - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.id); - should.exist(verifiableCredential.type); - should.exist(verifiableCredential.issuer); - should.exist(verifiableCredential.issuanceDate); - should.exist(verifiableCredential.credentialSubject); - verifiableCredential.credentialSubject.should.be.an('object'); - should.not.exist(verifiableCredential.credentialStatus); - should.exist(verifiableCredential.proof); - verifiableCredential.proof.should.be.an('object'); + it('issues a valid credential w/oauth2 w/root scope', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const configId = oauth2IssuerConfig.id; + const url = `${configId}/credentials/issue`; + const accessToken = await helpers.getOAuth2AccessToken( + {configId, action: 'write', target: '/'}); + result = await httpClient.post(url, { + agent, + headers: {authorization: `Bearer ${accessToken}`}, + json: {credential, options: issueOptions} }); - it('issues a valid credential w/oauth2 w/targeted scope', async () => { + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + should.exist(verifiableCredential.issuer); + should.exist(verifiableCredential.issuanceDate); + should.exist(verifiableCredential.credentialSubject); + verifiableCredential.credentialSubject.should.be.an('object'); + should.not.exist(verifiableCredential.credentialStatus); + should.exist(verifiableCredential.proof); + verifiableCredential.proof.should.be.an('object'); + }); + it('issues a valid credential w/oauth2 w/credentials scope', + async () => { const credential = klona(mockCredential); let error; let result; @@ -114,7 +80,7 @@ export function testIssueWithOAuth2({suiteName, algorithm, issueOptions}) { const configId = oauth2IssuerConfig.id; const url = `${configId}/credentials/issue`; const accessToken = await helpers.getOAuth2AccessToken( - {configId, action: 'write', target: '/credentials/issue'}); + {configId, action: 'write', target: '/credentials'}); result = await httpClient.post(url, { agent, headers: {authorization: `Bearer ${accessToken}`}, @@ -139,66 +105,98 @@ export function testIssueWithOAuth2({suiteName, algorithm, issueOptions}) { should.exist(verifiableCredential.proof); verifiableCredential.proof.should.be.an('object'); }); - it('fails to issue a valid credential w/bad action scope', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const configId = oauth2IssuerConfig.id; - const url = `${configId}/credentials/issue`; - const accessToken = await helpers.getOAuth2AccessToken( - // wrong action: `read` - {configId, action: 'read', target: '/credentials/issue'}); - result = await httpClient.post(url, { - agent, - headers: {authorization: `Bearer ${accessToken}`}, - json: {credential, options: issueOptions} - }); - } catch(e) { - error = e; - } - should.exist(error); - should.not.exist(result); - error.status.should.equal(403); - error.data.type.should.equal('NotAllowedError'); - should.exist(error.data.cause); - should.exist(error.data.cause.details); - should.exist(error.data.cause.details.code); - error.data.cause.details.code.should.equal( - 'ERR_JWT_CLAIM_VALIDATION_FAILED'); - should.exist(error.data.cause.details.claim); - error.data.cause.details.claim.should.equal('scope'); - }); - it('fails to issue a valid credential w/bad path scope', async () => { - const credential = klona(mockCredential); - let error; - let result; - try { - const configId = oauth2IssuerConfig.id; - const url = `${configId}/credentials/issue`; - const accessToken = await helpers.getOAuth2AccessToken( - // wrong path: `/foo` - {configId, action: 'write', target: '/foo'}); - result = await httpClient.post(url, { - agent, - headers: {authorization: `Bearer ${accessToken}`}, - json: {credential, options: issueOptions} - }); - } catch(e) { - error = e; - } - should.exist(error); - should.not.exist(result); - error.status.should.equal(403); - error.data.type.should.equal('NotAllowedError'); - should.exist(error.data.cause); - should.exist(error.data.cause.details); - should.exist(error.data.cause.details.code); - error.data.cause.details.code.should.equal( - 'ERR_JWT_CLAIM_VALIDATION_FAILED'); - should.exist(error.data.cause.details.claim); - error.data.cause.details.claim.should.equal('scope'); - }); + it('issues a valid credential w/oauth2 w/targeted scope', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const configId = oauth2IssuerConfig.id; + const url = `${configId}/credentials/issue`; + const accessToken = await helpers.getOAuth2AccessToken( + {configId, action: 'write', target: '/credentials/issue'}); + result = await httpClient.post(url, { + agent, + headers: {authorization: `Bearer ${accessToken}`}, + json: {credential, options: issueOptions} + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.id); + should.exist(verifiableCredential.type); + should.exist(verifiableCredential.issuer); + should.exist(verifiableCredential.issuanceDate); + should.exist(verifiableCredential.credentialSubject); + verifiableCredential.credentialSubject.should.be.an('object'); + should.not.exist(verifiableCredential.credentialStatus); + should.exist(verifiableCredential.proof); + verifiableCredential.proof.should.be.an('object'); + }); + it('fails to issue a valid credential w/bad action scope', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const configId = oauth2IssuerConfig.id; + const url = `${configId}/credentials/issue`; + const accessToken = await helpers.getOAuth2AccessToken( + // wrong action: `read` + {configId, action: 'read', target: '/credentials/issue'}); + result = await httpClient.post(url, { + agent, + headers: {authorization: `Bearer ${accessToken}`}, + json: {credential, options: issueOptions} + }); + } catch(e) { + error = e; + } + should.exist(error); + should.not.exist(result); + error.status.should.equal(403); + error.data.type.should.equal('NotAllowedError'); + should.exist(error.data.cause); + should.exist(error.data.cause.details); + should.exist(error.data.cause.details.code); + error.data.cause.details.code.should.equal( + 'ERR_JWT_CLAIM_VALIDATION_FAILED'); + should.exist(error.data.cause.details.claim); + error.data.cause.details.claim.should.equal('scope'); + }); + it('fails to issue a valid credential w/bad path scope', async () => { + const credential = klona(mockCredential); + let error; + let result; + try { + const configId = oauth2IssuerConfig.id; + const url = `${configId}/credentials/issue`; + const accessToken = await helpers.getOAuth2AccessToken( + // wrong path: `/foo` + {configId, action: 'write', target: '/foo'}); + result = await httpClient.post(url, { + agent, + headers: {authorization: `Bearer ${accessToken}`}, + json: {credential, options: issueOptions} + }); + } catch(e) { + error = e; + } + should.exist(error); + should.not.exist(result); + error.status.should.equal(403); + error.data.type.should.equal('NotAllowedError'); + should.exist(error.data.cause); + should.exist(error.data.cause.details); + should.exist(error.data.cause.details.code); + error.data.cause.details.code.should.equal( + 'ERR_JWT_CLAIM_VALIDATION_FAILED'); + should.exist(error.data.cause.details.claim); + error.data.cause.details.claim.should.equal('scope'); }); }); } diff --git a/test/mocha/assertions/issueWithoutStatus.js b/test/mocha/assertions/issueWithoutStatus.js index 3680b8d8..94384c2a 100644 --- a/test/mocha/assertions/issueWithoutStatus.js +++ b/test/mocha/assertions/issueWithoutStatus.js @@ -43,51 +43,49 @@ export function testIssueWithoutStatus({suiteName, algorithm, issueOptions}) { url: noStatusListIssuerId }); }); - describe('/credentials/issue', () => { - it('issues a valid credential w/no "credentialStatus"', async () => { - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {verifiableCredential} = await assertions.issueAndAssert({ - configId: noStatusListIssuerId, - credential, - issueOptions, - zcapClient, - capability: noStatusListIssuerRootZcap - }); - should.exist(verifiableCredential.id); - should.not.exist(verifiableCredential.credentialStatus); + it('issues a valid credential w/no "credentialStatus"', async () => { + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {verifiableCredential} = await assertions.issueAndAssert({ + configId: noStatusListIssuerId, + credential, + issueOptions, + zcapClient, + capability: noStatusListIssuerRootZcap }); - it('issues a VC 2.0 credential w/no "credentialStatus"', async () => { - const credential = klona(mockCredentialV2); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {verifiableCredential} = await assertions.issueAndAssert({ - configId: noStatusListIssuerId, - credential, - issueOptions, - zcapClient, - capability: noStatusListIssuerRootZcap - }); - should.exist(verifiableCredential.id); - should.not.exist(verifiableCredential.credentialStatus); + should.exist(verifiableCredential.id); + should.not.exist(verifiableCredential.credentialStatus); + }); + it('issues a VC 2.0 credential w/no "credentialStatus"', async () => { + const credential = klona(mockCredentialV2); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {verifiableCredential} = await assertions.issueAndAssert({ + configId: noStatusListIssuerId, + credential, + issueOptions, + zcapClient, + capability: noStatusListIssuerRootZcap }); + should.exist(verifiableCredential.id); + should.not.exist(verifiableCredential.credentialStatus); + }); - it('fails to issue an empty credential', async () => { - let error; - try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - await zcapClient.write({ - url: `${noStatusListIssuerId}/credentials/issue`, - capability: noStatusListIssuerRootZcap, - json: { - credential: {} - } - }); - } catch(e) { - error = e; - } - should.exist(error); - error.data.type.should.equal('ValidationError'); - }); + it('fails to issue an empty credential', async () => { + let error; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + await zcapClient.write({ + url: `${noStatusListIssuerId}/credentials/issue`, + capability: noStatusListIssuerRootZcap, + json: { + credential: {} + } + }); + } catch(e) { + error = e; + } + should.exist(error); + error.data.type.should.equal('ValidationError'); }); }); } diff --git a/test/mocha/assertions/testBitstringStatusList.js b/test/mocha/assertions/testBitstringStatusList.js index 2cfa69ea..cf5480b7 100644 --- a/test/mocha/assertions/testBitstringStatusList.js +++ b/test/mocha/assertions/testBitstringStatusList.js @@ -65,7 +65,7 @@ function testStatusPurpose({ capabilityAgent, zcaps, suiteName, statusListOptions, depOptions }); }); - describe('/credentials/issue', () => { + describe('issue', () => { it('issues a valid credential w/ "credentialStatus"', async () => { const zcapClient = helpers.createZcapClient({capabilityAgent}); const credential = klona(mockCredential); @@ -230,7 +230,7 @@ function testStatusPurpose({ } }); }); - describe('/credentials/status', () => { + describe('status', () => { let zcapClient; let verifiableCredential; before(async () => { diff --git a/test/mocha/assertions/testIssueCrashRecovery.js b/test/mocha/assertions/testIssueCrashRecovery.js index bbe3a1a6..0ba17f67 100644 --- a/test/mocha/assertions/testIssueCrashRecovery.js +++ b/test/mocha/assertions/testIssueCrashRecovery.js @@ -112,80 +112,151 @@ export function testIssueCrashRecovery({ } }); - describe('/credential/issue crash recovery', () => { - // stub modules in order to simulate failure conditions - let credentialStatusWriterStub; - let mathRandomStub; - before(async () => { - // make Math.random always return 0 - // this will ensure that the same shard is selected every time - // see _chooseRandom helper in ListManager.js - mathRandomStub = sinon.stub(Math, 'random').callsFake(() => 0); - // make credentialStatusWriter.finish a noop - // making this a noop is simulating a failure where the status list - // bookkeeping was not completed after an issuance - credentialStatusWriterStub = sinon.stub( - _CredentialStatusWriter.prototype, 'finish').callsFake( - async () => {}); - }); - after(async () => { - mathRandomStub.restore(); - credentialStatusWriterStub.restore(); + // stub modules in order to simulate failure conditions + let credentialStatusWriterStub; + let mathRandomStub; + beforeEach(async () => { + // make Math.random always return 0 + // this will ensure that the same shard is selected every time + // see _chooseRandom helper in ListManager.js + mathRandomStub = sinon.stub(Math, 'random').callsFake(() => 0); + // make credentialStatusWriter.finish a noop + // making this a noop is simulating a failure where the status list + // bookkeeping was not completed after an issuance + credentialStatusWriterStub = sinon.stub( + _CredentialStatusWriter.prototype, 'finish').callsFake( + async () => {}); + }); + afterEach(async () => { + mathRandomStub.restore(); + credentialStatusWriterStub.restore(); + }); + + // FIXME: add a test that finishes one credential writer but not + // another, resulting in a duplicate being detected for one status + // but not another -- and a successful recovery from this condition + + it('successfully recovers from a simulated crash', async () => { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + + // first issue a VC that is partially completed enough to return the + // VC, however, the status list index bookkeeping is not updated + // The earlier failure is detected by the second issue of a VC and + // the bookkeeping is repaired + const credential1 = klona(mockCredential); + credential1.id = 'urn:id1'; + const {data: {verifiableCredential: vc1}} = await zcapClient.write({ + url: `${bslRevocation.issuerId}/credentials/issue`, + capability: bslRevocation.rootZcap, + json: {credential: credential1, options: issueOptions} }); - // FIXME: add a test that finishes one credential writer but not - // another, resulting in a duplicate being detected for one status - // but not another -- and a successful recovery from this condition + const vc1StatusId = vc1.credentialStatus.id; - it('successfully recovers from a simulated crash', async () => { - const zcapClient = helpers.createZcapClient({capabilityAgent}); + // now issue second VC (should succeed and process the + const credential2 = klona(mockCredential); + credential2.id = 'urn:id2'; + const {data: {verifiableCredential: vc2}} = await zcapClient.write({ + url: `${bslRevocation.issuerId}/credentials/issue`, + capability: bslRevocation.rootZcap, + json: {credential: credential2, options: issueOptions} + }); - // first issue a VC that is partially completed enough to return the - // VC, however, the status list index bookkeeping is not updated - // The earlier failure is detected by the second issue of a VC and - // the bookkeeping is repaired - const credential1 = klona(mockCredential); - credential1.id = 'urn:id1'; - const {data: {verifiableCredential: vc1}} = await zcapClient.write({ + const vc2StatusId = vc2.credentialStatus.id; + + // this test ensures that the two credentials are not issued with the + // same status list index / hash fragment + vc1StatusId.should.not.equal(vc2StatusId); + const vc1StatusHash = parseInt(vc1StatusId.split('#')[1]); + const vc2StatusHash = parseInt(vc2StatusId.split('#')[1]); + vc1StatusHash.should.not.equal(vc2StatusHash); + }); + // ensure duplicate VCs are still properly detected when bookkeeping + // fails + it('fails when trying to issue a duplicate credential', async () => { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + + // issue VC (should succeed) + let credential = klona(mockCredential); + let error; + let result; + try { + result = await zcapClient.write({ url: `${bslRevocation.issuerId}/credentials/issue`, capability: bslRevocation.rootZcap, - json: {credential: credential1, options: issueOptions} + json: {credential, options: issueOptions} }); - - const vc1StatusId = vc1.credentialStatus.id; - - // now issue second VC (should succeed and process the - const credential2 = klona(mockCredential); - credential2.id = 'urn:id2'; - const {data: {verifiableCredential: vc2}} = await zcapClient.write({ + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + const {proof} = verifiableCredential; + should.exist(proof); + + // issue VC with the same ID again (should fail) + credential = klona(mockCredential); + result = undefined; + try { + result = await zcapClient.write({ url: `${bslRevocation.issuerId}/credentials/issue`, capability: bslRevocation.rootZcap, - json: {credential: credential2, options: issueOptions} + json: {credential, options: issueOptions} }); + } catch(e) { + error = e; + } + should.exist(error); + error.data.type.should.equal('DuplicateError'); + }); - const vc2StatusId = vc2.credentialStatus.id; + it('fails to issue with a duplicate "credentialId"', async () => { + const zcapClient = helpers.createZcapClient({capabilityAgent}); - // this test ensures that the two credentials are not issued with the - // same status list index / hash fragment - vc1StatusId.should.not.equal(vc2StatusId); - const vc1StatusHash = parseInt(vc1StatusId.split('#')[1]); - const vc2StatusHash = parseInt(vc2StatusId.split('#')[1]); - vc1StatusHash.should.not.equal(vc2StatusHash); - }); - // ensure duplicate VCs are still properly detected when bookkeeping - // fails - it('fails when trying to issue a duplicate credential', async () => { - const zcapClient = helpers.createZcapClient({capabilityAgent}); + // issue VC without `id` and no `credentialId` option + // (should succeed) + { + const credential = klona(mockCredential); + delete credential.id; + let error; + let result; + try { + result = await zcapClient.write({ + url: `${bslRevocation.issuerId}/credentials/issue`, + capability: bslRevocation.rootZcap, + json: { + credential, + options: issueOptions + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + const {proof} = verifiableCredential; + should.exist(proof); + } - // issue VC (should succeed) - let credential = klona(mockCredential); + // issue VC with "credentialId" option only (should succeed) + const credentialId = `urn:uuid:${uuid()}`; + { + const credential = klona(mockCredential); + delete credential.id; let error; let result; try { result = await zcapClient.write({ url: `${bslRevocation.issuerId}/credentials/issue`, capability: bslRevocation.rootZcap, - json: {credential, options: issueOptions} + json: { + credential, + options: {...issueOptions, credentialId} + } }); } catch(e) { error = e; @@ -196,239 +267,166 @@ export function testIssueCrashRecovery({ const {verifiableCredential} = result.data; const {proof} = verifiableCredential; should.exist(proof); + } - // issue VC with the same ID again (should fail) - credential = klona(mockCredential); - result = undefined; + // issue VC with the same "credentialId" again (should fail) + { + const credential = klona(mockCredential); + delete credential.id; + let error; + let result; try { result = await zcapClient.write({ url: `${bslRevocation.issuerId}/credentials/issue`, capability: bslRevocation.rootZcap, - json: {credential, options: issueOptions} + json: { + credential, + options: {...issueOptions, credentialId} + } }); } catch(e) { error = e; } should.exist(error); error.data.type.should.equal('DuplicateError'); - }); - - it('fails to issue with a duplicate "credentialId"', async () => { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - - // issue VC without `id` and no `credentialId` option - // (should succeed) - { - const credential = klona(mockCredential); - delete credential.id; - let error; - let result; - try { - result = await zcapClient.write({ - url: `${bslRevocation.issuerId}/credentials/issue`, - capability: bslRevocation.rootZcap, - json: { - credential, - options: issueOptions - } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - const {proof} = verifiableCredential; - should.exist(proof); - } + should.not.exist(result); + } + }); - // issue VC with "credentialId" option only (should succeed) - const credentialId = `urn:uuid:${uuid()}`; - { - const credential = klona(mockCredential); - delete credential.id; - let error; - let result; - try { - result = await zcapClient.write({ - url: `${bslRevocation.issuerId}/credentials/issue`, - capability: bslRevocation.rootZcap, - json: { - credential, - options: {...issueOptions, credentialId} - } - }); - } catch(e) { - error = e; - } - assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - const {proof} = verifiableCredential; - should.exist(proof); - } + it('issues VCs with list rollover', async function() { + // two minutes to issue and rollover lists + this.timeout(1000 * 60 * 2); - // issue VC with the same "credentialId" again (should fail) - { - const credential = klona(mockCredential); - delete credential.id; - let error; - let result; - try { - result = await zcapClient.write({ - url: `${bslRevocation.issuerId}/credentials/issue`, - capability: bslRevocation.rootZcap, - json: { - credential, - options: {...issueOptions, credentialId} - } - }); - } catch(e) { - error = e; - } - should.exist(error); - error.data.type.should.equal('DuplicateError'); - should.not.exist(result); - } - }); + // list length is 8, do two rollovers + const listLength = 8; + for(let i = 0; i < (listLength * 2 + 1); ++i) { + // first issue VC + const credential = klona(mockCredential); + credential.id = `urn:uuid:${uuid()}`; + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {data: {verifiableCredential}} = await zcapClient.write({ + url: `${smallBsl.issuerId}/credentials/issue`, + capability: smallBsl.rootZcap, + json: {credential, options: issueOptions} + }); - it('issues VCs with list rollover', async function() { - // two minutes to issue and rollover lists - this.timeout(1000 * 60 * 2); - - // list length is 8, do two rollovers - const listLength = 8; - for(let i = 0; i < (listLength * 2 + 1); ++i) { - // first issue VC - const credential = klona(mockCredential); - credential.id = `urn:uuid:${uuid()}`; - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {data: {verifiableCredential}} = await zcapClient.write({ - url: `${smallBsl.issuerId}/credentials/issue`, - capability: smallBsl.rootZcap, - json: {credential, options: issueOptions} - }); + // get VC status + const statusInfo = await helpers.getCredentialStatus( + {verifiableCredential}); + let {status} = statusInfo; + status.should.equal(false); - // get VC status - const statusInfo = await helpers.getCredentialStatus( - {verifiableCredential}); - let {status} = statusInfo; - status.should.equal(false); - - // then revoke VC - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - smallBsl.issuerConfig; - await zcapClient.write({ - url: `${smallBsl.statusId}/credentials/status`, - capability: smallBsl.statusRootZcap, - json: { - credentialId: verifiableCredential.id, - indexAllocator, - credentialStatus: verifiableCredential.credentialStatus, - status: true - } - }); - } catch(e) { - error = e; - } - assertNoError(error); - - // force refresh of new SLC + // then revoke VC + let error; + try { + const {statusListOptions: [{indexAllocator}]} = + smallBsl.issuerConfig; await zcapClient.write({ - url: `${statusInfo.statusListCredential}?refresh=true`, + url: `${smallBsl.statusId}/credentials/status`, capability: smallBsl.statusRootZcap, - json: {} + json: { + credentialId: verifiableCredential.id, + indexAllocator, + credentialStatus: verifiableCredential.credentialStatus, + status: true + } }); - - // check status of VC has changed - ({status} = await helpers.getCredentialStatus( - {verifiableCredential})); - status.should.equal(true); + } catch(e) { + error = e; } - }); + assertNoError(error); - it('issues VCs with limited lists', async function() { - // two minutes to issue and rollover lists - this.timeout(1000 * 60 * 2); - - const statusPurpose = 'revocation'; - - // list length is 8, do two rollovers to hit list count capacity of 2 - const listLength = 8; - for(let i = 0; i < (listLength * 2 + 1); ++i) { - // first issue VC - const credential = klona(mockTerseCredential); - credential.id = `urn:uuid:${uuid()}`; - const zcapClient = helpers.createZcapClient({capabilityAgent}); - let verifiableCredential; - try { - ({data: {verifiableCredential}} = await zcapClient.write({ - url: `${smallTerseStatusList.issuerId}/credentials/issue`, - capability: smallTerseStatusList.rootZcap, - json: {credential, options: terseIssueOptions} - })); - } catch(e) { - // max list count reached, expected at `listLength * 2` only - if(e?.data?.name === 'QuotaExceededError') { - i.should.equal(listLength * 2); - return; - } - throw e; - } + // force refresh of new SLC + await zcapClient.write({ + url: `${statusInfo.statusListCredential}?refresh=true`, + capability: smallBsl.statusRootZcap, + json: {} + }); + + // check status of VC has changed + ({status} = await helpers.getCredentialStatus( + {verifiableCredential})); + status.should.equal(true); + } + }); + + it('issues VCs with limited lists', async function() { + // two minutes to issue and rollover lists + this.timeout(1000 * 60 * 2); + + const statusPurpose = 'revocation'; - // ensure TerseBitstringStatusEntry was added to VC - should.exist(verifiableCredential.credentialStatus); - verifiableCredential.credentialStatus.should.have.keys([ - 'type', 'terseStatusListBaseUrl', 'terseStatusListIndex' - ]); - verifiableCredential.credentialStatus.type.should.equal( - 'TerseBitstringStatusListEntry'); - verifiableCredential.credentialStatus.terseStatusListIndex - .should.be.a('number'); - - // get VC status - const statusInfo = await helpers.getCredentialStatus( - {verifiableCredential, listLength, statusPurpose}); - let {status} = statusInfo; - status.should.equal(false); - - // then revoke VC - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - smallTerseStatusList.issuerConfig; - await zcapClient.write({ - url: `${smallTerseStatusList.statusId}/credentials/status`, - capability: smallTerseStatusList.statusRootZcap, - json: { - credentialId: verifiableCredential.id, - indexAllocator, - credentialStatus: statusInfo.expandedCredentialStatus, - status: true - } - }); - } catch(e) { - error = e; + // list length is 8, do two rollovers to hit list count capacity of 2 + const listLength = 8; + for(let i = 0; i < (listLength * 2 + 1); ++i) { + // first issue VC + const credential = klona(mockTerseCredential); + credential.id = `urn:uuid:${uuid()}`; + const zcapClient = helpers.createZcapClient({capabilityAgent}); + let verifiableCredential; + try { + ({data: {verifiableCredential}} = await zcapClient.write({ + url: `${smallTerseStatusList.issuerId}/credentials/issue`, + capability: smallTerseStatusList.rootZcap, + json: {credential, options: terseIssueOptions} + })); + } catch(e) { + // max list count reached, expected at `listLength * 2` only + if(e?.data?.name === 'QuotaExceededError') { + i.should.equal(listLength * 2); + return; } - assertNoError(error); + throw e; + } - // force refresh of new SLC + // ensure TerseBitstringStatusEntry was added to VC + should.exist(verifiableCredential.credentialStatus); + verifiableCredential.credentialStatus.should.have.keys([ + 'type', 'terseStatusListBaseUrl', 'terseStatusListIndex' + ]); + verifiableCredential.credentialStatus.type.should.equal( + 'TerseBitstringStatusListEntry'); + verifiableCredential.credentialStatus.terseStatusListIndex + .should.be.a('number'); + + // get VC status + const statusInfo = await helpers.getCredentialStatus( + {verifiableCredential, listLength, statusPurpose}); + let {status} = statusInfo; + status.should.equal(false); + + // then revoke VC + let error; + try { + const {statusListOptions: [{indexAllocator}]} = + smallTerseStatusList.issuerConfig; await zcapClient.write({ - url: `${statusInfo.statusListCredential}?refresh=true`, + url: `${smallTerseStatusList.statusId}/credentials/status`, capability: smallTerseStatusList.statusRootZcap, - json: {} + json: { + credentialId: verifiableCredential.id, + indexAllocator, + credentialStatus: statusInfo.expandedCredentialStatus, + status: true + } }); - - // check status of VC has changed - ({status} = await helpers.getCredentialStatus( - {verifiableCredential, listLength, statusPurpose})); - status.should.equal(true); + } catch(e) { + error = e; } - }); + assertNoError(error); + + // force refresh of new SLC + await zcapClient.write({ + url: `${statusInfo.statusListCredential}?refresh=true`, + capability: smallTerseStatusList.statusRootZcap, + json: {} + }); + + // check status of VC has changed + ({status} = await helpers.getCredentialStatus( + {verifiableCredential, listLength, statusPurpose})); + status.should.equal(true); + } }); }); } diff --git a/test/mocha/assertions/testIssueSd.js b/test/mocha/assertions/testIssueSd.js index 76eb524f..6791d134 100644 --- a/test/mocha/assertions/testIssueSd.js +++ b/test/mocha/assertions/testIssueSd.js @@ -42,51 +42,48 @@ export function testIssueSd({suiteName, algorithm, issueOptions}) { url: noStatusListIssuerId }); }); - describe('/credentials/issue', () => { - it('issues a valid credential w/ "options.mandatoryPointers"', - async () => { - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {verifiableCredential} = await assertions.issueAndAssert({ - configId: noStatusListIssuerId, + it('issues a valid credential w/ "options.mandatoryPointers"', async () => { + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {verifiableCredential} = await assertions.issueAndAssert({ + configId: noStatusListIssuerId, + credential, + issueOptions: { + ...issueOptions, + mandatoryPointers: ['/issuer'] + }, + zcapClient, + capability: noStatusListIssuerRootZcap + }); + should.exist(verifiableCredential.id); + should.not.exist(verifiableCredential.credentialStatus); + }); + it('fails to issue a valid credential w/ invalid ' + + '"options.mandatoryPointers"', async () => { + let error; + const missingPointer = '/nonExistentPointer'; + try { + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + await zcapClient.write({ + url: `${noStatusListIssuerId}/credentials/issue`, + capability: noStatusListIssuerRootZcap, + json: { credential, - issueOptions: { + options: { ...issueOptions, - mandatoryPointers: ['/issuer'] - }, - zcapClient, - capability: noStatusListIssuerRootZcap - }); - should.exist(verifiableCredential.id); - should.not.exist(verifiableCredential.credentialStatus); - }); - it('fails to issue a valid credential w/ invalid ' + - '"options.mandatoryPointers"', async () => { - let error; - const missingPointer = '/nonExistentPointer'; - try { - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - await zcapClient.write({ - url: `${noStatusListIssuerId}/credentials/issue`, - capability: noStatusListIssuerRootZcap, - json: { - credential, - options: { - ...issueOptions, - mandatoryPointers: [missingPointer] - } + mandatoryPointers: [missingPointer] } - }); - } catch(e) { - error = e; - } - should.exist(error); - error.data.type.should.equal('DataError'); - error.status.should.equal(400); - error.data.message.should.equal( - `JSON pointer "${missingPointer}" does not match document.`); - }); + } + }); + } catch(e) { + error = e; + } + should.exist(error); + error.data.type.should.equal('DataError'); + error.status.should.equal(400); + error.data.message.should.equal( + `JSON pointer "${missingPointer}" does not match document.`); }); }); } diff --git a/test/mocha/assertions/testIssueXi.js b/test/mocha/assertions/testIssueXi.js index f434d1b2..b0c1b9d3 100644 --- a/test/mocha/assertions/testIssueXi.js +++ b/test/mocha/assertions/testIssueXi.js @@ -43,51 +43,48 @@ export function testIssueXi({suiteName, algorithm, issueOptions}) { url: noStatusListIssuerId }); }); - describe('/credentials/issue', () => { - it('issues a valid credential w/ "options.extraInformation"', - async () => { - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const extraInformationBytes = new Uint8Array([ - 12, 52, 75, 63, 74, 85, 21, 5, 62, 10 - ]); - const extraInformationEncoded = encode(extraInformationBytes); - const {verifiableCredential} = await assertions.issueAndAssert({ - configId: noStatusListIssuerId, + it('issues a valid credential w/ "options.extraInformation"', async () => { + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const extraInformationBytes = new Uint8Array([ + 12, 52, 75, 63, 74, 85, 21, 5, 62, 10 + ]); + const extraInformationEncoded = encode(extraInformationBytes); + const {verifiableCredential} = await assertions.issueAndAssert({ + configId: noStatusListIssuerId, + credential, + issueOptions: { + ...issueOptions, + extraInformation: extraInformationEncoded + }, + zcapClient, + capability: noStatusListIssuerRootZcap + }); + should.exist(verifiableCredential.id); + should.not.exist(verifiableCredential.credentialStatus); + }); + it('fails to issue a valid credential w/ invalid ' + + '"options.extraInformation"', async () => { + let error; + try { + const credential = klona(mockCredential); + const zcapClient = helpers.createZcapClient({capabilityAgent}); + await zcapClient.write({ + url: `${noStatusListIssuerId}/credentials/issue`, + capability: noStatusListIssuerRootZcap, + json: { credential, - issueOptions: { + options: { ...issueOptions, - extraInformation: extraInformationEncoded - }, - zcapClient, - capability: noStatusListIssuerRootZcap - }); - should.exist(verifiableCredential.id); - should.not.exist(verifiableCredential.credentialStatus); - }); - it('fails to issue a valid credential w/ invalid ' + - '"options.extraInformation"', async () => { - let error; - try { - const credential = klona(mockCredential); - const zcapClient = helpers.createZcapClient({capabilityAgent}); - await zcapClient.write({ - url: `${noStatusListIssuerId}/credentials/issue`, - capability: noStatusListIssuerRootZcap, - json: { - credential, - options: { - ...issueOptions, - extraInformation: ['notAString'] - } + extraInformation: ['notAString'] } - }); - } catch(e) { - error = e; - } - should.exist(error); - error.data.name.should.equal('ValidationError'); - }); + } + }); + } catch(e) { + error = e; + } + should.exist(error); + error.data.name.should.equal('ValidationError'); }); }); } diff --git a/test/mocha/assertions/testStatusScaling.js b/test/mocha/assertions/testStatusScaling.js index 298abe53..6a1fd39e 100644 --- a/test/mocha/assertions/testStatusScaling.js +++ b/test/mocha/assertions/testStatusScaling.js @@ -33,7 +33,7 @@ export function testStatusScaling({ let zcaps; let smallBsl; let smallTerseStatusList; - beforeEach(async () => { + before(async () => { // provision dependencies ({capabilityAgent, zcaps} = await helpers.provisionDependencies( depOptions)); @@ -92,142 +92,140 @@ export function testStatusScaling({ } }); - describe('status scaling /credentials/issue', () => { - it('issues VCs with list rollover', async function() { - // two minutes to issue and rollover lists - this.timeout(1000 * 60 * 2); - - // list length is 8, do two rollovers - const listLength = 8; - for(let i = 0; i < (listLength * 2 + 1); ++i) { - // first issue VC - const credential = klona(mockCredential); - credential.id = `urn:uuid:${uuid()}`; - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {data: {verifiableCredential}} = await zcapClient.write({ - url: `${smallBsl.issuerId}/credentials/issue`, - capability: smallBsl.rootZcap, - json: {credential, options: issueOptions} - }); - - // get VC status - const statusInfo = await helpers.getCredentialStatus( - {verifiableCredential}); - let {status} = statusInfo; - status.should.equal(false); - - // then revoke VC - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - smallBsl.issuerConfig; - await zcapClient.write({ - url: `${smallBsl.statusId}/credentials/status`, - capability: smallBsl.statusRootZcap, - json: { - credentialId: verifiableCredential.id, - indexAllocator, - credentialStatus: verifiableCredential.credentialStatus, - status: true - } - }); - } catch(e) { - error = e; - } - assertNoError(error); + it('issues VCs with list rollover', async function() { + // two minutes to issue and rollover lists + this.timeout(1000 * 60 * 2); + + // list length is 8, do two rollovers + const listLength = 8; + for(let i = 0; i < (listLength * 2 + 1); ++i) { + // first issue VC + const credential = klona(mockCredential); + credential.id = `urn:uuid:${uuid()}`; + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {data: {verifiableCredential}} = await zcapClient.write({ + url: `${smallBsl.issuerId}/credentials/issue`, + capability: smallBsl.rootZcap, + json: {credential, options: issueOptions} + }); - // force refresh of new SLC + // get VC status + const statusInfo = await helpers.getCredentialStatus( + {verifiableCredential}); + let {status} = statusInfo; + status.should.equal(false); + + // then revoke VC + let error; + try { + const {statusListOptions: [{indexAllocator}]} = + smallBsl.issuerConfig; await zcapClient.write({ - url: `${statusInfo.statusListCredential}?refresh=true`, + url: `${smallBsl.statusId}/credentials/status`, capability: smallBsl.statusRootZcap, - json: {} + json: { + credentialId: verifiableCredential.id, + indexAllocator, + credentialStatus: verifiableCredential.credentialStatus, + status: true + } }); - - // check status of VC has changed - ({status} = await helpers.getCredentialStatus( - {verifiableCredential})); - status.should.equal(true); + } catch(e) { + error = e; } - }); - - it('issues VCs with limited lists', async function() { - // two minutes to issue and rollover lists - this.timeout(1000 * 60 * 2); - - const statusPurpose = 'revocation'; - - // list length is 8, do two rollovers to hit list count capacity of 2 - const listLength = 8; - for(let i = 0; i < (listLength * 2 + 1); ++i) { - // first issue VC - const credential = klona(mockTerseCredential); - credential.id = `urn:uuid:${uuid()}`; - const zcapClient = helpers.createZcapClient({capabilityAgent}); - let verifiableCredential; - try { - ({data: {verifiableCredential}} = await zcapClient.write({ - url: `${smallTerseStatusList.issuerId}/credentials/issue`, - capability: smallTerseStatusList.rootZcap, - json: {credential, options: terseIssueOptions} - })); - } catch(e) { - // max list count reached, expected at `listLength * 2` only - if(e?.data?.name === 'QuotaExceededError') { - i.should.equal(listLength * 2); - return; - } - throw e; - } + assertNoError(error); + + // force refresh of new SLC + await zcapClient.write({ + url: `${statusInfo.statusListCredential}?refresh=true`, + capability: smallBsl.statusRootZcap, + json: {} + }); - // ensure TerseBitstringStatusEntry was added to VC - should.exist(verifiableCredential.credentialStatus); - verifiableCredential.credentialStatus.should.have.keys([ - 'type', 'terseStatusListBaseUrl', 'terseStatusListIndex' - ]); - verifiableCredential.credentialStatus.type.should.equal( - 'TerseBitstringStatusListEntry'); - verifiableCredential.credentialStatus.terseStatusListIndex - .should.be.a('number'); - - // get VC status - const statusInfo = await helpers.getCredentialStatus( - {verifiableCredential, statusPurpose, listLength}); - let {status} = statusInfo; - status.should.equal(false); - - // then revoke VC - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - smallTerseStatusList.issuerConfig; - await zcapClient.write({ - url: `${smallTerseStatusList.statusId}/credentials/status`, - capability: smallTerseStatusList.statusRootZcap, - json: { - credentialId: verifiableCredential.id, - indexAllocator, - credentialStatus: statusInfo.expandedCredentialStatus, - status: true - } - }); - } catch(e) { - error = e; + // check status of VC has changed + ({status} = await helpers.getCredentialStatus( + {verifiableCredential})); + status.should.equal(true); + } + }); + + it('issues VCs with limited lists', async function() { + // two minutes to issue and rollover lists + this.timeout(1000 * 60 * 2); + + const statusPurpose = 'revocation'; + + // list length is 8, do two rollovers to hit list count capacity of 2 + const listLength = 8; + for(let i = 0; i < (listLength * 2 + 1); ++i) { + // first issue VC + const credential = klona(mockTerseCredential); + credential.id = `urn:uuid:${uuid()}`; + const zcapClient = helpers.createZcapClient({capabilityAgent}); + let verifiableCredential; + try { + ({data: {verifiableCredential}} = await zcapClient.write({ + url: `${smallTerseStatusList.issuerId}/credentials/issue`, + capability: smallTerseStatusList.rootZcap, + json: {credential, options: terseIssueOptions} + })); + } catch(e) { + // max list count reached, expected at `listLength * 2` only + if(e?.data?.name === 'QuotaExceededError') { + i.should.equal(listLength * 2); + return; } - assertNoError(error); + throw e; + } - // force refresh of new SLC + // ensure TerseBitstringStatusEntry was added to VC + should.exist(verifiableCredential.credentialStatus); + verifiableCredential.credentialStatus.should.have.keys([ + 'type', 'terseStatusListBaseUrl', 'terseStatusListIndex' + ]); + verifiableCredential.credentialStatus.type.should.equal( + 'TerseBitstringStatusListEntry'); + verifiableCredential.credentialStatus.terseStatusListIndex + .should.be.a('number'); + + // get VC status + const statusInfo = await helpers.getCredentialStatus( + {verifiableCredential, statusPurpose, listLength}); + let {status} = statusInfo; + status.should.equal(false); + + // then revoke VC + let error; + try { + const {statusListOptions: [{indexAllocator}]} = + smallTerseStatusList.issuerConfig; await zcapClient.write({ - url: `${statusInfo.statusListCredential}?refresh=true`, + url: `${smallTerseStatusList.statusId}/credentials/status`, capability: smallTerseStatusList.statusRootZcap, - json: {} + json: { + credentialId: verifiableCredential.id, + indexAllocator, + credentialStatus: statusInfo.expandedCredentialStatus, + status: true + } }); - - // check status of VC has changed - ({status} = await helpers.getCredentialStatus( - {verifiableCredential, statusPurpose, listLength})); - status.should.equal(true); + } catch(e) { + error = e; } - }); + assertNoError(error); + + // force refresh of new SLC + await zcapClient.write({ + url: `${statusInfo.statusListCredential}?refresh=true`, + capability: smallTerseStatusList.statusRootZcap, + json: {} + }); + + // check status of VC has changed + ({status} = await helpers.getCredentialStatus( + {verifiableCredential, statusPurpose, listLength})); + status.should.equal(true); + } }); }); } diff --git a/test/mocha/assertions/testTerseBitstringStatusList.js b/test/mocha/assertions/testTerseBitstringStatusList.js index d3bb0308..8f9b76b0 100644 --- a/test/mocha/assertions/testTerseBitstringStatusList.js +++ b/test/mocha/assertions/testTerseBitstringStatusList.js @@ -67,136 +67,131 @@ export function testTerseBitstringStatusList({ }); } }); - describe('/credentials/issue', () => { - it('issues a valid credential w/ terse "credentialStatus" for ' + - 'both revocation and suspension status purpose', async () => { - const credential = klona(mockTerseCredential); + it('issues a valid credential w/ terse "credentialStatus" for ' + + 'both revocation and suspension status purpose', async () => { + const credential = klona(mockTerseCredential); + let error; + let result; + try { + const zcapClient = helpers.createZcapClient({capabilityAgent}); + result = await zcapClient.write({ + url: `${terseMultistatus.issuerId}/credentials/issue`, + capability: terseMultistatus.rootZcap, + json: { + credential, + options: terseIssueOptions + } + }); + } catch(e) { + error = e; + } + assertNoError(error); + should.exist(result.data); + should.exist(result.data.verifiableCredential); + const {verifiableCredential} = result.data; + verifiableCredential.should.be.an('object'); + should.exist(verifiableCredential['@context']); + should.exist(verifiableCredential.type); + should.exist(verifiableCredential.credentialStatus); + verifiableCredential.credentialStatus.should.have.keys([ + 'type', 'terseStatusListBaseUrl', 'terseStatusListIndex' + ]); + verifiableCredential.credentialStatus.type.should.equal( + 'TerseBitstringStatusListEntry'); + verifiableCredential.credentialStatus.terseStatusListIndex + .should.be.a('number'); + should.exist(verifiableCredential.proof); + verifiableCredential.proof.should.be.an('object'); + }); + + it('updates multiple TerseBitstringStatusList statuses', async () => { + // first issue VC + const credential = klona(mockTerseCredential); + const credentialId = `urn:uuid:${uuid()}`; + const zcapClient = helpers.createZcapClient({capabilityAgent}); + const {data: {verifiableCredential}} = await zcapClient.write({ + url: `${terseMultistatus.issuerId}/credentials/issue`, + capability: terseMultistatus.rootZcap, + json: { + credential, + options: {...terseIssueOptions, credentialId} + } + }); + + // get VC statuses + const listLength = 131072; + const revocationStatusInfo = await helpers.getCredentialStatus( + {verifiableCredential, statusPurpose: 'revocation', listLength}); + revocationStatusInfo.status.should.equal(false); + const suspensionStatusInfo = await helpers.getCredentialStatus( + {verifiableCredential, statusPurpose: 'suspension', listLength}); + suspensionStatusInfo.status.should.equal(false); + + // then revoke VC + { let error; - let result; try { - const zcapClient = helpers.createZcapClient({capabilityAgent}); - result = await zcapClient.write({ - url: `${terseMultistatus.issuerId}/credentials/issue`, - capability: terseMultistatus.rootZcap, + const {statusListOptions: [{indexAllocator}]} = + terseMultistatus.issuerConfig; + await zcapClient.write({ + url: `${terseMultistatus.statusId}/credentials/status`, + capability: terseMultistatus.statusRootZcap, json: { - credential, - options: terseIssueOptions + credentialId, + indexAllocator, + credentialStatus: revocationStatusInfo + .expandedCredentialStatus, + status: true } }); } catch(e) { error = e; } assertNoError(error); - should.exist(result.data); - should.exist(result.data.verifiableCredential); - const {verifiableCredential} = result.data; - verifiableCredential.should.be.an('object'); - should.exist(verifiableCredential['@context']); - should.exist(verifiableCredential.type); - should.exist(verifiableCredential.credentialStatus); - verifiableCredential.credentialStatus.should.have.keys([ - 'type', 'terseStatusListBaseUrl', 'terseStatusListIndex' - ]); - verifiableCredential.credentialStatus.type.should.equal( - 'TerseBitstringStatusListEntry'); - verifiableCredential.credentialStatus.terseStatusListIndex - .should.be.a('number'); - should.exist(verifiableCredential.proof); - verifiableCredential.proof.should.be.an('object'); - }); - }); + } - describe('/credentials/status', () => { - it('updates multiple TerseBitstringStatusList statuses', - async () => { - // first issue VC - const credential = klona(mockTerseCredential); - const credentialId = `urn:uuid:${uuid()}`; - const zcapClient = helpers.createZcapClient({capabilityAgent}); - const {data: {verifiableCredential}} = await zcapClient.write({ - url: `${terseMultistatus.issuerId}/credentials/issue`, - capability: terseMultistatus.rootZcap, + // then suspend VC + { + let error; + try { + const {statusListOptions: [{indexAllocator}]} = + terseMultistatus.issuerConfig; + await zcapClient.write({ + url: `${terseMultistatus.statusId}/credentials/status`, + capability: terseMultistatus.statusRootZcap, json: { - credential, - options: {...terseIssueOptions, credentialId} + credentialId, + indexAllocator, + credentialStatus: suspensionStatusInfo + .expandedCredentialStatus, + status: true } }); + } catch(e) { + error = e; + } + assertNoError(error); + } - // get VC statuses - const listLength = 131072; - const revocationStatusInfo = await helpers.getCredentialStatus( - {verifiableCredential, statusPurpose: 'revocation', listLength}); - revocationStatusInfo.status.should.equal(false); - const suspensionStatusInfo = await helpers.getCredentialStatus( - {verifiableCredential, statusPurpose: 'suspension', listLength}); - suspensionStatusInfo.status.should.equal(false); - - // then revoke VC - { - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - terseMultistatus.issuerConfig; - await zcapClient.write({ - url: `${terseMultistatus.statusId}/credentials/status`, - capability: terseMultistatus.statusRootZcap, - json: { - credentialId, - indexAllocator, - credentialStatus: revocationStatusInfo - .expandedCredentialStatus, - status: true - } - }); - } catch(e) { - error = e; - } - assertNoError(error); - } - - // then suspend VC - { - let error; - try { - const {statusListOptions: [{indexAllocator}]} = - terseMultistatus.issuerConfig; - await zcapClient.write({ - url: `${terseMultistatus.statusId}/credentials/status`, - capability: terseMultistatus.statusRootZcap, - json: { - credentialId, - indexAllocator, - credentialStatus: suspensionStatusInfo - .expandedCredentialStatus, - status: true - } - }); - } catch(e) { - error = e; - } - assertNoError(error); - } - - // force refresh of new SLCs - await zcapClient.write({ - url: `${revocationStatusInfo.statusListCredential}?refresh=true`, - capability: terseMultistatus.statusRootZcap, - json: {} - }); - await zcapClient.write({ - url: `${suspensionStatusInfo.statusListCredential}?refresh=true`, - capability: terseMultistatus.statusRootZcap, - json: {} - }); + // force refresh of new SLCs + await zcapClient.write({ + url: `${revocationStatusInfo.statusListCredential}?refresh=true`, + capability: terseMultistatus.statusRootZcap, + json: {} + }); + await zcapClient.write({ + url: `${suspensionStatusInfo.statusListCredential}?refresh=true`, + capability: terseMultistatus.statusRootZcap, + json: {} + }); - // check statuses of VC have changed - const newRevocationStatus = await helpers.getCredentialStatus( - {verifiableCredential, statusPurpose: 'revocation', listLength}); - newRevocationStatus.status.should.equal(true); - const newSuspensionStatus = await helpers.getCredentialStatus( - {verifiableCredential, statusPurpose: 'suspension', listLength}); - newSuspensionStatus.status.should.equal(true); - }); + // check statuses of VC have changed + const newRevocationStatus = await helpers.getCredentialStatus( + {verifiableCredential, statusPurpose: 'revocation', listLength}); + newRevocationStatus.status.should.equal(true); + const newSuspensionStatus = await helpers.getCredentialStatus( + {verifiableCredential, statusPurpose: 'suspension', listLength}); + newSuspensionStatus.status.should.equal(true); }); }); }