ula-vc-data-management
Version:
A ULA data management plugin for W3C Verifiable Credentials
286 lines (256 loc) • 13.8 kB
text/typescript
/*
* Copyright 2020 Coöperatieve Rabobank U.A.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as chai from 'chai'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import * as chaiAsPromised from 'chai-as-promised'
import { VerifiableCredentialRepository } from '../../../src'
import { DataStorageMock } from '../../mock/data-storage-mock'
import { CredentialStatus, IProof, IVerifiableCredential, VerifiableCredential } from 'vp-toolkit-models'
const testProof: IProof = {
type: 'Secp256k1Signature2019',
created: new Date('01-01-2019 12:34:00'),
verificationMethod: 'pubkey',
nonce: '71512644-2f4f-4bb9-9c9d-9f5e1fc9a1d2'
}
const testCredParams: IVerifiableCredential = {
id: 'did:protocol:address',
type: ['VerifiableCredential'],
issuer: 'did:protocol:issueraddress',
issuanceDate: new Date('01-01-2019 12:00:00'),
credentialSubject: {
id: 'did:protocol:holderaddress',
'https://schema.org/givenName': 'John'
},
proof: testProof,
credentialStatus: new CredentialStatus({
id: '0x6AbAAFB672f60C16C604A29426aDA1Af9d96d440',
type: 'vcStatusRegistry2019'
}),
'@context': ['https://schema.org/givenName']
}
before(() => {
chai.should()
chai.use(sinonChai)
chai.use(chaiAsPromised)
})
describe('Verifiable Credential repository', function () {
const dataStorage = new DataStorageMock()
const vcRepoDataKey = 'verifiable_credential'
const sut = new VerifiableCredentialRepository(dataStorage)
afterEach(() => {
sinon.restore()
})
it('should return the correct items for findByContext', () => {
// Arranging and fake data to be returned from stub
const firstCredential = new VerifiableCredential(testCredParams)
const secondCredentialParams = Object.assign({}, testCredParams)
secondCredentialParams.proof = Object.assign({}, testProof) // To prevent TS2532 error
secondCredentialParams.proof.nonce = '4ccbcbc5-84aa-4d6a-a80b-7bb42dd7e5dc'
secondCredentialParams['@context'] = ['https://schema.org/familyName', 'https://schema.org/address']
const secondCredential = new VerifiableCredential(secondCredentialParams)
const thirdCredentialParams = Object.assign({}, testCredParams)
thirdCredentialParams.proof = Object.assign({}, testProof) // To prevent TS2532 error
thirdCredentialParams.proof.nonce = '19167bb6-879a-4a50-8d4e-84df59cf823e'
thirdCredentialParams['@context'] = undefined
const thirdCredential = new VerifiableCredential(thirdCredentialParams)
// Arranging stub & calls
const dataStubGet = sinon.stub(dataStorage, 'get')
.onCall(0).resolves([
testProof.nonce,
secondCredential.proof.nonce,
thirdCredential.proof.nonce
])
.onCall(1).resolves(firstCredential.toJSON())
.onCall(2).resolves(secondCredential.toJSON())
.onCall(3).resolves(thirdCredential.toJSON())
// Act
return sut.findByContext(/schema\.org\/givenname/i).then((value) => {
// Assert
const expectedFirstCredential = new VerifiableCredential(firstCredential.toJSON() as IVerifiableCredential)
return value.should.deep.equal([expectedFirstCredential])
&& dataStubGet.getCalls().length.should.be.equal(4)
&& dataStubGet.getCalls()[0].should.have.been.calledWithExactly(vcRepoDataKey)
&& dataStubGet.getCalls()[1].should.have.been.calledWithExactly(testProof.nonce)
&& dataStubGet.getCalls()[2].should.have.been.calledWithExactly(secondCredential.proof.nonce)
&& dataStubGet.getCalls()[3].should.have.been.calledWithExactly(thirdCredential.proof.nonce)
})
})
it('should return the correct items for findByCredentialSubject', () => {
// Arranging and fake data to be returned from stub
const firstCredential = new VerifiableCredential(testCredParams)
const secondCredentialParams = Object.assign({}, testCredParams)
secondCredentialParams.proof = Object.assign({}, testProof) // To prevent TS2532 error
secondCredentialParams.proof.nonce = '4ccbcbc5-84aa-4d6a-a80b-7bb42dd7e5dc'
secondCredentialParams.credentialSubject = { 'https://schema.org/familyName': '', 'https://schema.org/address': '' }
const secondCredential = new VerifiableCredential(secondCredentialParams)
const thirdCredentialParams = Object.assign({}, testCredParams)
thirdCredentialParams.proof = Object.assign({}, testProof) // To prevent TS2532 error
thirdCredentialParams.proof.nonce = '19167bb6-879a-4a50-8d4e-84df59cf823e'
thirdCredentialParams.credentialSubject = {}
const thirdCredential = new VerifiableCredential(thirdCredentialParams)
// Arranging stub & calls
const dataStubGet = sinon.stub(dataStorage, 'get')
.onCall(0).resolves([
testProof.nonce,
secondCredential.proof.nonce,
thirdCredential.proof.nonce
])
.onCall(1).resolves(firstCredential.toJSON())
.onCall(2).resolves(secondCredential.toJSON())
.onCall(3).resolves(thirdCredential.toJSON())
// Act
return sut.findByCredentialSubject(/schema\.org\/givenname/i).then((value) => {
// Assert
const expectedFirstCredential = new VerifiableCredential(firstCredential.toJSON() as IVerifiableCredential)
return value.should.deep.equal([expectedFirstCredential])
&& dataStubGet.getCalls().length.should.be.equal(4)
&& dataStubGet.getCalls()[0].should.have.been.calledWithExactly(vcRepoDataKey)
&& dataStubGet.getCalls()[1].should.have.been.calledWithExactly(testProof.nonce)
&& dataStubGet.getCalls()[2].should.have.been.calledWithExactly(secondCredential.proof.nonce)
&& dataStubGet.getCalls()[3].should.have.been.calledWithExactly(thirdCredential.proof.nonce)
})
})
it('should return the correct items for findByIssuer', () => {
// Arranging and fake data to be returned from stub
const firstCredential = new VerifiableCredential(testCredParams)
const secondCredentialParams = Object.assign({}, testCredParams)
secondCredentialParams.proof = Object.assign({}, testProof) // To prevent TS2532 error
secondCredentialParams.proof.nonce = '4ccbcbc5-84aa-4d6a-a80b-7bb42dd7e5dc'
secondCredentialParams.issuer = 'issuerTwo'
const secondCredential = new VerifiableCredential(secondCredentialParams)
const thirdCredentialParams = Object.assign({}, testCredParams)
thirdCredentialParams.proof = Object.assign({}, testProof) // To prevent TS2532 error
thirdCredentialParams.proof.nonce = '19167bb6-879a-4a50-8d4e-84df59cf823e'
secondCredentialParams.issuer = 'issuerThree'
const thirdCredential = new VerifiableCredential(thirdCredentialParams)
// Arranging stub & calls
const dataStubGet = sinon.stub(dataStorage, 'get')
.onCall(0).resolves([
testProof.nonce,
secondCredential.proof.nonce,
thirdCredential.proof.nonce
])
.onCall(1).resolves(firstCredential.toJSON())
.onCall(2).resolves(secondCredential.toJSON())
.onCall(3).resolves(thirdCredential.toJSON())
// Act
return sut.findByIssuer('issuerTwo').then((value) => {
// Assert
const expectedSecondCredential = new VerifiableCredential(secondCredential.toJSON() as IVerifiableCredential)
return value.should.deep.equal([expectedSecondCredential])
&& dataStubGet.getCalls().length.should.be.equal(4)
&& dataStubGet.getCalls()[0].should.have.been.calledWithExactly(vcRepoDataKey)
&& dataStubGet.getCalls()[1].should.have.been.calledWithExactly(testProof.nonce)
&& dataStubGet.getCalls()[2].should.have.been.calledWithExactly(secondCredential.proof.nonce)
&& dataStubGet.getCalls()[3].should.have.been.calledWithExactly(thirdCredential.proof.nonce)
})
})
it('should return the correct array for findAll', () => {
// Arranging and fake data to be returned from stub
const firstCredential = new VerifiableCredential(testCredParams)
const secondCredentialParams = Object.assign({}, testCredParams)
secondCredentialParams.proof = Object.assign({}, testProof) // To prevent TS2532 error
secondCredentialParams.proof.nonce = '4ccbcbc5-84aa-4d6a-a80b-7bb42dd7e5dc'
const secondCredential = new VerifiableCredential(secondCredentialParams)
const dataResult = [firstCredential, secondCredential]
// Arranging stub & calls
const dataStubGet = sinon.stub(dataStorage, 'get')
.onFirstCall().resolves([testProof.nonce, secondCredential.proof.nonce])
.onSecondCall().resolves(dataResult[0].toJSON())
.onThirdCall().resolves(dataResult[1].toJSON())
// Act
return sut.findAll().then((value) => {
// Assert
const expectedDataResult = dataResult.map(cred => {
return new VerifiableCredential(cred.toJSON() as IVerifiableCredential)
})
return value.should.deep.equal(expectedDataResult)
&& dataStubGet.getCalls().length.should.be.equal(3)
&& dataStubGet.getCalls()[0].should.have.been.calledWithExactly(vcRepoDataKey)
&& dataStubGet.getCalls()[1].should.have.been.calledWithExactly(testProof.nonce)
&& dataStubGet.getCalls()[2].should.have.been.calledWithExactly(secondCredential.proof.nonce)
})
})
it('should return an empty array for findAll when storage is not initialized', () => {
const dataStubGet = sinon.stub(dataStorage, 'get').resolves(undefined) // Undefined storage
return sut.findAll().then((value) => {
return value.should.deep.equal([])
&& dataStubGet.should.have.been.calledOnceWithExactly(vcRepoDataKey)
})
})
it('should throw when trying to find a single VC by a non-existing uuid', () => {
const dataStubGet = sinon.stub(dataStorage, 'get').resolves(undefined) // Unknown item
const nonce = '177c9547-1db5-4188-b98c-fdac93bd51ca'
const findAction = sut.findOneByNonce(nonce)
dataStubGet.should.have.been.calledOnceWithExactly(nonce)
return findAction.should.have.been.rejectedWith('No verifiable credential found')
})
it('should overwrite one verifiable credential properly', () => {
const vc = new VerifiableCredential(testCredParams)
const dataStubSet = sinon.stub(dataStorage, 'set')
const dataStubGet = sinon.stub(dataStorage, 'get').resolves([testProof.nonce])
return sut.saveOne(vc).then(() => {
return dataStubGet.should.have.been.calledOnceWithExactly(vcRepoDataKey)
&& dataStubSet.should.have.been.calledOnceWithExactly(testProof.nonce, vc.toJSON())
})
})
it('should save one verifiable credential properly', () => {
const vc = new VerifiableCredential(testCredParams)
const dataStubSet = sinon.stub(dataStorage, 'set')
const dataStubGet = sinon.stub(dataStorage, 'get').resolves([])
return sut.saveOne(vc).then(() => {
return dataStubGet.should.have.been.calledOnceWithExactly(vcRepoDataKey)
&& dataStubSet.getCalls().length.should.be.equal(2)
&& dataStubSet.getCalls()[0].should.have.been.calledWithExactly(testProof.nonce, vc.toJSON())
&& dataStubSet.getCalls()[1].should.have.been.calledWithExactly(vcRepoDataKey, [testProof.nonce])
})
})
it('should clear all verifiable credentials properly', () => {
const nonce1 = '4f73efa2-8849-4cd4-9b2c-7e2796c8f08f'
const nonce2 = 'b2577f61-b358-44d0-a1c2-951d7d5f7f03'
const dataStubGet = sinon.stub(dataStorage, 'get').resolves([nonce1, nonce2])
const dataStubRemove = sinon.stub(dataStorage, 'remove')
return sut.clearAll().then(() => {
sinon.assert.calledWithExactly(dataStubRemove.getCall(0), nonce1)
sinon.assert.calledWithExactly(dataStubRemove.getCall(1), nonce2)
sinon.assert.calledWithExactly(dataStubRemove.getCall(2), vcRepoDataKey)
return dataStubGet.should.have.been.calledOnceWithExactly(vcRepoDataKey)
&& dataStubRemove.callCount.should.be.equal(3)
})
})
it('should save multiple verifiable credential properly', () => {
const credsToSave = [new VerifiableCredential(testCredParams), new VerifiableCredential(testCredParams)]
const dataStubSet = sinon.stub(dataStorage, 'set')
const dataStubGet = sinon.stub(dataStorage, 'get')
.onFirstCall().resolves([])
.onSecondCall().resolves([])
return sut.saveMultiple(credsToSave).then(() => {
return dataStubGet.should.have.been.calledWithExactly(vcRepoDataKey)
&& dataStubGet.getCalls().length.should.be.equal(2)
&& dataStubSet.getCalls().length.should.be.equal(4)
&& dataStubSet.getCalls()[0].should.have.been.calledWithExactly(testProof.nonce, credsToSave[0].toJSON())
&& dataStubSet.getCalls()[1].should.have.been.calledWithExactly(vcRepoDataKey, [testProof.nonce])
&& dataStubSet.getCalls()[2].should.have.been.calledWithExactly(testProof.nonce, credsToSave[1].toJSON())
&& dataStubSet.getCalls()[1].should.have.been.calledWithExactly(vcRepoDataKey, [testProof.nonce])
})
})
it('should throw when saving a VC without a proof', () => {
let incorrectVc = JSON.parse(JSON.stringify(new VerifiableCredential(testCredParams)))
incorrectVc.proof = undefined
return sut.saveOne(incorrectVc).should.be.rejectedWith(Error, 'Verifiable credential does not contain a proof')
})
})