Skip to content

Commit

Permalink
adding unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nitro-neal committed Nov 2, 2023
1 parent c20e430 commit e4f2ee2
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 59 deletions.
57 changes: 0 additions & 57 deletions packages/credentials/src/verifiable-credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,63 +198,6 @@ export class VerifiableCredential {
throw new Error('Signature verification failed: Expected JWS header to contain alg and kid');
}

const parsedDidUrl = DidDocument.getDidFromKeyId(jwt.header.kid);
const fragment = jwt.header.kid.split('#')[1];

if(!fragment || ! parsedDidUrl) {
throw new Error('Signature verification failed: Expected kid in JWS header to be a DID URL');
}

const didResolutionResult: DIDResolutionResult = await tbdResolver.resolve(parsedDidUrl);
if (didResolutionResult.didResolutionMetadata.error) {
throw new Error(
`Signature verification failed: Failed to resolve DID ${parsedDidUrl}. ` +
`Error: ${didResolutionResult.didResolutionMetadata.error}`
);
}

const verificationMethodIds = new Set([`${parsedDidUrl}#${fragment}`, `#${fragment}`]);

if (!didResolutionResult.didDocument?.assertionMethod || !didResolutionResult.didDocument?.verificationMethod) {
throw new Error(
'Signature verification failed: Expected kid in JWS header to dereference ' +
'a DID Document Verification Method with an Assertion verification relationship'
);
}

const assertionMethods = didResolutionResult.didDocument?.assertionMethod;

let assertionMethod: VerificationMethod | undefined;

for (const element of assertionMethods) {
if (typeof element === 'string') {
if (verificationMethodIds.has(element)) {
assertionMethod = didResolutionResult.didDocument?.verificationMethod.find(vm => vm.id === element);
break;
}
} else {
if (verificationMethodIds.has(element.id)) {
assertionMethod = didResolutionResult.didDocument?.verificationMethod.find(vm => vm.id === element.id);
break;
}
}
}

if (!assertionMethod) {
throw new Error(
'Signature verification failed: Expected kid in JWS header to dereference ' +
'a DID Document Verification Method with an Assertion verification relationship'
);
}

if (assertionMethod.type !== 'JsonWebKey2020' || !assertionMethod.publicKeyJwk) {
throw new Error(
'Signature verification failed: Expected kid in JWS header to dereference ' +
'a DID Document Verification Method of type JsonWebKey2020 with a publicKeyJwk'
);
}

// Perform the signature verification
const verificationResponse = await verifyJWT(vcJwt, {
resolver: tbdResolver
});
Expand Down
66 changes: 64 additions & 2 deletions packages/credentials/tests/presentation-exchange.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ class BitcoinCredential {
) {}
}

class OtherCredential {
constructor(
public otherthing: string
) {}
}

describe('PresentationExchange', () => {
describe('Full Presentation Exchange', () => {
let signOptions: SignOptions;
Expand Down Expand Up @@ -45,8 +51,27 @@ describe('PresentationExchange', () => {
PresentationExchange.satisfiesPresentationDefinition([btcCredentialJwt], presentationDefinition);
});

it('should return the selected verifiable credentials', () => {
const actualSelectedVcJwts = PresentationExchange.selectCredentials([btcCredentialJwt], presentationDefinition);
expect(actualSelectedVcJwts).to.deep.equal([btcCredentialJwt]);
});

it('should return the only one verifiable credential', async () => {
const vc = VerifiableCredential.create(
'StreetCred',
signOptions.issuerDid,
signOptions.subjectDid,
new OtherCredential('otherstuff'),
);

const otherCredJwt = await vc.sign(signOptions);

const actualSelectedVcJwts = PresentationExchange.selectCredentials([btcCredentialJwt, otherCredJwt], presentationDefinition);
expect(actualSelectedVcJwts).to.deep.equal([btcCredentialJwt]);
});

it('should evaluate that the credential does not satisfy the presentation definition', async () => {
const incorrectPresentationDefinition = {
const otherPresentationDefinition = {
'id' : 'test-pd-id',
'name' : 'simple PD',
'purpose' : 'pd for testing',
Expand All @@ -67,7 +92,7 @@ describe('PresentationExchange', () => {
]
};

await expectThrowsAsync(() => PresentationExchange.satisfiesPresentationDefinition([btcCredentialJwt], incorrectPresentationDefinition), 'Input candidate does not contain property');
await expectThrowsAsync(() => PresentationExchange.satisfiesPresentationDefinition([btcCredentialJwt], otherPresentationDefinition), 'Input candidate does not contain property');
});

it('should successfully create a presentation from the given definition and credentials', () => {
Expand All @@ -76,6 +101,43 @@ describe('PresentationExchange', () => {
expect(presentationResult.presentationSubmission.definition_id).to.equal(presentationDefinition.id);
});

it('should throw error for invalid presentation definition', async () => {
const invalidPresentationDefinition = {
'id' : 'test-pd-id',
'name' : 'simple PD',
'purpose' : 'pd for testing',
'input_descriptors' : [
{
'id' : 'whatever',
'purpose' : 'id for testing',
'constraints' : {
'fields': [
{
'path': [
'not a valid path',
]
}
]
}
}
]
};

await expectThrowsAsync(() => PresentationExchange.createPresentationFromCredentials([btcCredentialJwt], invalidPresentationDefinition), 'Failed to pass validation check');
});

it('should fail to create a presentation with vc that does not match presentation definition', async() => {
const vc = VerifiableCredential.create(
'StreetCred',
signOptions.issuerDid,
signOptions.subjectDid,
new OtherCredential('otherstuff'),
);

const otherCredJwt = await vc.sign(signOptions);
await expectThrowsAsync(() => PresentationExchange.createPresentationFromCredentials([otherCredJwt], presentationDefinition), 'Failed to create Verifiable Presentation JWT due to: Required Credentials Not Present');
});

it('should successfully validate a presentation definition', () => {
const result:Validated = PresentationExchange.validateDefinition(presentationDefinition);
expect(result).to.deep.equal([{ tag: 'root', status: 'info', message: 'ok' }]);
Expand Down
49 changes: 49 additions & 0 deletions packages/credentials/tests/verifiable-credential.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,48 @@ describe('Verifiable Credential Tests', () => {
expect(vc.type).to.equal('StreetCred');
expect(vc.vcDataModel.issuanceDate).to.not.be.undefined;
expect(vc.vcDataModel.credentialSubject).to.deep.equal({ id: subjectDid, localRespect: 'high', legit: true });
});

it('should throw an error if data is not parseable into a JSON object', () => {
const issuerDid = 'did:example:issuer';
const subjectDid = 'did:example:subject';

const invalidData = 'NotAJSONObject';

expect(() => {
VerifiableCredential.create(
'InvalidDataTest',
issuerDid,
subjectDid,
invalidData
);
}).to.throw('Expected data to be parseable into a JSON object');
});

it('should throw an error if issuer or subject is not defined', () => {
const issuerDid = 'did:example:issuer';
const subjectDid = 'did:example:subject';
const validData = new StreetCredibility('high', true);

expect(() => {
VerifiableCredential.create(
'IssuerUndefinedTest',
'',
subjectDid,
validData
);
}).to.throw('Issuer and subject must be defined');

expect(() => {
VerifiableCredential.create(
'SubjectUndefinedTest',
issuerDid,
'',
validData
);
}).to.throw('Issuer and subject must be defined');

});

it('signing vc works', async () => {
const issuerDid = signOptions.issuerDid;
Expand Down Expand Up @@ -97,6 +136,10 @@ describe('Verifiable Credential Tests', () => {
await expectThrowsAsync(() => VerifiableCredential.verify(vcJwt), 'Unable to resolve DID');
});

it('parseJwt checks if missing vc property', async () => {
await expectThrowsAsync(() => VerifiableCredential.parseJwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'), 'Jwt payload missing vc property');
});

it('parseJwt returns an instance of VerifiableCredential on success', async () => {
const vc = VerifiableCredential.create(
'StreetCred',
Expand All @@ -120,6 +163,12 @@ describe('Verifiable Credential Tests', () => {
await expectThrowsAsync(() => VerifiableCredential.verify('invalid-jwt'), 'Not a valid jwt');
});

it('should throw an error if JWS header does not contain alg and kid', async () => {
const invalidJwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

await expectThrowsAsync(() => VerifiableCredential.verify(invalidJwt), 'Signature verification failed: Expected JWS header to contain alg and kid');
});

it('verify does not throw an exception with vaild vc', async () => {
const vc = VerifiableCredential.create(
'StreetCred',
Expand Down

0 comments on commit e4f2ee2

Please sign in to comment.