feat(test): add api branch test

This commit is contained in:
Methapon2001 2025-05-23 17:12:06 +07:00
parent f7c81641b2
commit 1896e2385d
5 changed files with 1251 additions and 1 deletions

View file

@ -7,6 +7,7 @@
"start": "node ./dist/app.js", "start": "node ./dist/app.js",
"dev": "nodemon", "dev": "nodemon",
"check": "tsc --noEmit", "check": "tsc --noEmit",
"test": "vitest",
"format": "prettier --write .", "format": "prettier --write .",
"debug": "nodemon", "debug": "nodemon",
"build": "tsoa spec-and-routes && tsc", "build": "tsoa spec-and-routes && tsc",
@ -27,12 +28,14 @@
"@types/multer": "^1.4.12", "@types/multer": "^1.4.12",
"@types/node": "^20.17.10", "@types/node": "^20.17.10",
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@vitest/ui": "^3.1.4",
"nodemon": "^3.1.9", "nodemon": "^3.1.9",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"prisma": "^6.3.0", "prisma": "^6.3.0",
"prisma-kysely": "^1.8.0", "prisma-kysely": "^1.8.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.7.2" "typescript": "^5.7.2",
"vitest": "^3.1.4"
}, },
"dependencies": { "dependencies": {
"@elastic/elasticsearch": "^8.17.0", "@elastic/elasticsearch": "^8.17.0",

909
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

323
test/branch.test.ts Normal file
View file

@ -0,0 +1,323 @@
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from "vitest";
import { PrismaClient } from "@prisma/client";
import { isDateString } from "./lib";
const prisma = new PrismaClient({
datasourceUrl: process.env.TEST_DATABASE_URL || process.env.DATABASE_URL,
});
const baseUrl = process.env.TEST_BASE_URL || "http://localhost";
const record: Record<string, any> = {
code: "CMT",
taxNo: "1052299402851",
name: "Chamomind",
nameEN: "Chamomind",
email: "contact@chamomind.com",
lineId: "@chamomind",
telephoneNo: "0988929248",
contactName: "John",
webUrl: "https://chamomind.com",
latitude: "",
longitude: "",
virtual: false,
permitNo: "1135182804792",
permitIssueDate: "2025-01-01T00:00:00.000Z",
permitExpireDate: "2030-01-01T00:00:00.000Z",
address: "11/3",
addressEN: "11/3",
soi: "1",
soiEN: "1",
moo: "2",
mooEN: "2",
street: "Straight",
streetEN: "Straight",
provinceId: "50",
districtId: "5001",
subDistrictId: "500107",
};
const recordList: Record<string, any>[] = [];
let token: string;
beforeAll(async () => {
const body = new URLSearchParams();
body.append("grant_type", "password");
body.append("client_id", "app");
body.append("username", process.env.TEST_USERNAME || "");
body.append("password", process.env.TEST_PASSWORD || "");
body.append("scope", "openid");
const res = await fetch(
process.env.KC_URL + "/realms/" + process.env.KC_REALM + "/protocol/openid-connect/token",
{
method: "POST",
body: body,
},
);
expect(res.ok).toBe(true);
await res.json().then((data) => {
token = data["access_token"];
});
});
afterAll(async () => {
if (!record["id"]) return;
await prisma.branch.deleteMany({
where: { id: { in: [record, ...recordList].map((v) => v["id"]) } },
});
await prisma.runningNo.deleteMany({
where: {
key: { in: [record, ...recordList].map((v) => `MAIN_BRANCH_${v["code"].slice(0, -5)}`) },
},
});
});
describe("branch management", () => {
it("create branch without required fields", async () => {
const requiredFields = [
"taxNo",
"name",
"nameEN",
"permitNo",
"telephoneNo",
"address",
"addressEN",
"email",
];
onTestFailed(() => console.log("Field:", requiredFields, "is required."));
for await (const field of requiredFields) {
const res = await fetch(baseUrl + "/api/v1/branch", {
method: "POST",
headers: {
["Content-Type"]: "application/json",
["Authorization"]: "Bearer " + token,
},
body: JSON.stringify({ ...record, [field]: undefined }),
});
if (res.ok) recordList.push(await res.json());
expect(res.ok).toBe(false);
expect(res.status).toBe(400);
}
});
it("create branch", async () => {
const res = await fetch(baseUrl + "/api/v1/branch", {
method: "POST",
headers: {
["Content-Type"]: "application/json",
["Authorization"]: "Bearer " + token,
},
body: JSON.stringify(record),
});
if (!res.ok) {
const text = await res.text();
try {
console.log(JSON.parse(text));
} catch (e) {
console.log(text);
}
}
expect(res.ok).toBe(true);
const data = await res.json();
record["id"] = data["id"]; // This field is auto generated
record["code"] = data["code"]; // This field is auto generated
recordList.push(data);
expect(data).toMatchObject(record);
});
it("get branch list", async () => {
const res = await fetch(baseUrl + "/api/v1/branch", {
method: "GET",
headers: {
["Authorization"]: "Bearer " + token,
},
});
if (!res.ok) {
const text = await res.text();
try {
console.log(JSON.parse(text));
} catch (e) {
console.log(text);
}
}
expect(res.ok).toBe(true);
const data = await res.json();
expect(data).toHaveProperty("result");
expect(data).toHaveProperty("total");
expect(data).toHaveProperty("page");
expect(data).toHaveProperty("pageSize");
expect(data.result).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
code: expect.any(String),
virtual: expect.any(Boolean),
name: expect.any(String),
nameEN: expect.any(String),
email: expect.any(String),
taxNo: expect.any(String),
telephoneNo: expect.any(String),
latitude: expect.any(String),
longitude: expect.any(String),
contactName: expect.toBeOneOf([expect.any(String), null]),
lineId: expect.toBeOneOf([expect.any(String), null]),
webUrl: expect.toBeOneOf([expect.any(String), null]),
remark: expect.toBeOneOf([expect.any(String), null]),
selectedImage: expect.toBeOneOf([expect.any(String), null]),
isHeadOffice: expect.any(Boolean),
permitNo: expect.any(String),
permitIssueDate: expect.toSatisfy(isDateString(true)),
permitExpireDate: expect.toSatisfy(isDateString(true)),
address: expect.any(String),
addressEN: expect.any(String),
moo: expect.toBeOneOf([expect.any(String), null]),
mooEN: expect.toBeOneOf([expect.any(String), null]),
street: expect.toBeOneOf([expect.any(String), null]),
streetEN: expect.toBeOneOf([expect.any(String), null]),
provinceId: expect.any(String),
province: expect.objectContaining({
id: expect.any(String),
name: expect.any(String),
nameEN: expect.any(String),
}),
districtId: expect.any(String),
district: expect.objectContaining({
id: expect.any(String),
name: expect.any(String),
nameEN: expect.any(String),
}),
subDistrictId: expect.any(String),
subDistrict: expect.objectContaining({
id: expect.any(String),
name: expect.any(String),
nameEN: expect.any(String),
zipCode: expect.any(String),
}),
status: expect.toBeOneOf(["CREATED", "ACTIVE", "INACTIVE"]),
statusOrder: expect.toBeOneOf([1, 0]),
createdAt: expect.toSatisfy(isDateString()),
createdByUserId: expect.toBeOneOf([expect.any(String), null]),
createdBy: expect.objectContaining({
id: expect.any(String),
username: expect.any(String),
firstName: expect.any(String),
lastName: expect.any(String),
firstNameEN: expect.any(String),
lastNameEN: expect.any(String),
}),
updatedAt: expect.toSatisfy(isDateString()),
updatedByUserId: expect.toBeOneOf([expect.any(String), null]),
updatedBy: expect.objectContaining({
id: expect.any(String),
username: expect.any(String),
firstName: expect.any(String),
lastName: expect.any(String),
firstNameEN: expect.any(String),
lastNameEN: expect.any(String),
}),
_count: expect.objectContaining({
branch: expect.any(Number),
}),
}),
]),
);
});
it("get branch by id", async () => {
const res = await fetch(baseUrl + "/api/v1/branch/" + record["id"], {
method: "GET",
headers: {
["Authorization"]: "Bearer " + token,
},
});
if (!res.ok) {
const text = await res.text();
try {
console.log(JSON.parse(text));
} catch (e) {
console.log(text);
}
}
expect(res.ok).toBe(true);
const data = await res.json();
expect(data).toMatchObject(record);
});
it("update branch by id", async () => {
const res = await fetch(baseUrl + "/api/v1/branch/" + record["id"], {
method: "PUT",
headers: {
["Content-Type"]: "application/json",
["Authorization"]: "Bearer " + token,
},
body: JSON.stringify({ name: "Chamomind Intl.", nameEN: "Chamomind Intl." }),
});
record["name"] = "Chamomind Intl.";
record["nameEN"] = "Chamomind Intl.";
expect(res.ok).toBe(true);
const data = await res.json();
expect(data).toMatchObject(record);
});
it("delete branch by id", async () => {
const res = await fetch(baseUrl + "/api/v1/branch/" + record["id"], {
method: "DELETE",
headers: {
["Content-Type"]: "application/json",
["Authorization"]: "Bearer " + token,
},
});
if (!res.ok) {
const text = await res.text();
try {
console.log(JSON.parse(text));
} catch (e) {
console.log(text);
}
}
expect(res.ok).toBe(true);
const data = await res.json();
expect(data).toMatchObject(record);
});
it("get deleted branch by id", async () => {
const res = await fetch(baseUrl + "/api/v1/branch/" + record["id"], {
method: "GET",
headers: {
["Authorization"]: "Bearer " + token,
},
});
expect(res.ok).toBe(false);
});
});

10
test/lib/index.ts Normal file
View file

@ -0,0 +1,10 @@
export function isDateString(nullable: boolean = false): (val: any) => boolean {
return (value: any) => {
try {
if (value) return !!new Date(value);
return nullable;
} catch (_) {
return false;
}
};
}

5
vite.config.ts Normal file
View file

@ -0,0 +1,5 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {},
});