diff --git a/apps/shared/src/events/events.ts b/apps/shared/src/events/events.ts index 5b79978bdecf17cb8617212ea6384bc57d238812..ee5eddae7c27e7832fa2559a4ee58d38833e4603 100644 --- a/apps/shared/src/events/events.ts +++ b/apps/shared/src/events/events.ts @@ -1,4 +1,6 @@ -import { utils, type ConnectionRecord } from '@aries-framework/core'; +import type { DidDocument, ConnectionRecord } from '@aries-framework/core'; + +import { utils } from '@aries-framework/core'; export class BaseEvent< T extends Record<string, unknown> = Record<string, unknown>, @@ -16,6 +18,10 @@ export class BaseEvent< } } +export class EventInfoPublicDid extends BaseEvent<{ + didDocument: DidDocument; +}> {} + export class EventDidcommConnectionsGetAll extends BaseEvent<{ connections: Array<ConnectionRecord>; }> {} diff --git a/apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts b/apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts index 80a4c8065631d527966e316a6788d6eaedf4fb7a..6487fe71335d5f22310d79eea81eca3270d13c95 100644 --- a/apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts +++ b/apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts @@ -1,3 +1,4 @@ +import { DidDocument } from '@aries-framework/core'; import { Test } from '@nestjs/testing'; import { mockConfigModule } from '../../config/__tests__/mockConfig.js'; @@ -10,7 +11,7 @@ describe('AgentController', () => { beforeEach(async () => { const moduleRef = await Test.createTestingModule({ - imports: [mockConfigModule], + imports: [mockConfigModule(3002)], controllers: [AgentController], providers: [AgentService], }).compile(); @@ -21,12 +22,11 @@ describe('AgentController', () => { describe('public did', () => { it('should get the public did information of the agent', async () => { - const result = { id: 'test' }; - jest - .spyOn(agentService, 'getPublicDid') - .mockResolvedValue(result) + const result = new DidDocument({ id: 'did:key:123' }); + jest.spyOn(agentService, 'getPublicDid').mockResolvedValue(result); - expect(await agentController.publicDid()).toBe(result); + const event = await agentController.publicDid(); + expect(event.data).toMatchObject({ didDocument: result }); }); }); }); diff --git a/apps/ssi-abstraction/src/agent/agent.controller.ts b/apps/ssi-abstraction/src/agent/agent.controller.ts index 42fcd10a93a652ec362589a6cd4f5a3193857319..e77bb16eecb7a7ab35bd2cc09ca7752eccbf39af 100644 --- a/apps/ssi-abstraction/src/agent/agent.controller.ts +++ b/apps/ssi-abstraction/src/agent/agent.controller.ts @@ -1,5 +1,6 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; +import { EventInfoPublicDid } from '@ocm/shared'; import { AgentService } from './agent.service.js'; @@ -9,6 +10,8 @@ export class AgentController { @MessagePattern('info.publicDid') public async publicDid() { - return await this.agent.getPublicDid() + const didDocument = await this.agent.getPublicDid(); + + return new EventInfoPublicDid({ didDocument }); } } diff --git a/apps/ssi-abstraction/src/agent/agent.service.ts b/apps/ssi-abstraction/src/agent/agent.service.ts index bc6289fbcb515a7deb1a0676d67427279275bbce..343e90852239e56a086d2e7200485b2c69bee7e0 100644 --- a/apps/ssi-abstraction/src/agent/agent.service.ts +++ b/apps/ssi-abstraction/src/agent/agent.service.ts @@ -58,7 +58,7 @@ export class AgentService { }); const httpInbound = new HttpInboundTransport({ - port: Number(peerPort.replace(':', '')), + port: peerPort, }); this.agent.registerInboundTransport(httpInbound); @@ -119,9 +119,7 @@ export class AgentService { const ledgerIds = this.configService.get('agent.ledgerIds'); if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') { - throw new Error( - 'Agent could not start, please provide a ledger environment variable.', - ); + return []; } return ledgerIds.map((id: LedgerIds) => { @@ -148,12 +146,11 @@ export class AgentService { if (!publicDidSeed) { logger.info('No public did seed provided, skipping registration'); + return; } if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') { - throw new Error( - 'Agent could not start, please provide a ledger environment variable.', - ); + return; } const registeredPublicDidResponses = await registerPublicDids({ @@ -177,9 +174,24 @@ export class AgentService { } public async getPublicDid() { - return { - id: 'test', - }; + const dids = await this.agent.dids.getCreatedDids({ method: 'indy' }); + if (dids.length === 0) { + throw new Error('No registered public DIDs'); + } + + if (dids.length > 1) { + throw new Error('Multiple public DIDs found'); + } + + const didRecord = dids[0]; + + if (!didRecord.didDocument) { + throw new Error( + 'A public DID was found, but did not include a DID Document', + ); + } + + return didRecord.didDocument; } public async onModuleInit() { diff --git a/apps/ssi-abstraction/src/agent/connections/__tests__/connections.controller.spec.ts b/apps/ssi-abstraction/src/agent/connections/__tests__/connections.controller.spec.ts index 91d7dc8ba83e434886745bb04fc3b33c2a3e8d7e..690d3f7b7faa38c78f44e23b7b005a11231e33ce 100644 --- a/apps/ssi-abstraction/src/agent/connections/__tests__/connections.controller.spec.ts +++ b/apps/ssi-abstraction/src/agent/connections/__tests__/connections.controller.spec.ts @@ -13,7 +13,7 @@ describe('ConnectionsController', () => { beforeEach(async () => { const moduleRef = await Test.createTestingModule({ - imports: [mockConfigModule, AgentModule], + imports: [mockConfigModule(3003), AgentModule], controllers: [ConnectionsController], providers: [ConnectionsService], }).compile(); diff --git a/apps/ssi-abstraction/src/agent/ledger/register.ts b/apps/ssi-abstraction/src/agent/ledger/register.ts index 1bff2fb5ad5c22acfda14280cd5190fac871bd4f..1396428d29e68d21ab7df21390bdd070fdb1336c 100644 --- a/apps/ssi-abstraction/src/agent/ledger/register.ts +++ b/apps/ssi-abstraction/src/agent/ledger/register.ts @@ -40,10 +40,11 @@ export const registerPublicDids = async ({ seed, }; - const res = await new axios.Axios().post<RegisterPublicDidResponse>( - ledgerRegisterUrl, - body, - ); + const res = await axios({ + method: 'post', + url: ledgerRegisterUrl, + data: body, + }); if (res.data) { logger.info('Agent DID registered.'); diff --git a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts index c12689ff832080728b8f335023a3b7db94ff9ccc..a1828c7843a28ddfc32e859201fd7d73753270f3 100644 --- a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts +++ b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts @@ -5,9 +5,9 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { validationSchema } from '../validation.js'; -const mockConfig: AppConfig = { +const mockConfig = (port: number = 3001): AppConfig => ({ agentHost: '', - port: 3000, + port:3000, jwtSecret: '', nats: { url: 'localhost', @@ -16,45 +16,48 @@ const mockConfig: AppConfig = { name: 'my-test-agent', walletId: 'some-id', walletKey: 'some-key', - ledgerIds: ['BCOVRIN_TEST'], + ledgerIds: [], host: '3000', - peerPort: '3001', + inboundPort: port, path: '', - publicDidSeed: 'none', + publicDidSeed: '', autoAcceptConnection: false, autoAcceptCredential: AutoAcceptCredential.ContentApproved, }, -}; - -export const mockConfigModule = ConfigModule.forRoot({ - load: [() => mockConfig], - validationSchema, }); +export const mockConfigModule = (port: number = 3000) => + ConfigModule.forRoot({ + load: [() => mockConfig(port)], + validationSchema, + }); + describe('configuration', () => { + const mockedConfig = mockConfig(); + describe('service', () => { it('should be able to instantiate a config service', () => { - const configuration = new ConfigService(mockConfig); + const configuration = new ConfigService(mockConfig()); expect(configuration).toBeInstanceOf(ConfigService); }); it('should be able to extract root value', () => { - const configuration = new ConfigService(mockConfig); + const configuration = new ConfigService(mockConfig()); - expect(configuration.get('port')).toStrictEqual(mockConfig.port); + expect(configuration.get('port')).toStrictEqual(mockedConfig.port); }); it('should be able to extract root value as object', () => { - const configuration = new ConfigService(mockConfig); + const configuration = new ConfigService(mockConfig()); - expect(configuration.get('agent')).toMatchObject(mockConfig.agent); + expect(configuration.get('agent')).toMatchObject(mockedConfig.agent); }); it('should be able to extract nested values', () => { - const configuration = new ConfigService(mockConfig); + const configuration = new ConfigService(mockConfig()); expect(configuration.get('agent.autoAcceptCredential')).toStrictEqual( - mockConfig.agent.autoAcceptCredential, + mockedConfig.agent.autoAcceptCredential, ); }); }); diff --git a/apps/ssi-abstraction/src/config/config.ts b/apps/ssi-abstraction/src/config/config.ts index 3e54d34bd69993e7912646c2ada3ef632be326a3..d3446365c8e2c88c27ce0df29a3fc6a8eac6d536 100644 --- a/apps/ssi-abstraction/src/config/config.ts +++ b/apps/ssi-abstraction/src/config/config.ts @@ -15,7 +15,7 @@ export interface AppConfig { walletKey: string; ledgerIds?: string[]; host: string; - peerPort: string; + inboundPort: number; path: string; publicDidSeed: string; autoAcceptConnection: boolean; @@ -25,7 +25,7 @@ export interface AppConfig { export const config = (): AppConfig => ({ agentHost: process.env.AGENT_HOST || '', - port: Number(process.env.PORT), + port: parseInt(process.env.PORT || '3000'), jwtSecret: process.env.JWT_SECRET || '', nats: { @@ -38,7 +38,7 @@ export const config = (): AppConfig => ({ walletKey: process.env.AGENT_WALLET_KEY || '', ledgerIds: process.env.AGENT_LEDGER_ID?.split(','), host: process.env.AGENT_HOST || '', - peerPort: process.env.AGENT_PEER_PORT || '', + inboundPort: parseInt(process.env.AGENT_INBOUND_PORT || '3001'), path: process.env.AGENT_URL_PATH || '', publicDidSeed: process.env.AGENT_PUBLIC_DID_SEED || '', autoAcceptConnection: process.env.AGENT_AUTO_ACCEPT_CONNECTION === 'true', diff --git a/apps/ssi-abstraction/test/agent.e2e-spec.ts b/apps/ssi-abstraction/test/agent.e2e-spec.ts index f5ec1eb27a22a7b74ebba8b4b3332c0881bbb6b2..224b446ec1898ab09b2f0d9613883ce5fd43a29e 100644 --- a/apps/ssi-abstraction/test/agent.e2e-spec.ts +++ b/apps/ssi-abstraction/test/agent.e2e-spec.ts @@ -1,6 +1,8 @@ import type { INestApplication } from '@nestjs/common'; import type { ClientProxy } from '@nestjs/microservices'; +import type { EventInfoPublicDid } from '@ocm/shared'; +import { DidDocument } from '@aries-framework/core'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; import { firstValueFrom, type Observable } from 'rxjs'; @@ -9,29 +11,39 @@ import { AgentModule } from '../src/agent/agent.module.js'; import { AgentService } from '../src/agent/agent.service.js'; import { mockConfigModule } from '../src/config/__tests__/mockConfig.js'; +const mockDidDocument = { + '@context': ['https://w3id.org/did/v1'], + id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM', + verificationMethod: [ + { + id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey', + type: 'Ed25519VerificationKey2018', + controller: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM', + publicKeyBase58: '4SySYXQUtuK26zfC7RpQpWYMThfbXphUf8LWyXXmxyTX', + }, + ], + authentication: ['did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey'], +}; + describe('Agent', () => { const TOKEN = 'AGENT_CLIENT_SERVICE'; let app: INestApplication; let client: ClientProxy; - const agentService = { - getPublicDid: () => - Promise.resolve({ - id: 'test', - }), - }; - beforeAll(async () => { + jest + .spyOn(AgentService.prototype, 'getPublicDid') + .mockImplementation(() => + Promise.resolve(new DidDocument(mockDidDocument)), + ); + const moduleRef = await Test.createTestingModule({ imports: [ - mockConfigModule, + mockConfigModule(3000), AgentModule, ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]), ], - }) - .overrideProvider(AgentService) - .useValue(agentService) - .compile(); + }).compile(); app = moduleRef.createNestApplication(); @@ -45,9 +57,16 @@ describe('Agent', () => { }); it('info.publicDid', async () => { - const response$: Observable<unknown> = client.send('info.publicDid', {}); + const response$: Observable<EventInfoPublicDid> = client.send( + 'info.publicDid', + {}, + ); + const response = await firstValueFrom(response$); - expect(response).toMatchObject({ id: 'test' }); + + expect(response.data).toMatchObject({ + didDocument: mockDidDocument, + }); }); afterAll(async () => { diff --git a/apps/ssi-abstraction/test/connections.e2e-spec.ts b/apps/ssi-abstraction/test/connections.e2e-spec.ts index 75e7762bb220510f3f7fc6522b8ffeac46000af4..a2b5a523c34e467efd953404e57ac64eaa940228 100644 --- a/apps/ssi-abstraction/test/connections.e2e-spec.ts +++ b/apps/ssi-abstraction/test/connections.e2e-spec.ts @@ -18,7 +18,7 @@ describe('Connections', () => { beforeAll(async () => { const moduleRef = await Test.createTestingModule({ imports: [ - mockConfigModule, + mockConfigModule(3004), AgentModule, ConnectionsModule, ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]), diff --git a/apps/ssi-abstraction/test/health.e2e-spec.ts b/apps/ssi-abstraction/test/health.e2e-spec.ts index 8d14bcc4ed2e81a66f0a4efaa73dcec6e442b0c6..479567b54300dff4d043abcf45cefeb11c485ff0 100644 --- a/apps/ssi-abstraction/test/health.e2e-spec.ts +++ b/apps/ssi-abstraction/test/health.e2e-spec.ts @@ -17,6 +17,7 @@ describe('HealthController (e2e)', () => { app = moduleFixture.createNestApplication(); await app.init(); }); + afterAll(async () => { await app.close(); }); diff --git a/apps/ssi-abstraction/test/jest.config.js b/apps/ssi-abstraction/test/jest.config.js index aff146f7bfb93ea0915f112ad94fa165c2cb03cd..c03e51b25e5fed650794e8265281358c6198df91 100644 --- a/apps/ssi-abstraction/test/jest.config.js +++ b/apps/ssi-abstraction/test/jest.config.js @@ -1,8 +1,9 @@ -/** @type {import('jest').Config} */ import config from '../jest.config.js'; +/** @type {import('jest').Config} */ export default { ...config, + testTimeout: 12000, rootDir: '.', testRegex: '.*\\.e2e-spec\\.ts$', };