ci(app): testing and other app config changes

This commit is contained in:
2025-12-30 08:01:16 -06:00
parent 9efd6419b6
commit 9531401e56
10 changed files with 7252 additions and 82 deletions

View File

@@ -54,6 +54,8 @@
"alplaprod", "alplaprod",
"Datamart", "Datamart",
"intiallally", "intiallally",
"OCME",
"onnotice",
"ppoo", "ppoo",
"prodlabels" "prodlabels"
], ],

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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": {

View 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
View 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,
});
});
});

View File

@@ -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
View 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
View 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
},
});