Add comprehensive backend test suite (#339)

* add test suite, mostly covers integration testing, tests are only backend side

* workflow runs the correct script

* workflow runs the correct script

* workflow runs the correct script

* unit tests incoming

* Fix multer silent rejections and error handler info leak

- Revert cb(null, false) to cb(new Error(...)) in auth.ts, collab.ts,
  and files.ts so invalid uploads return an error instead of silently
  dropping the file
- Error handler in app.ts now always returns 500 / "Internal server
  error" instead of forwarding err.message to the client

* Use statusCode consistently for multer errors and error handler

- Error handler in app.ts reads err.statusCode to forward the correct
  HTTP status while keeping the response body generic
This commit is contained in:
Julien G.
2026-04-03 13:17:53 +02:00
committed by GitHub
parent d48714d17a
commit 905c7d460b
74 changed files with 12821 additions and 311 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import * as crypto from 'crypto';
import * as crypto from 'node:crypto';
import { ENCRYPTION_KEY } from '../config';
const ENCRYPTED_PREFIX = 'enc:v1:';
+1 -1
View File
@@ -2,7 +2,7 @@ import { Response } from 'express';
const COOKIE_NAME = 'trek_session';
function cookieOptions(clear = false) {
export function cookieOptions(clear = false) {
const secure = process.env.COOKIE_SECURE !== 'false' && (process.env.NODE_ENV === 'production' || process.env.FORCE_HTTPS === 'true');
return {
httpOnly: true,
+3 -3
View File
@@ -175,14 +175,14 @@ const EVENT_TEXTS: Record<string, Record<EventType, EventTextFn>> = {
};
// Get localized event text
function getEventText(lang: string, event: EventType, params: Record<string, string>): EventText {
export function getEventText(lang: string, event: EventType, params: Record<string, string>): EventText {
const texts = EVENT_TEXTS[lang] || EVENT_TEXTS.en;
return texts[event](params);
}
// ── Email HTML builder ─────────────────────────────────────────────────────
function buildEmailHtml(subject: string, body: string, lang: string): string {
export function buildEmailHtml(subject: string, body: string, lang: string): string {
const s = I18N[lang] || I18N.en;
const appUrl = getAppUrl();
const ctaHref = appUrl || '#';
@@ -256,7 +256,7 @@ async function sendEmail(to: string, subject: string, body: string, userId?: num
}
}
function buildWebhookBody(url: string, payload: { event: string; title: string; body: string; tripName?: string }): string {
export function buildWebhookBody(url: string, payload: { event: string; title: string; body: string; tripName?: string }): string {
const isDiscord = /discord(?:app)?\.com\/api\/webhooks\//.test(url);
const isSlack = /hooks\.slack\.com\//.test(url);
+2 -2
View File
@@ -116,7 +116,7 @@ const TTL_FORECAST_MS = 60 * 60 * 1000; // 1 hour
const TTL_CURRENT_MS = 15 * 60 * 1000; // 15 minutes
const TTL_CLIMATE_MS = 24 * 60 * 60 * 1000; // 24 hours
function cacheKey(lat: string, lng: string, date?: string): string {
export function cacheKey(lat: string, lng: string, date?: string): string {
const rlat = parseFloat(lat).toFixed(2);
const rlng = parseFloat(lng).toFixed(2);
return `${rlat}_${rlng}_${date || 'current'}`;
@@ -138,7 +138,7 @@ function setCache(key: string, data: WeatherResult, ttlMs: number): void {
// ── Helpers ─────────────────────────────────────────────────────────────
function estimateCondition(tempAvg: number, precipMm: number): string {
export function estimateCondition(tempAvg: number, precipMm: number): string {
if (precipMm > 5) return tempAvg <= 0 ? 'Snow' : 'Rain';
if (precipMm > 1) return tempAvg <= 0 ? 'Snow' : 'Drizzle';
if (precipMm > 0.3) return 'Clouds';