Skip to content

Commit

Permalink
Add tests for error cases in did-resolver.spec.ts (#561)
Browse files Browse the repository at this point in the history
* Add tests for error cases in did-resolver.spec.ts

This commit adds new tests in did-resolver.spec.ts to cover some of the previously uncovered error-related branching paths.

This commit contributes partly to issue #136.

Based on the output of `npm run test:node` before vs. after this commit, the `% Branch` is now increased by 9.66 percentage points, and the `% Stmt` and `% Lines` by 3.29 percentage points.

Before:
```
did-resolver.ts |   87.91 |    78.57 |   66.66 |   87.91
```

After:
```
did-resolver.ts |    91.2 |    88.23 |   66.66 |    91.2 |
```

* Add tests for error cases in did-resolver.spec.ts

This commit adds new tests in did-resolver.spec.ts to cover some of the previously uncovered error-related branching paths.

This commit contributes partly to issue #136.

Based on the output of `npm run test:node` before vs. after this commit, the `% Branch` is now increased by 9.66 percentage points, and the `% Stmt` and `% Lines` by 3.29 percentage points.

Before:
```
did-resolver.ts |   87.91 |    78.57 |   66.66 |   87.91
```

After:
```
did-resolver.ts |   92.34 |    94.11 |   66.66 |   92.34 |
```

* Add tests for error cases in did-resolver.spec.ts

This commit adds new tests in did-resolver.spec.ts to cover some of the previously uncovered error-related branching paths.

This commit contributes partly to issue #136.

Based on the output of `npm run test:node` before vs. after this commit, the `% Branch` is now increased by 9.66 percentage points, and the `% Stmt` and `% Lines` by 3.29 percentage points.

Before:
```
did-resolver.ts |   87.91 |    78.57 |   66.66 |   87.91
```

After:
```
did-resolver.ts |   92.34 |    94.11 |   66.66 |   92.34 |
```

* Arrange new Did related error codes alphabetically

* Use a different code for DidNotString
  • Loading branch information
shobitb authored Oct 17, 2023
1 parent ff60a58 commit 4daab00
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Here's to a thrilling Hacktoberfest voyage with us! 🎉
# Decentralized Web Node (DWN) SDK <!-- omit in toc -->

Code Coverage
![Statements](https://img.shields.io/badge/statements-98.35%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95.14%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-95.73%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-98.35%25-brightgreen.svg?style=flat)
![Statements](https://img.shields.io/badge/statements-98.46%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95.29%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-95.73%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-98.46%25-brightgreen.svg?style=flat)

- [Introduction](#introduction)
- [Installation](#installation)
Expand Down
4 changes: 4 additions & 0 deletions src/core/dwn-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export class DwnError extends Error {
export enum DwnErrorCode {
AuthenticateJwsMissing = 'AuthenticateJwsMissing',
AuthorizationUnknownAuthor = 'AuthorizationUnknownAuthor',
DidMethodNotSupported = 'DidMethodNotSupported',
DidNotString = 'DidNotString',
DidNotValid = 'DidNotValid',
DidResolutionFailed = 'DidResolutionFailed',
GeneralJwsVerifierInvalidSignature = 'GeneralJwsVerifierInvalidSignature',
GrantAuthorizationGrantExpired = 'GrantAuthorizationGrantExpired',
GrantAuthorizationGrantMissing = 'GrantAuthorizationGrantMissing',
Expand Down
5 changes: 3 additions & 2 deletions src/did/did-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Did } from './did.js';
import { DidIonResolver } from './did-ion-resolver.js';
import { DidKeyResolver } from './did-key-resolver.js';
import { MemoryCache } from '../utils/memory-cache.js';
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';

/**
* A DID resolver that by default supports `did:key` and `did:ion` DIDs.
Expand Down Expand Up @@ -49,7 +50,7 @@ export class DidResolver {
const didResolver = this.didResolvers.get(didMethod);

if (!didResolver) {
throw new Error(`${didMethod} DID method not supported`);
throw new DwnError(DwnErrorCode.DidMethodNotSupported, `${didMethod} DID method not supported`);
}

// use cached result if exists
Expand All @@ -66,7 +67,7 @@ export class DidResolver {
let errMsg = `Failed to resolve DID ${did}.`;
errMsg += error ? ` Error: ${error}` : '';

throw new Error(errMsg);
throw new DwnError(DwnErrorCode.DidResolutionFailed, errMsg);
}

return resolutionResult;
Expand Down
6 changes: 4 additions & 2 deletions src/did/did.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';

/**
* DID related operations.
*/
Expand All @@ -16,13 +18,13 @@ export class Did {
*/
public static validate(did: unknown): void {
if (typeof did !== 'string') {
throw new Error(`DID is not string: ${did}`);
throw new DwnError(DwnErrorCode.DidNotString, `DID is not string: ${did}`);
}

// eslint-disable-next-line
const didRegex= /^did:([a-z0-9]+):((?:(?:[a-zA-Z0-9._-]|(?:%[0-9a-fA-F]{2}))*:)*((?:[a-zA-Z0-9._-]|(?:%[0-9a-fA-F]{2}))+))((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\/[^#?]*)?([?][^#]*)?(#.*)?$/;
if (!didRegex.test(did)) {
throw new TypeError(`DID is not a valid DID: ${did}`);
throw new DwnError(DwnErrorCode.DidNotValid, `DID is not a valid DID: ${did}`);
}
}

Expand Down
60 changes: 60 additions & 0 deletions tests/did/did-resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chai, { expect } from 'chai';

import { DidIonResolver } from '../../src/did/did-ion-resolver.js';
import { DidResolver } from '../../src/did/did-resolver.js';
import { DwnErrorCode } from '../../src/core/dwn-error.js';

// extends chai to test promises
chai.use(chaiAsPromised);
Expand Down Expand Up @@ -32,4 +33,63 @@ describe('DidResolver', () => {
sinon.assert.calledTwice(cacheGetSpy); // should try to fetch from cache both times
sinon.assert.calledOnce(ionDidResolveSpy); // should only resolve using ION resolver once (the first time)
});

it('should throw error when invalid DID is used', async () => {
const did = 'blah';
const didIonResolver = new DidIonResolver();
const didResolver = new DidResolver([didIonResolver]);

await expect(didResolver.resolve(did)).to.eventually.be.rejected.and.has.property('code', DwnErrorCode.DidNotValid);
});

it('should throw error when unsupported DID method is used', async () => {
const did = 'did:foo:EiClkZMDxPKqC9c-umQfTkR8vvZ9JPhl_xLDI9Nfk38w5w';
const didIonResolver = new DidIonResolver();
const didResolver = new DidResolver([didIonResolver]);

await expect(didResolver.resolve(did)).to.eventually.be.rejected.and.has.property('code', DwnErrorCode.DidMethodNotSupported);
});

it('should throw error when resolution fails due to error in didResolutionMetadata', async () => {
const did = 'did:ion:EiClkZMDxPKqC9c-umQfTkR8vvZ9JPhl_xLDI9Nfk38w5w';
const didIonResolver = new DidIonResolver('unusedResolutionEndpoint');
const didResolver = new DidResolver([didIonResolver]);

const mockResolution = {
didDocument : 'any' as any,
didResolutionMetadata : { error: 'some error' },
didDocumentMetadata : 'any' as any
};

const ionDidResolveSpy = sinon.stub(didIonResolver, 'resolve').resolves(mockResolution);
const cacheGetSpy = sinon.spy(didResolver['cache'], 'get');

await expect(didResolver.resolve(did)).to.eventually.be.rejected.and.has.property('code', DwnErrorCode.DidResolutionFailed);

sinon.assert.calledOnce(cacheGetSpy);
sinon.assert.calledOnce(ionDidResolveSpy);

});

it('should throw error when resolution fails due to undefined didDocument', async () => {
const did = 'did:ion:EiClkZMDxPKqC9c-umQfTkR8vvZ9JPhl_xLDI9Nfk38w5w';
const didIonResolver = new DidIonResolver('unusedResolutionEndpoint');
const didResolver = new DidResolver([didIonResolver]);

const mockResolution = {
didDocument : undefined,
didResolutionMetadata : 'any' as any,
didDocumentMetadata : 'any' as any
};

const ionDidResolveSpy = sinon.stub(didIonResolver, 'resolve').resolves(mockResolution);
const cacheGetSpy = sinon.spy(didResolver['cache'], 'get');

await expect(didResolver.resolve(did)).to.eventually.be.rejected.and.has.property('code', DwnErrorCode.DidResolutionFailed);

sinon.assert.calledOnce(cacheGetSpy);
sinon.assert.calledOnce(ionDidResolveSpy);

});

});
4 changes: 3 additions & 1 deletion tests/did/did.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import chai, { expect } from 'chai';

import { Did } from '../../src/did/did.js';

import { DwnError } from '../../src/core/dwn-error.js';

// extends chai to test promises
chai.use(chaiAsPromised);

Expand All @@ -15,6 +17,6 @@ describe('Did.validate', () => {

it('should fail validation for valid DIDs', () => {
expect(() => Did.validate(null)).to.throw(Error);
expect(() => Did.validate('did:123456789abcdefghijk')).to.throw(Error);
expect(() => Did.validate('did:123456789abcdefghijk')).to.throw(DwnError);
});
});

0 comments on commit 4daab00

Please sign in to comment.