From 232652b73bef34605e86da5ef8530fc7994296bf Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <berend@animo.id> Date: Thu, 23 Nov 2023 13:47:24 +0100 Subject: [PATCH] test(ssi): added integration and unit tests Signed-off-by: Berend Sliedrecht <berend@animo.id> --- .../agent/__tests__/agent.controller.spec.ts | 32 ++++++++++ .../src/agent/agent.controller.ts | 4 +- .../src/agent/agent.service.ts | 23 ++++--- .../agent/connection/connection.controller.ts | 14 ----- .../src/agent/connection/connection.module.ts | 13 ---- .../__tests__/connections.controller.spec.ts | 35 +++++++++++ .../connections/connections.controller.ts | 14 +++++ .../agent/connections/connections.module.ts | 13 ++++ .../connections.service.ts} | 2 +- apps/ssi-abstraction/src/app.module.ts | 4 +- .../src/config/__tests__/mockConfig.ts | 61 +++++++++++++++++++ apps/ssi-abstraction/src/config/config.ts | 6 +- apps/ssi-abstraction/test/agent.e2e-spec.ts | 57 +++++++++++++++++ .../test/connections.e2e-spec.ts | 51 ++++++++++++++++ apps/ssi-abstraction/test/didComm.e2e-spec.ts | 33 ---------- apps/ssi-abstraction/tsconfig.build.json | 2 +- apps/ssi-abstraction/tsconfig.production.json | 2 +- 17 files changed, 282 insertions(+), 84 deletions(-) create mode 100644 apps/ssi-abstraction/src/agent/__tests__/agent.controller.spec.ts delete mode 100644 apps/ssi-abstraction/src/agent/connection/connection.controller.ts delete mode 100644 apps/ssi-abstraction/src/agent/connection/connection.module.ts create mode 100644 apps/ssi-abstraction/src/agent/connections/__tests__/connections.controller.spec.ts create mode 100644 apps/ssi-abstraction/src/agent/connections/connections.controller.ts create mode 100644 apps/ssi-abstraction/src/agent/connections/connections.module.ts rename apps/ssi-abstraction/src/agent/{connection/connection.service.ts => connections/connections.service.ts} (93%) create mode 100644 apps/ssi-abstraction/src/config/__tests__/mockConfig.ts create mode 100644 apps/ssi-abstraction/test/agent.e2e-spec.ts create mode 100644 apps/ssi-abstraction/test/connections.e2e-spec.ts delete mode 100644 apps/ssi-abstraction/test/didComm.e2e-spec.ts 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 0000000..5d24ea1 --- /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 4a07c5b..42fcd10 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 6c3855e..0cc5c29 100644 --- a/apps/ssi-abstraction/src/agent/agent.service.ts +++ b/apps/ssi-abstraction/src/agent/agent.service.ts @@ -1,9 +1,6 @@ -import type { - LedgerIds} from './utils/ledgerConfig.js'; -import type { - InitConfig} from '@aries-framework/core'; -import type { - IndyVdrPoolConfig} from '@aries-framework/indy-vdr'; +import type { LedgerIds } from './utils/ledgerConfig.js'; +import type { InitConfig } from '@aries-framework/core'; +import type { IndyVdrPoolConfig } from '@aries-framework/indy-vdr'; import { AnonCredsModule } from '@aries-framework/anoncreds'; import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs'; @@ -38,13 +35,9 @@ import { ConfigService } from '@nestjs/config'; import { logger } from '../globalUtils/logger.js'; import { registerPublicDids } from './ledger/register.js'; -import { - ledgerNamespaces, - LEDGER_GENESIS, -} from './utils/ledgerConfig.js'; +import { ledgerNamespaces, LEDGER_GENESIS } from './utils/ledgerConfig.js'; import { AgentLogger } from './utils/logger.js'; - export type AppAgent = Agent<AgentService['modules']>; @Injectable() @@ -86,7 +79,7 @@ export class AgentService { key: walletKey, }, endpoints, - logger: new AgentLogger(LogLevel.warn), + logger: new AgentLogger(LogLevel.off), }; } @@ -183,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 83df332..0000000 --- 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 4deaa82..0000000 --- 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 0000000..f516874 --- /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 0000000..c2aa973 --- /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 0000000..edee4f4 --- /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 0e4c7ed..5b8c7e8 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 d7c2484..6cfce4e 100644 --- a/apps/ssi-abstraction/src/app.module.ts +++ b/apps/ssi-abstraction/src/app.module.ts @@ -3,7 +3,7 @@ import { ConfigModule } from '@nestjs/config'; import { TerminusModule } from '@nestjs/terminus'; 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'; import { HealthController } from './health/health.controller.js'; @@ -17,7 +17,7 @@ import { HealthController } from './health/health.controller.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 0000000..c12689f --- /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 102dd75..c7dac95 100644 --- a/apps/ssi-abstraction/src/config/config.ts +++ b/apps/ssi-abstraction/src/config/config.ts @@ -1,6 +1,6 @@ import type { 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 || '', @@ -45,6 +44,5 @@ export const config = (): Config => ({ autoAcceptConnection: process.env.AGENT_AUTO_ACCEPT_CONNECTION === 'true', autoAcceptCredential: process.env .AGENT_AUTO_ACCEPT_CREDENTIAL as AutoAcceptCredential, - 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 0000000..f5ec1eb --- /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 0000000..d749062 --- /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/apps/ssi-abstraction/test/didComm.e2e-spec.ts b/apps/ssi-abstraction/test/didComm.e2e-spec.ts deleted file mode 100644 index 28e77ba..0000000 --- a/apps/ssi-abstraction/test/didComm.e2e-spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { INestApplication } from '@nestjs/common'; -import type { TestingModule } from '@nestjs/testing'; - -import { Test } from '@nestjs/testing'; -import request from 'supertest'; - -import { AppModule } from '../src/app.module.js'; - -describe('DidCommController (e2e)', () => { - let app: INestApplication; - - beforeAll(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - afterAll(async () => { - await app.close(); - }); - - it('/v1/agent/connections/createConnection (POST)', async () => { - await request(app.getHttpServer()) - .post('v1/agent/connections/createConnection') - .expect(200); - }); - - it('/v1/agent/info (GET)', async () => { - await request(app.getHttpServer()).post('v1/agent/info').expect(200); - }); -}); diff --git a/apps/ssi-abstraction/tsconfig.build.json b/apps/ssi-abstraction/tsconfig.build.json index b7dacb2..197273c 100644 --- a/apps/ssi-abstraction/tsconfig.build.json +++ b/apps/ssi-abstraction/tsconfig.build.json @@ -10,5 +10,5 @@ "outDir": "./dist", "rootDir": "./src" }, - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] + "exclude": ["node_modules", "test", "dist", "__tests__", "**/*.spec.ts"] } diff --git a/apps/ssi-abstraction/tsconfig.production.json b/apps/ssi-abstraction/tsconfig.production.json index 0f476e0..be97749 100644 --- a/apps/ssi-abstraction/tsconfig.production.json +++ b/apps/ssi-abstraction/tsconfig.production.json @@ -10,5 +10,5 @@ "outDir": "./dist", "rootDir": "./src" }, - "exclude": ["node_modules", "dist", "**/*spec.ts"] + "exclude": ["node_modules", "dist", "__tests__", "**/*.spec.ts"] } -- GitLab