From 4cf97f9676e6f1ef81d57fb4fd9ad8230a9c5f55 Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <berend@animo.id> Date: Thu, 18 Jan 2024 16:31:28 +0100 Subject: [PATCH] test(ssi): added e2e and unit tests for proofs Signed-off-by: Berend Sliedrecht <berend@animo.id> --- .../src/events/credentialOfferEvents.ts | 13 +- .../src/events/credentialRequestEvents.ts | 13 +- apps/shared/src/events/proofEvents.ts | 4 +- .../anoncredsCredentials.controller.ts | 24 ++-- .../anoncredsCredentials.service.ts | 8 +- .../anoncredsProofs.controller.spec.ts | 77 ++++++++++ .../anoncredsProofs.service.ts | 2 +- .../test/anoncredsProofs.e2e-spec.ts | 133 ++++++++++++++++++ 8 files changed, 252 insertions(+), 22 deletions(-) create mode 100644 apps/ssi-abstraction/src/agent/anoncredsProofs/__tests__/anoncredsProofs.controller.spec.ts create mode 100644 apps/ssi-abstraction/test/anoncredsProofs.e2e-spec.ts diff --git a/apps/shared/src/events/credentialOfferEvents.ts b/apps/shared/src/events/credentialOfferEvents.ts index d120f84..da38971 100644 --- a/apps/shared/src/events/credentialOfferEvents.ts +++ b/apps/shared/src/events/credentialOfferEvents.ts @@ -1,6 +1,11 @@ import type { BaseEventInput } from './baseEvents.js'; import type { AnonCredsCredentialOffer } from '@aries-framework/anoncreds'; +import { + CredentialExchangeRecord, + JsonTransformer, +} from '@aries-framework/core'; + import { BaseEvent } from './baseEvents.js'; export type EventAnonCredsCredentialOfferGetAllInput = BaseEventInput; @@ -11,7 +16,9 @@ export class EventAnonCredsCredentialOfferGetAll extends BaseEvent< public static token = 'anoncreds.credentialOffers.getAll'; public get instance() { - return this.data; + return this.data.map((d) => + JsonTransformer.fromJSON(d, CredentialExchangeRecord), + ); } public static fromEvent(e: EventAnonCredsCredentialOfferGetAll) { @@ -33,7 +40,9 @@ export class EventAnonCredsCredentialOfferGetById extends BaseEvent<AnonCredsCre public static token = 'anoncreds.credentialOffers.getById'; public get instance() { - return this.data; + return this.data + ? JsonTransformer.fromJSON(this.data, CredentialExchangeRecord) + : null; } public static fromEvent(e: EventAnonCredsCredentialOfferGetById) { diff --git a/apps/shared/src/events/credentialRequestEvents.ts b/apps/shared/src/events/credentialRequestEvents.ts index 213562f..f143033 100644 --- a/apps/shared/src/events/credentialRequestEvents.ts +++ b/apps/shared/src/events/credentialRequestEvents.ts @@ -1,6 +1,11 @@ import type { BaseEventInput } from './baseEvents.js'; import type { AnonCredsCredentialRequest } from '@aries-framework/anoncreds'; +import { + CredentialExchangeRecord, + JsonTransformer, +} from '@aries-framework/core'; + import { BaseEvent } from './baseEvents.js'; export type EventAnonCredsCredentialRequestGetAllInput = BaseEventInput; @@ -11,7 +16,9 @@ export class EventAnonCredsCredentialRequestGetAll extends BaseEvent< public static token = 'anoncreds.credentialRequests.getAll'; public get instance() { - return this.data; + return this.data.map((d) => + JsonTransformer.fromJSON(d, CredentialExchangeRecord), + ); } public static fromEvent(e: EventAnonCredsCredentialRequestGetAll) { @@ -33,7 +40,9 @@ export class EventAnonCredsCredentialRequestGetById extends BaseEvent<AnonCredsC public static token = 'anoncreds.credentialRequests.getById'; public get instance() { - return this.data; + return this.data + ? JsonTransformer.fromJSON(this.data, CredentialExchangeRecord) + : null; } public static fromEvent(e: EventAnonCredsCredentialRequestGetById) { diff --git a/apps/shared/src/events/proofEvents.ts b/apps/shared/src/events/proofEvents.ts index b40e17a..195777b 100644 --- a/apps/shared/src/events/proofEvents.ts +++ b/apps/shared/src/events/proofEvents.ts @@ -38,7 +38,9 @@ export class EventAnonCredsProofsGetById extends BaseEvent<ProofExchangeRecord | public static token = 'anoncreds.proofs.getById'; public get instance() { - return JsonTransformer.fromJSON(this.data, ProofExchangeRecord); + return this.data + ? JsonTransformer.fromJSON(this.data, ProofExchangeRecord) + : this.data; } public static fromEvent(e: EventAnonCredsProofsGetById) { diff --git a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts index 1cef75b..11a6d93 100644 --- a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts +++ b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts @@ -1,10 +1,10 @@ import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { - EventDidcommAnonCredsCredentialsGetAll, - EventDidcommAnonCredsCredentialsGetAllInput, - EventDidcommAnonCredsCredentialsGetById, - EventDidcommAnonCredsCredentialsGetByIdInput, + EventAnonCredsCredentialsGetAll, + EventAnonCredsCredentialsGetAllInput, + EventAnonCredsCredentialsGetById, + EventAnonCredsCredentialsGetByIdInput, EventDidcommAnonCredsCredentialsOffer, EventDidcommAnonCredsCredentialsOfferInput, EventDidcommAnonCredsCredentialsOfferToSelfInput, @@ -17,21 +17,21 @@ import { AnonCredsCredentialsService } from './anoncredsCredentials.service.js'; export class AnonCredsCredentialsController { public constructor(private credentialsService: AnonCredsCredentialsService) {} - @MessagePattern(EventDidcommAnonCredsCredentialsGetAll.token) + @MessagePattern(EventAnonCredsCredentialsGetAll.token) public async getAll( - options: EventDidcommAnonCredsCredentialsGetAllInput, - ): Promise<EventDidcommAnonCredsCredentialsGetAll> { - return new EventDidcommAnonCredsCredentialsGetAll( + options: EventAnonCredsCredentialsGetAllInput, + ): Promise<EventAnonCredsCredentialsGetAll> { + return new EventAnonCredsCredentialsGetAll( await this.credentialsService.getAll(options), options.tenantId, ); } - @MessagePattern(EventDidcommAnonCredsCredentialsGetById.token) + @MessagePattern(EventAnonCredsCredentialsGetById.token) public async getById( - options: EventDidcommAnonCredsCredentialsGetByIdInput, - ): Promise<EventDidcommAnonCredsCredentialsGetById> { - return new EventDidcommAnonCredsCredentialsGetById( + options: EventAnonCredsCredentialsGetByIdInput, + ): Promise<EventAnonCredsCredentialsGetById> { + return new EventAnonCredsCredentialsGetById( await this.credentialsService.getById(options), options.tenantId, ); diff --git a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts index 3eb52b6..6cb0cb7 100644 --- a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts +++ b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts @@ -1,6 +1,6 @@ import type { - EventDidcommAnonCredsCredentialsGetAllInput, - EventDidcommAnonCredsCredentialsGetByIdInput, + EventAnonCredsCredentialsGetAllInput, + EventAnonCredsCredentialsGetByIdInput, EventDidcommAnonCredsCredentialsOfferInput, EventDidcommAnonCredsCredentialsOfferToSelfInput, } from '@ocm/shared'; @@ -20,7 +20,7 @@ export class AnonCredsCredentialsService { public async getAll({ tenantId, - }: EventDidcommAnonCredsCredentialsGetAllInput): Promise< + }: EventAnonCredsCredentialsGetAllInput): Promise< Array<CredentialExchangeRecord> > { return this.withTenantService.invoke(tenantId, (t) => @@ -31,7 +31,7 @@ export class AnonCredsCredentialsService { public async getById({ tenantId, credentialRecordId, - }: EventDidcommAnonCredsCredentialsGetByIdInput): Promise<CredentialExchangeRecord | null> { + }: EventAnonCredsCredentialsGetByIdInput): Promise<CredentialExchangeRecord | null> { return this.withTenantService.invoke(tenantId, (t) => t.credentials.findById(credentialRecordId), ); diff --git a/apps/ssi-abstraction/src/agent/anoncredsProofs/__tests__/anoncredsProofs.controller.spec.ts b/apps/ssi-abstraction/src/agent/anoncredsProofs/__tests__/anoncredsProofs.controller.spec.ts new file mode 100644 index 0000000..91eb605 --- /dev/null +++ b/apps/ssi-abstraction/src/agent/anoncredsProofs/__tests__/anoncredsProofs.controller.spec.ts @@ -0,0 +1,77 @@ +import { ProofExchangeRecord, ProofState } from '@aries-framework/core'; +import { Test } from '@nestjs/testing'; + +import { mockConfigModule } from '../../../config/__tests__/mockConfig.js'; +import { AgentModule } from '../../agent.module.js'; +import { AnonCredsProofsController } from '../anoncredsProofs.controller.js'; +import { AnonCredsProofsService } from '../anoncredsProofs.service.js'; + +describe('AnonCredsProofsController', () => { + let proofsController: AnonCredsProofsController; + let proofsService: AnonCredsProofsService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [mockConfigModule(), AgentModule], + controllers: [AnonCredsProofsController], + providers: [AnonCredsProofsService], + }).compile(); + + proofsService = moduleRef.get(AnonCredsProofsService); + proofsController = moduleRef.get(AnonCredsProofsController); + }); + + it('get all', async () => { + const result: Array<ProofExchangeRecord> = []; + jest.spyOn(proofsService, 'getAll').mockResolvedValue(result); + + const event = await proofsController.getAll({ + tenantId: 'some-id', + }); + + expect(event.data).toStrictEqual(result); + }); + + it('get by id', async () => { + const result: ProofExchangeRecord | null = null; + jest.spyOn(proofsService, 'getById').mockResolvedValue(result); + + const event = await proofsController.getById({ + tenantId: 'some-id', + proofRecordId: 'some-id', + }); + + expect(event.data).toStrictEqual(result); + }); + + it('request', async () => { + const result = new ProofExchangeRecord({ + state: ProofState.Done, + threadId: 'some-id', + protocolVersion: 'v2', + }); + jest.spyOn(proofsService, 'request').mockResolvedValue(result); + + const event = await proofsController.request({ + tenantId: 'some-id', + connectionId: 'some-id', + name: 'My New Proof Request', + requestedAttributes: { + identity: { + names: ['name'], + restrictions: [{ issuer_id: 'did:web:government.org' }], + }, + }, + requestedPredicates: { + 'age > 18': { + name: 'age', + restrictions: [{ issuer_id: 'did:web:government.org' }], + predicateType: '>', + predicateValue: 18, + }, + }, + }); + + expect(event.data).toStrictEqual(result); + }); +}); diff --git a/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.service.ts b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.service.ts index 4512f28..6de7633 100644 --- a/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.service.ts +++ b/apps/ssi-abstraction/src/agent/anoncredsProofs/anoncredsProofs.service.ts @@ -32,7 +32,7 @@ export class AnonCredsProofsService { EventAnonCredsProofsGetById['data'] > { return this.withTenantService.invoke(tenantId, (t) => - t.proofs.getById(proofRecordId), + t.proofs.findById(proofRecordId), ); } diff --git a/apps/ssi-abstraction/test/anoncredsProofs.e2e-spec.ts b/apps/ssi-abstraction/test/anoncredsProofs.e2e-spec.ts new file mode 100644 index 0000000..fe6c5f7 --- /dev/null +++ b/apps/ssi-abstraction/test/anoncredsProofs.e2e-spec.ts @@ -0,0 +1,133 @@ +import type { INestApplication } from '@nestjs/common'; +import type { ClientProxy } from '@nestjs/microservices'; +import type { + EventAnonCredsProofsGetAllInput, + EventAnonCredsProofsGetByIdInput, + EventDidcommAnonCredsProofsRequestInput, +} from '@ocm/shared'; + +import { ProofState } from '@aries-framework/core'; +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { Test } from '@nestjs/testing'; +import { + EventAnonCredsProofsGetAll, + EventAnonCredsProofsGetById, + EventDidcommAnonCredsProofsRequest, +} from '@ocm/shared'; +import { firstValueFrom } from 'rxjs'; + +import { AgentModule } from '../src/agent/agent.module.js'; +import { AnonCredsProofsModule } from '../src/agent/anoncredsProofs/anoncredsProofs.module.js'; +import { ConnectionsModule } from '../src/agent/connections/connections.module.js'; +import { ConnectionsService } from '../src/agent/connections/connections.service.js'; +import { DidsModule } from '../src/agent/dids/dids.module.js'; +import { TenantsModule } from '../src/agent/tenants/tenants.module.js'; +import { TenantsService } from '../src/agent/tenants/tenants.service.js'; +import { mockConfigModule } from '../src/config/__tests__/mockConfig.js'; + +describe('Proofs', () => { + const TOKEN = 'PROOFS_CLIENT_SERVICE'; + let app: INestApplication; + let client: ClientProxy; + let tenantId: string; + + let connectionId: string; + let credentialDefinitionId: string; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + mockConfigModule(3004, true), + AgentModule, + ConnectionsModule, + AnonCredsProofsModule, + TenantsModule, + DidsModule, + 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(); + + const tenantsService = app.get(TenantsService); + const { id: tId } = await tenantsService.create(TOKEN); + tenantId = tId; + + const connectionsService = app.get(ConnectionsService); + const { id } = await connectionsService.createConnectionWithSelf({ + tenantId, + }); + connectionId = id; + }); + + afterAll(async () => { + await app.close(); + client.close(); + }); + + it(EventAnonCredsProofsGetAll.token, async () => { + const response$ = client.send< + EventAnonCredsProofsGetAll, + EventAnonCredsProofsGetAllInput + >(EventAnonCredsProofsGetAll.token, { tenantId }); + const response = await firstValueFrom(response$); + const eventInstance = EventAnonCredsProofsGetAll.fromEvent(response); + + expect(eventInstance.instance).toEqual(expect.arrayContaining([])); + }); + + it(EventAnonCredsProofsGetById.token, async () => { + const response$ = client.send< + EventAnonCredsProofsGetById, + EventAnonCredsProofsGetByIdInput + >(EventAnonCredsProofsGetById.token, { + tenantId, + proofRecordId: 'some-id', + }); + const response = await firstValueFrom(response$); + const eventInstance = EventAnonCredsProofsGetById.fromEvent(response); + + expect(eventInstance.instance).toEqual(null); + }); + + it(EventDidcommAnonCredsProofsRequest.token, async () => { + const response$ = client.send< + EventDidcommAnonCredsProofsRequest, + EventDidcommAnonCredsProofsRequestInput + >(EventDidcommAnonCredsProofsRequest.token, { + tenantId, + name: 'My Test Proof Request', + connectionId, + requestedAttributes: { + Identity: { + names: ['Name'], + restrictions: [{ cred_def_id: credentialDefinitionId }], + }, + }, + requestedPredicates: { + 'Age > 21': { + name: 'Age', + restrictions: [{ cred_def_id: credentialDefinitionId }], + predicateType: '>', + predicateValue: 21, + }, + }, + }); + + const response = await firstValueFrom(response$); + const eventInstance = + EventDidcommAnonCredsProofsRequest.fromEvent(response); + + expect(eventInstance.instance).toMatchObject({ + state: ProofState.RequestSent, + }); + }); +}); -- GitLab