diff --git a/apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts b/apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..5d24ea1a0a8e089e834ba6ae883f9c781bc82a3c --- /dev/null +++ b/apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts @@ -0,0 +1,32 @@ +import { Test } from '@nestjs/testing'; + +import { mockConfigModule } from '../../config/__tests__/mockConfig.js'; +import { AgentController } from '../agent.controller.js'; +import { AgentService } from '../agent.service.js'; + +describe('AgentController', () => { + let agentController: AgentController; + let agentService: AgentService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [mockConfigModule], + controllers: [AgentController], + providers: [AgentService], + }).compile(); + + agentService = moduleRef.get(AgentService); + agentController = moduleRef.get(AgentController); + }); + + describe('public did', () => { + it('should get the public did information of the agent', async () => { + const result = { id: 'test' }; + jest + .spyOn(agentService, 'getPublicDid') + .mockImplementation(() => Promise.resolve(result)); + + expect(await agentController.publicDid()).toBe(result); + }); + }); +}); diff --git a/apps/ssi-abstraction/src/agent/agent.controller.ts b/apps/ssi-abstraction/src/agent/agent.controller.ts index 4a07c5b4ef9a3d5245532d684065abd50aec2fde..42fcd10a93a652ec362589a6cd4f5a3193857319 100644 --- a/apps/ssi-abstraction/src/agent/agent.controller.ts +++ b/apps/ssi-abstraction/src/agent/agent.controller.ts @@ -9,8 +9,6 @@ export class AgentController { @MessagePattern('info.publicDid') public async publicDid() { - return { - id: 'test', - }; + return await this.agent.getPublicDid() } } diff --git a/apps/ssi-abstraction/src/agent/agent.service.ts b/apps/ssi-abstraction/src/agent/agent.service.ts index 7e06a896bf77496b3db9532b23b318afabbe96d9..bc6289fbcb515a7deb1a0676d67427279275bbce 100644 --- a/apps/ssi-abstraction/src/agent/agent.service.ts +++ b/apps/ssi-abstraction/src/agent/agent.service.ts @@ -79,7 +79,7 @@ export class AgentService { key: walletKey, }, endpoints, - logger: new AgentLogger(LogLevel.warn), + logger: new AgentLogger(LogLevel.off), }; } @@ -176,6 +176,12 @@ export class AgentService { } } + public async getPublicDid() { + return { + id: 'test', + }; + } + public async onModuleInit() { await this.agent.initialize(); await this.registerPublicDid(); diff --git a/apps/ssi-abstraction/src/agent/connection/connection.controller.ts b/apps/ssi-abstraction/src/agent/connection/connection.controller.ts deleted file mode 100644 index 83df3322630ab214bc77ba9eac3fb1727c2e677a..0000000000000000000000000000000000000000 --- a/apps/ssi-abstraction/src/agent/connection/connection.controller.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Controller } from '@nestjs/common'; -import { MessagePattern } from '@nestjs/microservices'; - -import { ConnectionService } from './connection.service.js'; - -@Controller('connection') -export class ConnectionController { - public constructor(private connectionService: ConnectionService) {} - - @MessagePattern('connection.getAll') - public async getAll() { - return this.connectionService.getAll(); - } -} diff --git a/apps/ssi-abstraction/src/agent/connection/connection.module.ts b/apps/ssi-abstraction/src/agent/connection/connection.module.ts deleted file mode 100644 index 4deaa8296424ce45f0b32aff09d270111a6f2378..0000000000000000000000000000000000000000 --- a/apps/ssi-abstraction/src/agent/connection/connection.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { AgentModule } from '../agent.module.js'; - -import { ConnectionController } from './connection.controller.js'; -import { ConnectionService } from './connection.service.js'; - -@Module({ - imports: [AgentModule], - providers: [ConnectionService], - controllers: [ConnectionController], -}) -export class ConnectionModule {} 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 new file mode 100644 index 0000000000000000000000000000000000000000..f516874f7f301baadde5b3b270bc3c1ef715936a --- /dev/null +++ b/apps/ssi-abstraction/src/agent/connections/__tests__/connections.controller.spec.ts @@ -0,0 +1,35 @@ +import type { ConnectionRecord } from '@aries-framework/core'; + +import { Test } from '@nestjs/testing'; + +import { mockConfigModule } from '../../../config/__tests__/mockConfig.js'; +import { AgentModule } from '../../agent.module.js'; +import { ConnectionsController } from '../connections.controller.js'; +import { ConnectionsService } from '../connections.service.js'; + +describe('ConnectionsController', () => { + let connectionsController: ConnectionsController; + let connectionsService: ConnectionsService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [mockConfigModule, AgentModule], + controllers: [ConnectionsController], + providers: [ConnectionsService], + }).compile(); + + connectionsService = moduleRef.get(ConnectionsService); + connectionsController = moduleRef.get(ConnectionsController); + }); + + describe('get all', () => { + it('should get all the connection records of the agent', async () => { + const result: Array<ConnectionRecord> = []; + jest + .spyOn(connectionsService, 'getAll') + .mockImplementation(() => Promise.resolve(result)); + + expect(await connectionsController.getAll()).toBe(result); + }); + }); +}); diff --git a/apps/ssi-abstraction/src/agent/connections/connections.controller.ts b/apps/ssi-abstraction/src/agent/connections/connections.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2aa9734a7a6c2cbd27491c7e898d09461b3b085 --- /dev/null +++ b/apps/ssi-abstraction/src/agent/connections/connections.controller.ts @@ -0,0 +1,14 @@ +import { Controller } from '@nestjs/common'; +import { MessagePattern } from '@nestjs/microservices'; + +import { ConnectionsService } from './connections.service.js'; + +@Controller('connections') +export class ConnectionsController { + public constructor(private connectionsService: ConnectionsService) {} + + @MessagePattern('didcomm.connections.getAll') + public async getAll() { + return await this.connectionsService.getAll(); + } +} diff --git a/apps/ssi-abstraction/src/agent/connections/connections.module.ts b/apps/ssi-abstraction/src/agent/connections/connections.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..edee4f45a63a1731c0bf23487a1028f8f5dae183 --- /dev/null +++ b/apps/ssi-abstraction/src/agent/connections/connections.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; + +import { AgentModule } from '../agent.module.js'; + +import { ConnectionsController } from './connections.controller.js'; +import { ConnectionsService } from './connections.service.js'; + +@Module({ + imports: [AgentModule], + providers: [ConnectionsService], + controllers: [ConnectionsController], +}) +export class ConnectionsModule {} diff --git a/apps/ssi-abstraction/src/agent/connection/connection.service.ts b/apps/ssi-abstraction/src/agent/connections/connections.service.ts similarity index 93% rename from apps/ssi-abstraction/src/agent/connection/connection.service.ts rename to apps/ssi-abstraction/src/agent/connections/connections.service.ts index 0e4c7ed0d57b64b8957fedcad2e878293cba89fc..5b8c7e8cae7f321b456055b21fd3ac0ced877bc8 100644 --- a/apps/ssi-abstraction/src/agent/connection/connection.service.ts +++ b/apps/ssi-abstraction/src/agent/connections/connections.service.ts @@ -6,7 +6,7 @@ import { Injectable } from '@nestjs/common'; import { AgentService } from '../agent.service.js'; @Injectable() -export class ConnectionService { +export class ConnectionsService { public agent: AppAgent; public constructor(agentService: AgentService) { diff --git a/apps/ssi-abstraction/src/app.module.ts b/apps/ssi-abstraction/src/app.module.ts index 762a4ebabc01787d9729b8a7cd547a39a3b46db3..42d993124609ab1823b3e35c109962c6777b41f5 100644 --- a/apps/ssi-abstraction/src/app.module.ts +++ b/apps/ssi-abstraction/src/app.module.ts @@ -4,7 +4,7 @@ import { TerminusModule } from '@nestjs/terminus'; import { HealthController } from '@ocm/shared'; import { AgentModule } from './agent/agent.module.js'; -import { ConnectionModule } from './agent/connection/connection.module.js'; +import { ConnectionsModule } from './agent/connections/connections.module.js'; import { config } from './config/config.js'; import { validationSchema } from './config/validation.js'; @@ -17,7 +17,7 @@ import { validationSchema } from './config/validation.js'; validationSchema, }), AgentModule, - ConnectionModule, + ConnectionsModule, ], controllers: [HealthController], }) diff --git a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..c12689ff832080728b8f335023a3b7db94ff9ccc --- /dev/null +++ b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts @@ -0,0 +1,61 @@ +import type { AppConfig } from '../config.js'; + +import { AutoAcceptCredential } from '@aries-framework/core'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +import { validationSchema } from '../validation.js'; + +const mockConfig: AppConfig = { + agentHost: '', + port: 3000, + jwtSecret: '', + nats: { + url: 'localhost', + }, + agent: { + name: 'my-test-agent', + walletId: 'some-id', + walletKey: 'some-key', + ledgerIds: ['BCOVRIN_TEST'], + host: '3000', + peerPort: '3001', + path: '', + publicDidSeed: 'none', + autoAcceptConnection: false, + autoAcceptCredential: AutoAcceptCredential.ContentApproved, + }, +}; + +export const mockConfigModule = ConfigModule.forRoot({ + load: [() => mockConfig], + validationSchema, +}); + +describe('configuration', () => { + describe('service', () => { + it('should be able to instantiate a config service', () => { + const configuration = new ConfigService(mockConfig); + expect(configuration).toBeInstanceOf(ConfigService); + }); + + it('should be able to extract root value', () => { + const configuration = new ConfigService(mockConfig); + + expect(configuration.get('port')).toStrictEqual(mockConfig.port); + }); + + it('should be able to extract root value as object', () => { + const configuration = new ConfigService(mockConfig); + + expect(configuration.get('agent')).toMatchObject(mockConfig.agent); + }); + + it('should be able to extract nested values', () => { + const configuration = new ConfigService(mockConfig); + + expect(configuration.get('agent.autoAcceptCredential')).toStrictEqual( + mockConfig.agent.autoAcceptCredential, + ); + }); + }); +}); diff --git a/apps/ssi-abstraction/src/config/config.ts b/apps/ssi-abstraction/src/config/config.ts index 874026b28f0d69b6135be58ee4f567d7bde7c0ab..3e54d34bd69993e7912646c2ada3ef632be326a3 100644 --- a/apps/ssi-abstraction/src/config/config.ts +++ b/apps/ssi-abstraction/src/config/config.ts @@ -1,6 +1,6 @@ import { AutoAcceptCredential } from '@aries-framework/core'; -interface Config { +export interface AppConfig { agentHost: string; port: number; jwtSecret: string; @@ -20,11 +20,10 @@ interface Config { publicDidSeed: string; autoAcceptConnection: boolean; autoAcceptCredential: AutoAcceptCredential; - idUnionKey: string; }; } -export const config = (): Config => ({ +export const config = (): AppConfig => ({ agentHost: process.env.AGENT_HOST || '', port: Number(process.env.PORT), jwtSecret: process.env.JWT_SECRET || '', @@ -46,6 +45,5 @@ export const config = (): Config => ({ autoAcceptCredential: (process.env.AGENT_AUTO_ACCEPT_CREDENTIAL as AutoAcceptCredential) || AutoAcceptCredential.ContentApproved, - idUnionKey: process.env.AGENT_ID_UNION_KEY || '', }, }); diff --git a/apps/ssi-abstraction/test/agent.e2e-spec.ts b/apps/ssi-abstraction/test/agent.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5ec1eb27a22a7b74ebba8b4b3332c0881bbb6b2 --- /dev/null +++ b/apps/ssi-abstraction/test/agent.e2e-spec.ts @@ -0,0 +1,57 @@ +import type { INestApplication } from '@nestjs/common'; +import type { ClientProxy } from '@nestjs/microservices'; + +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { Test } from '@nestjs/testing'; +import { firstValueFrom, type Observable } from 'rxjs'; + +import { AgentModule } from '../src/agent/agent.module.js'; +import { AgentService } from '../src/agent/agent.service.js'; +import { mockConfigModule } from '../src/config/__tests__/mockConfig.js'; + +describe('Agent', () => { + const TOKEN = 'AGENT_CLIENT_SERVICE'; + let app: INestApplication; + let client: ClientProxy; + + const agentService = { + getPublicDid: () => + Promise.resolve({ + id: 'test', + }), + }; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + mockConfigModule, + AgentModule, + ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]), + ], + }) + .overrideProvider(AgentService) + .useValue(agentService) + .compile(); + + app = moduleRef.createNestApplication(); + + app.connectMicroservice({ transport: Transport.NATS }); + + await app.startAllMicroservices(); + await app.init(); + + client = app.get(TOKEN); + await client.connect(); + }); + + it('info.publicDid', async () => { + const response$: Observable<unknown> = client.send('info.publicDid', {}); + const response = await firstValueFrom(response$); + expect(response).toMatchObject({ id: 'test' }); + }); + + afterAll(async () => { + await app.close(); + client.close(); + }); +}); diff --git a/apps/ssi-abstraction/test/connections.e2e-spec.ts b/apps/ssi-abstraction/test/connections.e2e-spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7490626e4b7660c3a8284ed792b05eb4476859e --- /dev/null +++ b/apps/ssi-abstraction/test/connections.e2e-spec.ts @@ -0,0 +1,51 @@ +import type { INestApplication } from '@nestjs/common'; +import type { ClientProxy } from '@nestjs/microservices'; + +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { Test } from '@nestjs/testing'; +import { firstValueFrom, type Observable } from 'rxjs'; + +import { AgentModule } from '../src/agent/agent.module.js'; +import { ConnectionsModule } from '../src/agent/connections/connections.module.js'; +import { mockConfigModule } from '../src/config/__tests__/mockConfig.js'; + +describe('Connections', () => { + const TOKEN = 'CONNECTIONS_CLIENT_SERVICE'; + let app: INestApplication; + let client: ClientProxy; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + mockConfigModule, + AgentModule, + ConnectionsModule, + ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]), + ], + }).compile(); + + app = moduleRef.createNestApplication(); + + app.connectMicroservice({ transport: Transport.NATS }); + + await app.startAllMicroservices(); + await app.init(); + + client = app.get(TOKEN); + await client.connect(); + }); + + it('didcomm.connections.getAll', async () => { + const response$: Observable<unknown> = client.send( + 'didcomm.connections.getAll', + {}, + ); + const response = await firstValueFrom(response$); + expect(response).toMatchObject([]); + }); + + afterAll(async () => { + await app.close(); + client.close(); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbea83af8011fab14e49d30b49dbe690f6473d30..c3d69660c77ee1c4e204f882876f8772b6e2ed06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,8 +36,8 @@ importers: specifier: ^5.0.1 version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.54.0)(prettier@3.1.0) eslint-plugin-workspaces: - specifier: ^0.10.0 - version: 0.10.0 + specifier: ^0.9.0 + version: 0.9.0 husky: specifier: ^8.0.0 version: 8.0.3 @@ -7869,10 +7869,10 @@ packages: synckit: 0.8.5 dev: true - /eslint-plugin-workspaces@0.10.0: - resolution: {integrity: sha512-H692yRZFczzzyde0Sq3nmRDlyywv6foYJnmsxO3slWImJdCf4g5D+gzdWeRpmfitgUsFZxXVJdvW4OS6yY4M9g==} + /eslint-plugin-workspaces@0.9.0: + resolution: {integrity: sha512-krMuZ+yZgzwv1oTBfz50oamNVPDIm7CDyot3i1GRKBqMD2oXAwnXHLQWH7ctpV8k6YVrkhcaZhuV9IJxD8OPAQ==} dependencies: - find-workspaces: 0.3.0 + find-workspaces: 0.2.0 dev: true /eslint-scope@5.1.1: @@ -8191,7 +8191,7 @@ packages: dependencies: chalk: 4.1.2 commander: 7.2.0 - fast-glob: 3.3.1 + fast-glob: 3.3.2 find-up: 5.0.0 fs-extra: 9.1.0 dev: false @@ -8608,8 +8608,8 @@ packages: semver-regex: 4.0.5 dev: true - /find-workspaces@0.3.0: - resolution: {integrity: sha512-sHdt3vbddcDuN0CYnKoG/b77jrOkSYPlxoM7ve7/vEvAd29XC7u/qE2zavRzJv4eD1sbTvDnRNZskdy/e0v83A==} + /find-workspaces@0.2.0: + resolution: {integrity: sha512-OTHryv88yjzwvbXHGi0+XRFu7Jqe5pFuIR2mhqdatDJQOBJd7MFJOPFJv4EbNo8n1BNM/13Y2KcyDpFQYf0ETw==} dependencies: fast-glob: 3.3.2 pkg-types: 1.0.3