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