diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/parent-routes.test.ts | 36 | ||||
| -rw-r--r-- | test/signin-routes.test.ts | 50 |
2 files changed, 84 insertions, 2 deletions
diff --git a/test/parent-routes.test.ts b/test/parent-routes.test.ts index b93e85d..83734bb 100644 --- a/test/parent-routes.test.ts +++ b/test/parent-routes.test.ts @@ -2,9 +2,10 @@ import { describe, test, expect, vi, beforeEach } from "vitest"; import request from "supertest"; import express, { Request, Response, NextFunction } from "express"; import { db } from "../src/db/db"; -import { linkedDevices, deviceConfig } from "../src/db/schema"; +import { linkedDevices, deviceConfig, users } from "../src/db/schema"; import { eq } from "drizzle-orm"; import createParentRouter from "../src/routes/parent"; +import { redis } from "../src/db/redis/client"; vi.mock("../src/middleware/auth", () => ({ authParent: (req: Request, res: Response, next: NextFunction) => { @@ -28,6 +29,8 @@ describe("Parent Routes", () => { app.use("/", createParentRouter(onlineDevices)); await db.delete(deviceConfig).execute(); + await db.update(users).set({ emailVerified: true }).where(eq(users.id, 1)); + vi.mocked(redis.set).mockReset(); }); test("should get devices for parent", async () => { @@ -126,4 +129,35 @@ describe("Parent Routes", () => { expect(response.body.success).toBe(false); }); + + test("should generate one-time kid link code", async () => { + vi.mocked(redis.set).mockResolvedValueOnce("OK"); + + const response = await request(app).post("/parent/kid-link-code").expect(200); + + expect(response.body.success).toBe(true); + expect(response.body.code).toMatch(/^[A-Z0-9]{3}-[A-Z0-9]{3}$/); + expect(response.body.expiresInSeconds).toBe(300); + + const firstCall = vi.mocked(redis.set).mock.calls[0]; + expect(firstCall).toBeDefined(); + expect(firstCall?.[0]).toMatch(/^kid-link-code:[A-Z0-9]{3}-[A-Z0-9]{3}$/); + expect(firstCall?.[1]).toBe("1"); + expect(firstCall?.[2]).toBe("EX"); + expect(firstCall?.[3]).toBe(300); + expect(firstCall?.[4]).toBe("NX"); + }); + + test("should reject kid link code generation when email is not verified", async () => { + await db + .update(users) + .set({ emailVerified: false }) + .where(eq(users.id, 1)); + + const response = await request(app).post("/parent/kid-link-code").expect(400); + + expect(response.body.success).toBe(false); + expect(response.body.reason).toContain("Verify your email"); + expect(redis.set).not.toHaveBeenCalled(); + }); }); diff --git a/test/signin-routes.test.ts b/test/signin-routes.test.ts index 74f205d..e044d72 100644 --- a/test/signin-routes.test.ts +++ b/test/signin-routes.test.ts @@ -2,12 +2,13 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import argon2 from "argon2"; import * as jose from "jose"; import { db } from "../src/db/db"; -import { users } from "../src/db/schema"; +import { users, linkedDevices } from "../src/db/schema"; import { eq } from "drizzle-orm"; import signinRouter from "../src/routes/signin"; import type { Request, Response } from "express"; import { verifyGoogleIdToken } from "../src/account/google"; import { signJwt } from "../src/account/jwt"; +import { redis } from "../src/db/redis/client"; const sendMail = vi.fn(); @@ -41,6 +42,7 @@ describe("Signin Routes", () => { vi.mocked(verifyGoogleIdToken).mockReset(); vi.mocked(signJwt).mockReset(); vi.mocked(signJwt).mockResolvedValue("signed-jwt"); + vi.mocked(redis.getdel).mockReset(); }); function getRouteHandler(path: string, method: "get" | "post") { @@ -274,4 +276,50 @@ describe("Signin Routes", () => { `action="/reset-password/${encodeURIComponent(token)}"`, ); }); + + test("should link a kid device with a valid one-time code", async () => { + const beforeDevices = await db + .select() + .from(linkedDevices) + .where(eq(linkedDevices.parentId, 1)); + + vi.mocked(redis.getdel).mockResolvedValueOnce("1"); + + const response = await invokeRoute("/kid/link", "post", { + body: { + code: "abc-123", + }, + }); + + expect(response.statusCode).toBe(200); + expect(response.body).toEqual({ + success: true, + token: "signed-jwt", + reason: "", + }); + expect(redis.getdel).toHaveBeenCalledWith("kid-link-code:ABC-123"); + + const afterDevices = await db + .select() + .from(linkedDevices) + .where(eq(linkedDevices.parentId, 1)); + + expect(afterDevices.length).toBe(beforeDevices.length + 1); + }); + + test("should reject linking when code is expired or invalid", async () => { + vi.mocked(redis.getdel).mockResolvedValueOnce(null); + + const response = await invokeRoute("/kid/link", "post", { + body: { + code: "ABC-123", + }, + }); + + expect(response.statusCode).toBe(200); + expect(response.body).toEqual({ + success: false, + reason: "Invalid or expired code", + }); + }); }); |