Skip to content
This repository has been archived by the owner on Jan 10, 2024. It is now read-only.

Commit

Permalink
#9 added repository and router unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
natebolam committed Oct 14, 2023
1 parent 17df8f8 commit dbdacda
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 49 deletions.
2 changes: 2 additions & 0 deletions api/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
cd api
npx lint-staged
5 changes: 5 additions & 0 deletions api/src/domain/interfaces/use-cases/deployment/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { type Deployment } from "@src/domain/entities/deployment";

export interface ICreateUseCase {
execute: (deployment: Deployment) => Promise<Deployment>;
}
15 changes: 15 additions & 0 deletions api/src/domain/use-cases/deployment/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Deployment } from "@src/domain/entities/deployment";
import { IDeploymentRepository } from "@src/domain/interfaces/repositories/deployment-repository";
import { ICreateUseCase } from "@src/domain/interfaces/use-cases/deployment/create";

export class CreateDeployment implements ICreateUseCase {
deploymentRepository: IDeploymentRepository;
constructor(deploymentRepository: IDeploymentRepository) {
this.deploymentRepository = deploymentRepository;
}

async execute(deployment: Deployment): Promise<Deployment> {
const result = await this.deploymentRepository.create(deployment);
return result;
}
}
2 changes: 1 addition & 1 deletion api/src/domain/use-cases/deployment/list-by-owner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Deployment } from "@src/domain/entities/deployment";
import { IDeploymentRepository } from "@src/domain/interfaces/repositories/deployment-repository";
import { IListByOwnerUseCase } from "@src/domain/interfaces/use-cases/deployment/list-by-owner";

export class ListByOwner implements IListByOwnerUseCase {
export class ListDeploymentsByOwner implements IListByOwnerUseCase {
deploymentRepository: IDeploymentRepository;
constructor(deploymentRepository: IDeploymentRepository) {
this.deploymentRepository = deploymentRepository;
Expand Down
6 changes: 4 additions & 2 deletions api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import server from "./server";
import DeploymentRouter from "./routers/deployment-router";
import { ListDeployments } from "./domain/use-cases/deployment/list";
import { ListByOwner } from "./domain/use-cases/deployment/list-by-owner";
import { ListDeploymentsByOwner } from "./domain/use-cases/deployment/list-by-owner";
import { CreateDeployment } from "./domain/use-cases/deployment/create";
import { DeploymentRepositoryImpl } from "./domain/repositories/deployment-repository";
import { AkashApiDeploymentDataSource } from "./data/data-sources/akash/akash-deployment-data-source";

Expand All @@ -14,7 +15,8 @@ import { AkashApiDeploymentDataSource } from "./data/data-sources/akash/akash-de

const deploymentMiddleWare = DeploymentRouter(
new ListDeployments(repo),
new ListByOwner(repo)
new ListDeploymentsByOwner(repo),
new CreateDeployment(repo)
);

server.use("/deployment", deploymentMiddleWare);
Expand Down
14 changes: 13 additions & 1 deletion api/src/routers/deployment-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import express from "express";
import asyncHandler from "express-async-handler";
import { type IListUseCase } from "@src/domain/interfaces/use-cases/deployment/list";
import { type IListByOwnerUseCase } from "@src/domain/interfaces/use-cases/deployment/list-by-owner";
import { type ICreateUseCase } from "@src/domain/interfaces/use-cases/deployment/create";

export const apiRouter = express.Router();

export default function DeploymentRouter(
listUseCase: IListUseCase,
listByOwnerUseCase: IListByOwnerUseCase
listByOwnerUseCase: IListByOwnerUseCase,
createUseCase: ICreateUseCase
): express.Router {
const router = express.Router();

Expand Down Expand Up @@ -35,5 +37,15 @@ export default function DeploymentRouter(
})
);

router.post("/", async (req, res) => {
try {
const deployment = await createUseCase.execute(req.body);
res.statusCode = 201;
res.send(deployment);
} catch (err) {
res.status(500).send({ message: "Error saving deployment", error: err });
}
});

return router;
}
71 changes: 41 additions & 30 deletions api/tests/domain/repositories/deployment-repository.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
import { IDeploymentDataSource } from "../../../src/data/interfaces/data-sources/deployment-data-source";
import AkashQuery from "@akashnetwork/akashjs/build/protobuf/akash/deployment/v1beta3/query";
import AkashDeployment from "@akashnetwork/akashjs/build/protobuf/akash/deployment/v1beta3/deployment";
//import AkashDeployment from "@akashnetwork/akashjs/build/protobuf/akash/deployment/v1beta3/deployment";
import { MsgCreateDeploymentResponse } from "@akashnetwork/akashjs/build/protobuf/akash/deployment/v1beta3/deploymentmsg";
import { IDeploymentRepository } from "../../../src/domain/interfaces/repositories/deployment-repository";
import { DeploymentRepositoryImpl } from "../../../src/domain/repositories/deployment-repository";
import Long from "long";
import { Deployment } from "@src/domain/entities/deployment";
import { base64FromBytes } from "../../../../shared/utils/encode";

const deploymentOwner = "akash1xq5s8qmhvsvxvz8v5s3s6z4j0n3x5q9q4qz4q4";
const expectedDatasourceData = [
{
$type: "akash.deployment.v1beta3.QueryDeploymentResponse" as const,
deployment: {
$type: "akash.deployment.v1beta3.Deployment" as const,
deploymentId: {
$type: "akash.deployment.v1beta3.DeploymentID" as const,
owner: deploymentOwner,
dseq: Long.ZERO
},
state: 0 as number,
version: new Uint8Array(),
createdAt: Long.ZERO
},
groups: [],
escrowAccount: undefined
}
];
const expectedRepositoryData: Deployment[] = [
{
owner: deploymentOwner,
dseq: Long.ZERO.toNumber(),
state: 0,
version: base64FromBytes(new Uint8Array()),
createdAt: Long.ZERO.toNumber()
}
];

class MockDeploymentDataSource implements IDeploymentDataSource {
create(): Promise<MsgCreateDeploymentResponse> {
throw new Error(`Method not implemented.`);
Expand All @@ -34,41 +63,23 @@ describe("Deployment Repository", () => {
);
});

describe("listDeployments", () => {
describe("listAllDeployments", () => {
test("should return data", async () => {
console.log(AkashQuery, AkashDeployment);
const expectedDatasourceData = [
{
$type: "akash.deployment.v1beta3.QueryDeploymentResponse" as const,
deployment: {
$type: "akash.deployment.v1beta3.Deployment" as const,
deploymentId: {
$type: "akash.deployment.v1beta3.DeploymentID" as const,
owner: "akash1xq5s8qmhvsvxvz8v5s3s6z4j0n3x5q9q4qz4q4",
dseq: Long.ZERO
},
state: 0 as number,
version: new Uint8Array(),
createdAt: Long.ZERO
},
groups: [],
escrowAccount: undefined
}
];
const expectedRepositoryData: Deployment[] = [
{
owner: "akash1xq5s8qmhvsvxvz8v5s3s6z4j0n3x5q9q4qz4q4",
dseq: Long.ZERO.toNumber(),
state: 0,
version: base64FromBytes(new Uint8Array()),
createdAt: Long.ZERO.toNumber()
}
];
jest
.spyOn(mockDeploymentDataSource, "list")
.mockImplementation(() => Promise.resolve(expectedDatasourceData));
const result = await deploymentRepository.list();
expect(result).toStrictEqual(expectedRepositoryData);
});
});

describe("listDeploymentsByOwner", () => {
test("should return data", async () => {
jest
.spyOn(mockDeploymentDataSource, "listByOwner")
.mockImplementation(() => Promise.resolve(expectedDatasourceData));
const result = await deploymentRepository.listByOwner(deploymentOwner);
expect(result).toStrictEqual(expectedRepositoryData);
});
});
});
124 changes: 109 additions & 15 deletions api/tests/routers/deployment-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,32 @@ import request from "supertest";
import { type Deployment } from "../../src/domain/entities/deployment";
import { type IListUseCase } from "../../src/domain/interfaces/use-cases/deployment/list";
import { type IListByOwnerUseCase } from "../../src/domain/interfaces/use-cases/deployment/list-by-owner";
import { type ICreateUseCase } from "../../src/domain/interfaces/use-cases/deployment/create";
import DeploymentRouter from "../../src/routers/deployment-router";
import server from "../../src/server";
import Long from "long";
import { base64FromBytes } from "../../../shared/utils/encode";

const testAddress1 = "akash1xq5s8qmhvsvxvz8v5s3s6z4j0n3x5q9q4qz4q4";
const testAddress2 = "akash1xq5s8qmhvsvxvz8v5s3s6z4j0n3x5q9q4qz4q5";
const testAddress3 = "akash1xq5s8qmhvsvxvz8v5s3s6z4j0n3x5q9q4qz4q6";
const testData: Deployment[] = [
{
owner: testAddress1,
dseq: Long.ZERO.toNumber(),
state: 0,
createdAt: Long.ZERO.toNumber(),
version: base64FromBytes(new Uint8Array())
},
{
owner: testAddress2,
dseq: Long.ZERO.toNumber(),
state: 0,
createdAt: Long.ZERO.toNumber(),
version: base64FromBytes(new Uint8Array())
}
];

class MockListUseCase implements IListUseCase {
async execute(): Promise<Deployment[]> {
throw new Error("Method not implemented.");
Expand All @@ -19,16 +40,28 @@ class MockListByOwnerUseCase implements IListByOwnerUseCase {
}
}

class MockCreateUseCase implements ICreateUseCase {
async execute(deployment: Deployment): Promise<Deployment> {
throw new Error(`Method not implemented. ${deployment}`);
}
}

describe("Deployment Router", () => {
let mockListUseCase: IListUseCase;
let mockListByOwnerUseCase: IListByOwnerUseCase;
let mockCreateUseCase: ICreateUseCase;

beforeAll(() => {
mockListUseCase = new MockListUseCase();
mockListByOwnerUseCase = new MockListByOwnerUseCase();
mockCreateUseCase = new MockCreateUseCase();
server.use(
"/deployment",
DeploymentRouter(mockListUseCase, mockListByOwnerUseCase)
DeploymentRouter(
mockListUseCase,
mockListByOwnerUseCase,
mockCreateUseCase
)
);
});

Expand All @@ -37,34 +70,95 @@ describe("Deployment Router", () => {
});

describe("GET /deployment", () => {
// List all deployments
test("should return 200 with data", async () => {
const expectedData: Deployment[] = [
{
owner: "akash1xq5s8qmhvsvxvz8v5s3s6z4j0n3x5q9q4qz4q4",
dseq: Long.ZERO.toNumber(),
state: 0,
createdAt: Long.ZERO.toNumber(),
version: base64FromBytes(new Uint8Array())
}
];
jest
.spyOn(mockListUseCase, "execute")
.mockImplementation(async () => await Promise.resolve(expectedData));
.mockImplementation(async () => await Promise.resolve(testData));

const response = await request(server).get("/deployment/list");
const response = await request(server).get(`/deployment/list`);

expect(response.status).toBe(200);
expect(mockListUseCase.execute).toBeCalledTimes(1);
expect(response.body).toStrictEqual(expectedData);
expect(response.body).toStrictEqual(testData);
});

test("GET /deployment returns 500 on use case error", async () => {
jest
.spyOn(mockListUseCase, "execute")
.mockImplementation(
async () => await Promise.reject(Error("Error fetching data"))
async () => await Promise.reject(Error("Error fetching deployments"))
);
const response = await request(server).get(`/deployment/list`);
expect(response.status).toBe(500);
});

// List deployments by owner
test("should return 200 with data", async () => {
jest
.spyOn(mockListByOwnerUseCase, "execute")
.mockImplementation(
async () =>
await Promise.resolve(
testData.filter((d) => d.owner === testAddress1)
)
);

const response = await request(server).get(
`/deployment/listByOwner/${testAddress1}`
);

expect(response.status).toBe(200);
expect(mockListByOwnerUseCase.execute).toBeCalledTimes(1);
expect(response.body[0].owner).toStrictEqual(testAddress1);
});

test(`GET /deployment/${testAddress1} returns 500 on use case error`, async () => {
jest
.spyOn(mockListByOwnerUseCase, "execute")
.mockImplementation(
async () => await Promise.reject(Error("Error fetching deployments"))
);
const response = await request(server).get(
`/deployment/listByOwner/${testAddress1}`
);
expect(response.status).toBe(500);
});
});

describe("POST /deployment", () => {
const newDeployment = {
owner: testAddress3,
dseq: Long.ZERO.toNumber(),
state: 0,
createdAt: Long.ZERO.toNumber(),
version: base64FromBytes(new Uint8Array())
};

// Create deployment
test("should return 200 with data", async () => {
jest
.spyOn(mockCreateUseCase, "execute")
.mockImplementation(async () => await Promise.resolve(newDeployment));

const response = await request(server)
.post(`/deployment`)
.send(newDeployment);

expect(response.status).toBe(201);
expect(mockCreateUseCase.execute).toBeCalledTimes(1);
expect(response.body).toStrictEqual(newDeployment);
});

test(`POST /deployment returns 500 on use case error`, async () => {
jest
.spyOn(mockCreateUseCase, "execute")
.mockImplementation(
async () => await Promise.reject(Error("Error creating deployment"))
);
const response = await request(server).get("/deployment/list");
const response = await request(server)
.post(`/deployment`)
.send(newDeployment);
expect(response.status).toBe(500);
});
});
Expand Down

0 comments on commit dbdacda

Please sign in to comment.