Skip to content

Commit

Permalink
Merge pull request #2 from SamagraX-RCW/tests
Browse files Browse the repository at this point in the history
feat: add more tests
  • Loading branch information
tushar5526 authored Jul 18, 2023
2 parents 072742a + 9efd4e1 commit 80ed883
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 69 deletions.
14 changes: 14 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
DATABASE_URL=""
SCHEMA_BASE_URL=""

# Auth related vars
JWKS_URI=""
ENABLE_AUTH=false

# Core Service Vars
IDENTITY_BASE_URL= # URL of the identity service to facilitate DID creation

# Service VARS
SCHEMA_BASE_URL=

CREDENTIAL_SERVICE_BASE_URL=""
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ RUN yarn build

FROM node:16
RUN apt-get update \
&& apt-get install -y wkhtmltopdf
&& apt-get install -y wkhtmltopdf=0.12.5-1

WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/package*.json ./
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile.test
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ RUN yarn

FROM node:16 as build
RUN apt-get update \
&& apt-get install -y wkhtmltopdf
&& apt-get install -y wkhtmltopdf=0.12.5-1

WORKDIR /app
COPY prisma ./prisma/
COPY --from=install /app/node_modules ./node_modules
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ test:
bash setup_vault_gha.sh docker-compose-test.yml vault-test
docker-compose -f docker-compose-test.yml up -d identity-test
docker-compose -f docker-compose-test.yml up -d schema-test
docker-compose -f docker-compose-test.yml up --abort-on-container-exit credential-test
docker-compose -f docker-compose-test.yml up --build --abort-on-container-exit credential-test
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"start:prod": "node dist/main",
"start:migrate:prod": "npx prisma migrate deploy && node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test:migrate": "npx prisma migrate deploy && jest --coverage",
"test:migrate": "npx prisma migrate deploy && jest --coverage && jest --config ./test/jest-e2e.json",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
Expand Down
12 changes: 4 additions & 8 deletions src/credentials/credentials.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,20 @@ export class CredentialsController {
description: 'Returns a credential with the given id',
})
getCredentialById(@Param('id') id: string, @Req() req: Request) {
const accept: string = req.headers['accept'];
const accept: string = req.headers['accept']?.trim() || 'application/json';
const templateId: string = req.headers['templateid'] as string;

if (!templateId && accept.trim() !== 'application/json')
if (!templateId && accept !== 'application/json')
throw new BadRequestException('Template id is required');
else if (!templateId && accept.trim() === 'application/json')
else if (!templateId && accept === 'application/json')
return this.credentialsService.getCredentialById(
id,
templateId,
RENDER_OUTPUT.JSON
);

let output = RENDER_OUTPUT.JSON;
switch (accept.trim()) {
switch (accept) {
case 'application/json':
output = RENDER_OUTPUT.JSON;
break;
Expand Down Expand Up @@ -116,8 +116,4 @@ export class CredentialsController {
return this.credentialsService.verifyCredential(credId);
}

@Get('schema/:id')
async getSchemaByCredId(@Param('id') id: string) {
return this.credentialsService.getSchemaByCredId(id);
}
}
9 changes: 9 additions & 0 deletions src/credentials/credentials.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,12 @@ export const getCredentialByIdSchema = {
'type',
],
};

export const generateRenderingTemplatePayload = (schemaId, schemaVersion) => {
return {
schemaId: schemaId,
schemaVersion: schemaVersion,
template: "<html lang='en'><head><meta charset='UTF-8' /><meta http-equiv='X-UA-Compatible' content='IE=edge' /><meta name='viewport' content='width=device-width, initial-scale=1.0' /><script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js'></script><link rel='stylesheet' href='style.css' /><title>Certificate</title></head><body><div class='outer-border'><div class='inner-dotted-border'><img class='logo' src='https://www.vid.no/site/assets/files/17244/christ-deemed-to-be-university-vid.png' alt='' /><br /><span class='certification'>CERTIFICATE OF COMPLETION</span><br /><br /><span class='certify'><i>is hereby awarded to</i></span><br /><br /><span class='name'><b>Daniel Vitorrie</b></span><br /><br /><span class='certify'><i>for successfully completing the</i></span><br /><br /><span class='fs-30 diploma'>diploma in Java Developer</span><br /><br /><span class='fs-20 thank'>Thank you for demonstrating the type of character and integrity that inspire others</span><br /><div class='footer'><div class='date'><span class='certify'><i>Awarded: </i></span><br /><span class='fs-20'> xxxxxx</span></div><div class='qr'>{{grade}}, {{programme}}, {{certifyingInstitute}}, {{evaluatingInstitute}}/></div><span class='sign'>Dean, Christ Univeristy</span></div></div></div></body><style>*{margin:1px;padding:0;box-sizing:border-box}.outer-border{width:80vw;padding:10px;text-align:center;border:10px solid #252F50;font-family:'Lato', sans-serif;margin:auto}.inner-dotted-border{padding:10px;text-align:center;border:2px solid #252F50}.logo{width:75px}.certification{font-size:50px;font-weight:bold;color:#252F50;font-family:\"Times New Roman\", Times, serif}.certify{font-size:20px;color:#252F50}.diploma{color:#252F50;font-family:\"Lucida Handwriting\", \"Comic Sans\", cursive}.name{font-size:30px;color:#252F50;border-bottom:1px solid;font-family:\"Lucida Handwriting\", \"Comic Sans\", cursive}.thank{color:#252F50}.fs-30{font-size:30px}.fs-20{font-size:20px}.footer{display:flex;justify-content:space-between;margin:0 4rem}.date{display:flex;align-self:flex-end}.date>.fs-20{font-family:\"Lucida Handwriting\", \"Comic Sans\", cursive}.qr{margin-top:1.25rem;display:flex;justify-content:end;margin-left:0.75rem;margin-right:0.75rem}.sign{border-top:1px solid;align-self:flex-end}</style></html>",
type: "Handlebar"
}
}
62 changes: 53 additions & 9 deletions src/credentials/credentials.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import { PrismaClient } from '@prisma/client';
import {
generateCredentialRequestPayload,
generateCredentialSchemaTestBody,
generateTestDIDBody,
getCredentialByIdSchema,
issueCredentialReturnTypeSchema,
generateRenderingTemplatePayload,
} from './credentials.fixtures';
import { schemaHasRules } from 'ajv/dist/compile/util';
import { RENDER_OUTPUT } from './enums/renderOutput.enum';

// setup ajv
const ajv = new Ajv2019({ strictTuples: false });
Expand Down Expand Up @@ -87,13 +87,46 @@ describe('CredentialsService', () => {
expect(validate(newCred)).toBe(true);
});

it('should get a credential', async () => {
it('should get a credential in JSON', async () => {
const newCred: any = await service.issueCredential(sampleCredReqPayload);
const cred = await service.getCredentialById(newCred.credential?.id);
UnsignedVCValidator.parse(cred);
expect(getCredReqValidate(cred)).toBe(true);
});

it('should get a credential in QR', async () => {
const newCred: any = await service.issueCredential(sampleCredReqPayload);
const dataURL = await service.getCredentialById(newCred.credential?.id, null, RENDER_OUTPUT.QR);
expect(dataURL).toBeDefined(); // Assert that the dataURL is defined
expect(dataURL).toContain('data:image/png;base64,');
});


it('should get a credential in HTML', async () => {
const newCred: any = await service.issueCredential(sampleCredReqPayload);
const templatePayload = generateRenderingTemplatePayload(newCred.credentialSchemaId, "1.0.0")
const template = await httpSerivce.axiosRef.post(`${process.env.SCHEMA_BASE_URL}/template`, templatePayload);
const cred = await service.getCredentialById(newCred.credential?.id, template.data.template.templateId, RENDER_OUTPUT.HTML);
expect(cred).toContain('</html>')
expect(cred).toContain('IIIT Sonepat, NIT Kurukshetra')
expect(cred).toBeDefined()
});

it('should get a credential in PDF', async () => {
const newCred: any = await service.issueCredential(sampleCredReqPayload);
const templatePayload = generateRenderingTemplatePayload(newCred.credentialSchemaId, "1.0.0")
const template = await httpSerivce.axiosRef.post(`${process.env.SCHEMA_BASE_URL}/template`, templatePayload);
const cred = await service.getCredentialById(newCred.credential?.id, template.data.template.templateId, RENDER_OUTPUT.PDF);
expect(cred).toBeDefined()
});


it('should get a credential in STRING', async () => {
const newCred: any = await service.issueCredential(sampleCredReqPayload);
const cred = await service.getCredentialById(newCred.credential?.id, null, RENDER_OUTPUT.STRING);
expect(cred).toBeDefined()
});

it('should throw because no credential is present to be searched by ID', async () => {
await expect(service.getCredentialById('did:ulp:123')).rejects.toThrow();
});
Expand All @@ -102,6 +135,13 @@ describe('CredentialsService', () => {
await expect(service.verifyCredential('did:ulp:123')).rejects.toThrow();
});

it('should verify an issued credential', async () => {
const newCred = await service.issueCredential(sampleCredReqPayload);
const res = {checks: [{active: "OK", expired: "NOK", proof: "OK", revoked: "OK"}], status: "ISSUED"}
const verifyRes = await service.verifyCredential(newCred.credential['id']);
expect(verifyRes).toEqual(res);
});

it('should say revoked', async () => {
const newCred = await service.issueCredential(sampleCredReqPayload);
expect(
Expand All @@ -122,7 +162,6 @@ describe('CredentialsService', () => {
});

it('should return array of creds based on issuer', async () => {
try {
const newCred = await service.issueCredential(sampleCredReqPayload);
expect(
await service.getCredentialsBySubjectOrIssuer({
Expand All @@ -131,10 +170,15 @@ describe('CredentialsService', () => {
},
})
).toBeInstanceOf(Array);
} catch (e) {
expect(e.message).toBe(
'No credentials found for the given subject or issuer'
);
}
});


it('should return array of creds based on issuer', async () => {
const newCred = await service.issueCredential(sampleCredReqPayload);
expect(
await service.getCredentials(['tag1'])
).toBeInstanceOf(Array);
const res = await service.getCredentials(['tag1'], 2, 1000)
expect(res.length).toEqual(0)
});
});
17 changes: 0 additions & 17 deletions src/credentials/credentials.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { HttpService } from '@nestjs/axios';
import {
BadRequestException,
Injectable,
Expand Down Expand Up @@ -320,20 +319,4 @@ export class CredentialsService {
return { id: cred.id, ...signed };
});
}

async getSchemaByCredId(credId: string) {
const schema = await this.prisma.verifiableCredentials.findUnique({
where: {
id: credId,
},
select: {
credential_schema: true,
},
});
if (!schema) {
this.logger.error('Credential not found');
throw new NotFoundException('Credential not found');
}
return schema;
}
}
69 changes: 38 additions & 31 deletions test/app.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,54 @@ import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
import { HttpModule, HttpService } from '@nestjs/axios';
import { IdentityUtilsService } from '../src/credentials/utils/identity.utils.service';
import {
generateCredentialRequestPayload,
generateCredentialSchemaTestBody,
} from '../src/credentials/credentials.fixtures';

describe('AppController (e2e)', () => {
let app: INestApplication;
let id: any;
const sampleCredReqPayload: any = {
credential: {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://www.w3.org/2018/credentials/examples/v1',
],
type: ['VerifiableCredential', 'UniversityDegreeCredential'],
issuer: 'did:ulp:76687f0a-e5bb-4176-86e1-28d5a76fdfa8',
issuanceDate: '2023-02-06T11:56:27.259Z',
expirationDate: '2023-02-08T11:56:27.259Z',
credentialSubject: {
id: 'did:ulp:b4a191af-d86e-453c-9d0e-dd4771067235',
grade: '9.23',
programme: 'B.Tech',
certifyingInstitute: 'IIIT Sonepat',
evaluatingInstitute: 'NIT Kurukshetra',
},
},
credentialSchemaId: 'did:ulpschema:c9cc0f03-4f94-4f44-9bcd-b24a86596fa2',
tags: ['tag1', 'tag2', 'tag3'],
};

let sampleCredReqPayload: any;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
imports: [AppModule, HttpModule],
}).compile();

app = moduleFixture.createNestApplication();
await app.init();
});
let httpSerivce = moduleFixture.get<HttpService>(HttpService);
let identityUtilsService =
moduleFixture.get<IdentityUtilsService>(IdentityUtilsService);

it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
let issuerDID = (
await identityUtilsService.generateDID([
'VerifiableCredentialTESTINGIssuer',
])
)[0].id;

let subjectDID = (
await identityUtilsService.generateDID([
'VerifiableCredentialTESTINGIssuer',
])
)[0].id;

const schemaPayload = generateCredentialSchemaTestBody();
schemaPayload.schema.author = issuerDID;
const schema = await httpSerivce.axiosRef.post(
`${process.env.SCHEMA_BASE_URL}/credential-schema`,
schemaPayload
);
let credentialSchemaID = schema.data.schema.id;
sampleCredReqPayload = generateCredentialRequestPayload(
issuerDID,
subjectDID,
credentialSchemaID,
schema.data.schema.version
);

await app.init();
});

it('/credentials/issue (POST)', () => {
Expand All @@ -59,7 +67,6 @@ describe('AppController (e2e)', () => {
return request(app.getHttpServer())
.get(`/credentials/${id}`)
.expect(200)
.expect(sampleCredReqPayload.credential);
});

it('/credentials/:id/verify (GET)', () => {
Expand Down

0 comments on commit 80ed883

Please sign in to comment.