73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import "reflect-metadata";
|
|
import { ConflictException, UnauthorizedException } from "@nestjs/common";
|
|
import { JwtService } from "@nestjs/jwt";
|
|
import * as bcrypt from "bcrypt";
|
|
import { AuthService } from "../src/auth/auth.service";
|
|
|
|
/** A fake `pg.Pool` — the goal here is auth logic (hashing, verification,
|
|
* conflict handling, JWT claims), not exercising a real Postgres, which
|
|
* `test_live_datastores.py`'s equivalent role covers for ai-service. */
|
|
function fakePool(rows: unknown[] = [], rejectCode?: string) {
|
|
return {
|
|
query: jest.fn().mockImplementation(async () => {
|
|
if (rejectCode) {
|
|
const error = new Error("duplicate") as Error & { code: string };
|
|
error.code = rejectCode;
|
|
throw error;
|
|
}
|
|
return { rows };
|
|
}),
|
|
} as unknown as import("pg").Pool;
|
|
}
|
|
|
|
const jwt = new JwtService({ secret: "test-secret-not-for-real-use" });
|
|
|
|
describe("AuthService", () => {
|
|
it("registers a new user and returns username/role", async () => {
|
|
const pool = fakePool([{ username: "demo", role: "user" }]);
|
|
const service = new AuthService(pool, jwt);
|
|
const result = await service.register("demo", "1");
|
|
expect(result).toEqual({ username: "demo", role: "user" });
|
|
});
|
|
|
|
it("rejects registering a username that already exists", async () => {
|
|
const pool = fakePool([], "23505");
|
|
const service = new AuthService(pool, jwt);
|
|
await expect(service.register("admin", "1")).rejects.toBeInstanceOf(
|
|
ConflictException
|
|
);
|
|
});
|
|
|
|
it("logs in with the correct password and issues a JWT carrying the role", async () => {
|
|
const passwordHash = await bcrypt.hash("1", 12);
|
|
const pool = fakePool([
|
|
{ id: "u-1", username: "admin", password_hash: passwordHash, role: "admin" },
|
|
]);
|
|
const service = new AuthService(pool, jwt);
|
|
const result = await service.login("admin", "1");
|
|
expect(result.user).toEqual({ username: "admin", role: "admin" });
|
|
const decoded = jwt.verify(result.token) as { sub: string; role: string };
|
|
expect(decoded.sub).toBe("u-1");
|
|
expect(decoded.role).toBe("admin");
|
|
});
|
|
|
|
it("rejects a wrong password", async () => {
|
|
const passwordHash = await bcrypt.hash("1", 12);
|
|
const pool = fakePool([
|
|
{ id: "u-1", username: "admin", password_hash: passwordHash, role: "admin" },
|
|
]);
|
|
const service = new AuthService(pool, jwt);
|
|
await expect(service.login("admin", "wrong")).rejects.toBeInstanceOf(
|
|
UnauthorizedException
|
|
);
|
|
});
|
|
|
|
it("rejects a username that does not exist without leaking that distinction", async () => {
|
|
const pool = fakePool([]);
|
|
const service = new AuthService(pool, jwt);
|
|
await expect(service.login("nobody", "1")).rejects.toBeInstanceOf(
|
|
UnauthorizedException
|
|
);
|
|
});
|
|
});
|