ci(app): testing and other app config changes
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -54,6 +54,8 @@
|
|||||||
"alplaprod",
|
"alplaprod",
|
||||||
"Datamart",
|
"Datamart",
|
||||||
"intiallally",
|
"intiallally",
|
||||||
|
"OCME",
|
||||||
|
"onnotice",
|
||||||
"ppoo",
|
"ppoo",
|
||||||
"prodlabels"
|
"prodlabels"
|
||||||
],
|
],
|
||||||
|
|||||||
47
README.md
47
README.md
@@ -1,3 +1,46 @@
|
|||||||
# lst_v3
|
# Logistics support tool
|
||||||
|
|
||||||
The tool that supports us in our everyday alplaprod adventures
|
> The support tool for ALPLA Prod
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Quick summary of current rewrite/migration goal.
|
||||||
|
|
||||||
|
- **Phase:** Backend rewrite
|
||||||
|
- **Last updated:** 2024-05-01
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Feature Status
|
||||||
|
|
||||||
|
| Feature | Description | Status |
|
||||||
|
|----------|--------------|--------|
|
||||||
|
| User Authentication | Login, Signup, JWT refresh, API Key | 🟨 In Progress |
|
||||||
|
| User Profile | Edit profile, upload avatar | ⏳ Not Started |
|
||||||
|
| Notifications | Subscribe, Create, Update | ⏳ Not Started |
|
||||||
|
| Datamart | Create, Update, Run | 🔧 In Progress |
|
||||||
|
| Frontend | Analytics and charts | ⏳ Not Started |
|
||||||
|
| One Click Print | Printing system | ⏳ Not Started |
|
||||||
|
| Silo Adjustments | Adjustments | ⏳ Not Started |
|
||||||
|
| Demand Management | Orders, Forecast | ⏳ Not Started |
|
||||||
|
| Open Docks | Integrations | ⏳ Not Started |
|
||||||
|
| Transport Insight | Integrations | ⏳ Not Started |
|
||||||
|
| Quality | Request Tool | ⏳ Not Started |
|
||||||
|
| OCME | Custom integration | ⏳ Not Started |
|
||||||
|
| API Migration | Moving to new REST endpoints | 🔧 In Progress |
|
||||||
|
| System | Tests, Updates, Remote Logging | ⏳ Not Started |
|
||||||
|
|
||||||
|
_Status legend:_
|
||||||
|
✅ Complete 🟨 In Progress ⏳ Not Started
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup / Installation
|
||||||
|
|
||||||
|
How to run the current version of the app.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/youruser/yourrepo.git
|
||||||
|
cd yourrepo
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
type Success<T> = { data: T; error: null };
|
|
||||||
type Failure<E> = { data: null; error: E };
|
|
||||||
|
|
||||||
export type Result<T, E = Error> = Success<T> | Failure<E>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A universal tryCatch wrapper that:
|
|
||||||
* - Never throws
|
|
||||||
* - Always resolves to Result<T,E>
|
|
||||||
* - Allows optional error mapping function for strong typing
|
|
||||||
*/
|
|
||||||
|
|
||||||
export async function tryCatch<T, E = Error>(
|
|
||||||
promise: Promise<T>,
|
|
||||||
onError?: (error: unknown) => E,
|
|
||||||
): Promise<Result<T, E>> {
|
|
||||||
try {
|
|
||||||
const data = await promise;
|
|
||||||
return { data, error: null };
|
|
||||||
} catch (err: unknown) {
|
|
||||||
const error = onError
|
|
||||||
? onError(err)
|
|
||||||
: err instanceof Error
|
|
||||||
? (err as E)
|
|
||||||
: (new Error(String(err)) as E);
|
|
||||||
return { data: null, error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7140
package-lock.json
generated
7140
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -4,9 +4,9 @@
|
|||||||
"description": "The tool that supports us in our everyday alplaprod",
|
"description": "The tool that supports us in our everyday alplaprod",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "dotenvx run -f .env -- vitest",
|
||||||
"dev": "dotenvx run -f .env -- npm run dev:app",
|
"dev": "dotenvx run -f .env -- npm run dev:app",
|
||||||
"dev:app": "cd backend && tsx watch app.ts",
|
"dev:app": "cd backend && tsx watch server.ts",
|
||||||
"dev:db:migrate": "npx drizzle-kit push",
|
"dev:db:migrate": "npx drizzle-kit push",
|
||||||
"dev:db:generate": "tsc && npx drizzle-kit generate --config=drizzle.config.ts",
|
"dev:db:generate": "tsc && npx drizzle-kit generate --config=drizzle.config.ts",
|
||||||
"build": "npm run specCheck && npm run lint && npm run dev:db:generate && npm run dev:db:migrate && npm run build:app && rimraf dist/backend",
|
"build": "npm run specCheck && npm run lint && npm run dev:db:generate && npm run dev:db:migrate && npm run build:app && rimraf dist/backend",
|
||||||
@@ -33,17 +33,24 @@
|
|||||||
"@commitlint/cli": "^18.4.0",
|
"@commitlint/cli": "^18.4.0",
|
||||||
"@commitlint/config-conventional": "^18.4.0",
|
"@commitlint/config-conventional": "^18.4.0",
|
||||||
"@scalar/express-api-reference": "^0.8.28",
|
"@scalar/express-api-reference": "^0.8.28",
|
||||||
|
"@swc/core": "^1.15.7",
|
||||||
|
"@swc/jest": "^0.2.39",
|
||||||
|
"@types/cors": "^2.8.19",
|
||||||
"@types/express": "^5.0.6",
|
"@types/express": "^5.0.6",
|
||||||
"@types/morgan": "^1.9.10",
|
"@types/morgan": "^1.9.10",
|
||||||
"@types/mssql": "^9.1.8",
|
"@types/mssql": "^9.1.8",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
|
"@types/nodemailer": "^7.0.4",
|
||||||
|
"@types/nodemailer-express-handlebars": "^4.0.6",
|
||||||
"@types/pg": "^8.16.0",
|
"@types/pg": "^8.16.0",
|
||||||
|
"@types/supertest": "^6.0.3",
|
||||||
"@types/swagger-jsdoc": "^6.0.4",
|
"@types/swagger-jsdoc": "^6.0.4",
|
||||||
"@types/swagger-ui-express": "^4.1.8",
|
"@types/swagger-ui-express": "^4.1.8",
|
||||||
"@vercel/ncc": "^0.38.4",
|
"@vercel/ncc": "^0.38.4",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"better-auth": "^1.4.6",
|
"better-auth": "^1.4.9",
|
||||||
"commitizen": "^4.3.0",
|
"commitizen": "^4.3.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
@@ -52,18 +59,24 @@
|
|||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"morgan": "^1.10.1",
|
"morgan": "^1.10.1",
|
||||||
"mssql": "^12.2.0",
|
"mssql": "^12.2.0",
|
||||||
|
"nodemailer": "^7.0.12",
|
||||||
|
"nodemailer-express-handlebars": "^7.0.0",
|
||||||
"npm-check-updates": "^19.1.2",
|
"npm-check-updates": "^19.1.2",
|
||||||
"openapi-types": "^12.1.3",
|
"openapi-types": "^12.1.3",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"pino": "^10.1.0",
|
"pino": "^10.1.0",
|
||||||
"pino-pretty": "^13.1.3",
|
"pino-pretty": "^13.1.3",
|
||||||
|
"postgres": "^3.4.7",
|
||||||
|
"supertest": "^7.1.4",
|
||||||
|
"ts-jest": "^29.4.6",
|
||||||
"ts-node-dev": "^2.0.0",
|
"ts-node-dev": "^2.0.0",
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3",
|
||||||
|
"vite-tsconfig-paths": "^6.0.3",
|
||||||
|
"vitest": "^4.0.16"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dotenvx/dotenvx": "^1.51.2",
|
"@dotenvx/dotenvx": "^1.51.2"
|
||||||
"postgres": "^3.4.7"
|
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"commitizen": {
|
"commitizen": {
|
||||||
|
|||||||
29
tests/examplequery.test.ts
Normal file
29
tests/examplequery.test.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
|
import { connectProdSql } from "../backend/src/prodSql/prodSqlConnection.controller.js";
|
||||||
|
|
||||||
|
let pool: any;
|
||||||
|
|
||||||
|
describe("Prod SQL connection", () => {
|
||||||
|
// This may take seconds, so give plenty of time
|
||||||
|
vi.setTimeout(30000);
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
pool = await connectProdSql();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
if (pool && pool.close) await pool.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should connect and return expected data", async () => {
|
||||||
|
// Example query — use something safe and consistent
|
||||||
|
const result = await pool
|
||||||
|
.request()
|
||||||
|
.query("SELECT TOP 1 id, name FROM Users ORDER BY id ASC");
|
||||||
|
|
||||||
|
expect(result.recordset).toBeDefined();
|
||||||
|
expect(Array.isArray(result.recordset)).toBe(true);
|
||||||
|
expect(result.recordset.length).toBeGreaterThan(0);
|
||||||
|
expect(result.recordset[0]).toHaveProperty("id");
|
||||||
|
});
|
||||||
|
});
|
||||||
42
tests/sendEmail.test.ts
Normal file
42
tests/sendEmail.test.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { describe, expect, test, vi } from "vitest";
|
||||||
|
import { sendEmail } from "../backend/src/utils/sendEmail.utils";
|
||||||
|
|
||||||
|
// Mock the logger before imports
|
||||||
|
vi.mock("../backend/src/logger/logger.controller", () => ({
|
||||||
|
createLogger: () => ({
|
||||||
|
info: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
debug: vi.fn(),
|
||||||
|
fatal: vi.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("Mail sending", () => {
|
||||||
|
test("should send an email successfully", async () => {
|
||||||
|
const result = await sendEmail({
|
||||||
|
email: "blake.matthes@alpla.com",
|
||||||
|
subject: "LST - Testing system",
|
||||||
|
template: "testEmail",
|
||||||
|
context: { name: "blake" },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result?.message).toContain("blake.matthes@alpla.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle email send failure gracefully", async () => {
|
||||||
|
const badResult = await sendEmail({
|
||||||
|
email: "invalid-address",
|
||||||
|
subject: "LST - Testing system",
|
||||||
|
template: "",
|
||||||
|
context: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(badResult).toMatchObject({
|
||||||
|
success: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"moduleResolution": "nodenext",
|
"moduleResolution": "nodenext",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"types": ["node", "better-auth"],
|
"types": ["node", "better-auth", "jest"],
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
|
|||||||
11
tsconfig.test.json
Normal file
11
tsconfig.test.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"target": "ESNext",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["tests/**/*", "backend/**/*"]
|
||||||
|
}
|
||||||
8
vitest.config.ts
Normal file
8
vitest.config.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
include: ["tests/**/*.test.ts"], // ← point to your tests folder
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user