UNPKG

@microsoft/useragent-sdk

Version:

SDK for building decentralized identity wallets and enterprise agents.

207 lines (174 loc) 8.25 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License in the project root for license information. *--------------------------------------------------------------------------------------------*/ import SidetreeRegistrar from '../../src/registrars/SidetreeRegistrar'; import IdentifierDocument from '../../src/IdentifierDocument'; import Identifier from '../../src/Identifier'; import UserAgentError from '../../src/UserAgentError'; import UserAgentOptions from '../../src/UserAgentOptions'; import CryptoOptions from '../../src/CryptoOptions'; import KeyStoreConstants from '../../src/keystores/KeyStoreConstants'; import CryptoFactory from '../../src/crypto/plugin/CryptoFactory'; import KeyStoreInMemory from '../../src/crypto/keyStore/KeyStoreInMemory'; import IJwsFlatJson from '../../src/crypto/protocols/jose/jws/IJwsFlatJson'; import OctKey from '../../src/crypto/keys/Oct/OctKey'; import SubtleCryptoNode from '../../src/crypto/plugin/SubtleCryptoNode'; import KeyContainer from '../../src/crypto/keys/KeyContainer'; let fetchMock: any; // Add a document to the cache const DOCUMENT = { '@context': 'https://w3id.org/did/v1', 'id': 'did:test:identifier' }; describe('SidetreeRegistrar', () => { let options: UserAgentOptions; beforeAll(() => { fetchMock = require('fetch-mock'); options = new UserAgentOptions(); (<CryptoOptions> options.cryptoOptions).signingAlgorithm = 'ES256K'; options.cryptoFactory = new CryptoFactory(new KeyStoreInMemory(), SubtleCryptoNode.getSubtleCrypto()); options.registrar = new SidetreeRegistrar('https://registrar.org', options); }); afterEach(() => { fetchMock.restore(); }); it('should throw because of missing options', async (done) => { try { await new SidetreeRegistrar('https://registrar.org/', <any>{}).register(new IdentifierDocument(''), ''); fail('Failed to throw because of missing options'); } catch (err) { expect('options and options.keyStore need to be defined').toBe(err.message); done(); } }); it('should construct new instance of the SidetreeRegistrar', async () => { let options = new UserAgentOptions(); options.timeoutInSeconds = 30; options.keyStore = new KeyStoreInMemory(); const registrar = new SidetreeRegistrar('https://registrar.org/', options); expect(registrar).toBeDefined(); expect(registrar.url).toEqual('https://registrar.org/'); }); it('should construct new instance of the SidetreeRegistrar appending trailing slash', async () => { let options = new UserAgentOptions(); options.timeoutInSeconds = 30; options.keyStore = new KeyStoreInMemory(); const registrar = new SidetreeRegistrar('https://registrar.org', options); expect(registrar).toBeDefined(); expect(registrar.url).toEqual('https://registrar.org/'); }); it('should return a new identifier', async () => { // Setup registration environment const seed = new OctKey('ABDE'); await options.keyStore.save('masterSeed', new KeyContainer(seed)); let identifier: Identifier = new Identifier('did:test:identifier', options); fetchMock.mock( (url: any, opts: any) => { expect(url).toEqual('https://registrar.org/'); expect(opts).toBeDefined(); // Make sure the document has been passed const body: IJwsFlatJson = JSON.parse(opts.body); expect(body.header).toBeDefined(); // the header should be undefined. commented out for moment to get to identiverse - todo //expect(body.protected).toBeUndefined(); expect(body.payload).toBeDefined(); expect(body.signature).toBeDefined(); return true; }, new Promise(resolve => resolve(identifier)), { method: 'POST' } ); identifier = await identifier.createLinkedIdentifier('did:ion:peer', true); expect(identifier).toBeDefined(); }); it('should throw UserAgentError when fetch timeout threshold reached', async done => { options.timeoutInSeconds = 1; options.registrar = new SidetreeRegistrar('https://registrar.org/', options); // Setup registration environment const seed = new OctKey('ABDE'); await options.keyStore.save('masterSeed', new KeyContainer(seed)); const identifier: Identifier = new Identifier('did:test:identifier', options); // Set the mock timeout to be greater than the fetch configuration // timeout to ensure that the fetch timeout works as expected. const delay = new Promise((response, _) => setTimeout(response, 1000 * 10)); fetchMock.post('https://registrar.org/', delay.then((_) => 404)); // tslint:disable-next-line: no-floating-promises identifier.createLinkedIdentifier('did:ion:peer', true) .then(() => { fail('No throw detected because of timeout.'); }) .catch((error: any) => { expect(error).toBeDefined(); expect(error instanceof UserAgentError).toBeTruthy(); expect(error.message).toEqual(`Fetch timed out.`); }) .finally(() => { options.timeoutInSeconds = 30; done(); }); }); it('should throw UserAgentError when error returned by registrar', async done => { // Setup registration environment const seed = new OctKey('ABDE'); await options.keyStore.save('masterSeed', new KeyContainer(seed)); const identifier: Identifier = new Identifier('did:test:identifier', options); fetchMock.post('https://registrar.org/', 404); // tslint:disable-next-line: no-floating-promises identifier.createLinkedIdentifier('did:ion:peer', true) .then(() => { fail('No throw detected because of 404.'); }) .catch((error: any) => { expect(error).toBeDefined(); expect(error instanceof UserAgentError).toBeTruthy(); expect(error.message).toEqual('Failed to register the identifier document: Status 404'); }) .finally(done); }); it('should throw UserAgentError when generating an identifier and no public key specified', async done => { const registrar = new SidetreeRegistrar('https://registrar.org', options); fetchMock.post('https://registrar.org/register', 404); await registrar .generateIdentifier(new IdentifierDocument(DOCUMENT)) .catch((error: any) => { expect(error).toBeDefined(); expect(error instanceof UserAgentError).toBeTruthy(); expect(error.message).toEqual('At least one public key must be specified in the identifier document.'); }) .finally(done); }); it('should return generated identifier', async () => { const registrar = new SidetreeRegistrar('https://registrar.org', options); const genesisDocument = Object.assign(DOCUMENT, { publicKeys: [{ id: '#master', type: 'Secp256k1VerificationKey2018', publicKeyHex: '02f49802fb3e09c6dd43f19aa41293d1e0dad044b68cf81cf7079499edfd0aa9f1' }] }); const identifier: Identifier = await registrar.generateIdentifier(new IdentifierDocument(genesisDocument)); expect(identifier).toBeDefined(); expect(identifier.id).toContain('did:ion'); }); it('should always return same identifier when provided same genesis document ', async () => { const registrar = new SidetreeRegistrar('https://registrar.org', options); const genesisDocument = Object.assign(DOCUMENT, { publicKeys: [{ id: '#master', type: 'Secp256k1VerificationKey2018', publicKeyHex: '02f49802fb3e09c6dd43f19aa41293d1e0dad044b68cf81cf7079499edfd0aa9f1' }] }); let previousIdentifier: string = ''; for (let index = 0; index < 20; index++) { const identifier: Identifier = await registrar.generateIdentifier(new IdentifierDocument(genesisDocument)); if (index !== 0) { expect(identifier.id).toEqual(previousIdentifier); } previousIdentifier = identifier.id; } }); });