feat: add tests
Some checks failed
TrafficCue Server CI / check (push) Has been cancelled

This commit is contained in:
2025-08-30 10:12:20 +02:00
parent 03e602c814
commit 390594bb39
8 changed files with 541 additions and 59 deletions

View File

@ -46,6 +46,6 @@ export function getTokenUID(token: string): string | null {
return null;
}
export function getTokenData(token: string): JWTPayload {
return decode(token) as JWTPayload;
export function getTokenData(token: string): JWTPayload | null {
return decode(token) as JWTPayload | null;
}

View File

@ -1,13 +1,26 @@
import { Pool } from "pg";
import "reflect-metadata";
import { DataSource } from "typeorm";
import { User } from "./entities/User";
import { Review } from "./entities/Review";
import { Saved } from "./entities/Saved";
export const db = new DataSource({
type: "postgres",
url: process.env.DATABASE_URL,
synchronize: process.argv.includes("sync"),
entities: [User, Review, Saved]
})
let db: DataSource | null = null;
export function getDb(forceSync = false): DataSource {
if (!db) {
db = new DataSource({
type: "postgres",
url: process.env.DATABASE_URL,
synchronize: process.argv.includes("sync") || forceSync,
entities: [User, Review, Saved]
})
}
return db;
}
export async function initDb() {
const db = getDb();
if (!db.isInitialized) {
await db.initialize();
}
}

6
src/entry.ts Normal file
View File

@ -0,0 +1,6 @@
import { initDb } from "./db";
import app from "./main";
await initDb();
Bun.serve(app);

View File

@ -1,6 +1,5 @@
import { Hono } from "hono";
import { cors } from "hono/cors";
import { db } from "./db";
import { post } from "./ai";
import { rateLimiter } from "hono-rate-limiter";
import { getTokenData, getTokenUID, verifyToken } from "./auth";
@ -15,8 +14,6 @@ import { ILike } from "typeorm";
const app = new Hono();
const { upgradeWebSocket, websocket } = createBunWebSocket<ServerWebSocket>();
await db.initialize();
app.use(
"/api/*", // or replace with "*" to enable cors for all routes
cors({
@ -73,6 +70,7 @@ app.post("/api/user", async (c) => {
}
const tokenData = getTokenData(token);
if(!tokenData) return c.json({ error: "Invalid token" }, 400);
if(!tokenData.sub || typeof tokenData.sub != "string") return c.json({ error: "Invalid token" }, 400);
if(!tokenData.preferred_username || typeof tokenData.preferred_username != "string") return c.json({ error: "Invalid token" }, 400);
const user = await User.findOneBy({ id: tokenData.sub }) || new User();
@ -96,13 +94,13 @@ app.get("/api/user", async (c) => {
if (process.env.REVIEWS_ENABLED) {
app.get("/api/reviews", async (c) => {
let { lat, lon } = c.req.query();
const { lat, lon } = c.req.query();
if (!lat || !lon) {
return c.json({ error: "Latitude and longitude are required" }, 400);
}
// Remove unnecessary precision from lat/lon
let nlat = Number(parseFloat(lat).toFixed(6));
let nlon = Number(parseFloat(lon).toFixed(6));
const nlat = Number(parseFloat(lat).toFixed(6));
const nlon = Number(parseFloat(lon).toFixed(6));
console.log(`Fetching reviews for lat: ${lat}, lon: ${lon}`);
const reviews = await Review.findBy({
latitude: nlat,
@ -138,21 +136,14 @@ if (process.env.REVIEWS_ENABLED) {
return c.json({ error: "Unauthorized" }, 401);
}
/* const res = await pool.query(
"INSERT INTO reviews (user_id, latitude, longitude, rating, comment) VALUES ($1, $2, $3, $4, $5) RETURNING *",
[uid, lat, lon, rating, comment],
);
return c.json(res.rows[0]); */
let user = await User.findOneBy({ id: uid });
const user = await User.findOneBy({ id: uid });
if(!user) {
return c.json({ error: "Invalid user ID" }, 400);
}
// Remove unnecessary precision from lat/lon
let nlat = Number(parseFloat(lat).toFixed(6));
let nlon = Number(parseFloat(lon).toFixed(6));
const nlat = Number(parseFloat(lat).toFixed(6));
const nlon = Number(parseFloat(lon).toFixed(6));
const review = new Review();
review.latitude = nlat;
@ -185,19 +176,14 @@ app.get("/api/saved", async (c) => {
return c.json({ error: "Unauthorized" }, 401);
}
/* const res = await pool.query(
"SELECT * FROM saved WHERE user_id = $1",
[uid],
);
// const user = await User.findOneBy({ id: uid });
// if(!user) {
// return c.json({ error: "Invalid user ID" }, 400);
// }
return c.json(res.rows); */
let user = await User.findOneBy({ id: uid });
if(!user) {
return c.json({ error: "Invalid user ID" }, 400);
}
const saved = await Saved.findBy({ user });
const saved = await Saved.findBy({ user: {
id: uid
} });
return c.json(saved);
})
@ -228,14 +214,7 @@ app.put("/api/saved", async (c) => {
return c.json({ error: "Unauthorized" }, 401);
}
/* const res = await pool.query(
"INSERT INTO saved (user_id, name, data) VALUES ($1, $2, $3) RETURNING *",
[uid, name, data],
);
return c.json(res.rows[0]); */
let user = await User.findOneBy({ id: uid });
const user = await User.findOneBy({ id: uid });
if(!user) {
return c.json({ error: "Invalid user ID" }, 400);
}
@ -276,19 +255,12 @@ app.delete("/api/saved", async (c) => {
return c.json({ error: "Unauthorized" }, 401);
}
/* const res = await pool.query(
"DELETE FROM saved WHERE user_id = $1 AND name = $2",
[uid, name],
);
// const user = await User.findOneBy({ id: uid });
// if(!user) {
// return c.json({ error: "Invalid user ID" }, 400);
// }
return c.json(res.rows[0]); */
let user = await User.findOneBy({ id: uid });
if(!user) {
return c.json({ error: "Invalid user ID" }, 400);
}
const saved = await Saved.findOneBy({ user, name });
const saved = await Saved.findOneBy({ user: { id: uid }, name });
if(!saved) {
return c.json({ error: "No such save found" }, 400);
}