feat(core): add auth controllers with tests (sign-in, sign-up, sign-out)
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { InputParseError } from "@/entities/errors/common.js";
|
||||
import type { Cookie } from "@/entities/models/cookie.js";
|
||||
import { signInUseCase } from "@/application/use-cases/auth/sign-in.use-case.js";
|
||||
|
||||
const inputSchema = z.object({
|
||||
username: z.string().min(3).max(31),
|
||||
password: z.string().min(6).max(255),
|
||||
});
|
||||
|
||||
export async function signInController(
|
||||
input: Partial<z.infer<typeof inputSchema>>
|
||||
): Promise<Cookie> {
|
||||
const { data, error: inputParseError } = inputSchema.safeParse(input);
|
||||
|
||||
if (inputParseError) {
|
||||
throw new InputParseError("Invalid data", { cause: inputParseError });
|
||||
}
|
||||
|
||||
const { cookie } = await signInUseCase(data);
|
||||
return cookie;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { InputParseError } from "@/entities/errors/common.js";
|
||||
import type { Cookie } from "@/entities/models/cookie.js";
|
||||
import { signOutUseCase } from "@/application/use-cases/auth/sign-out.use-case.js";
|
||||
|
||||
export async function signOutController(
|
||||
sessionId: string | undefined
|
||||
): Promise<Cookie> {
|
||||
if (!sessionId) {
|
||||
throw new InputParseError("Must provide a session ID");
|
||||
}
|
||||
|
||||
const { blankCookie } = await signOutUseCase(sessionId);
|
||||
return blankCookie;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { InputParseError } from "@/entities/errors/common.js";
|
||||
import { signUpUseCase } from "@/application/use-cases/auth/sign-up.use-case.js";
|
||||
|
||||
const inputSchema = z
|
||||
.object({
|
||||
username: z.string().min(3).max(31),
|
||||
password: z.string().min(6).max(255),
|
||||
confirmPassword: z.string().min(6).max(255),
|
||||
})
|
||||
.superRefine(({ password, confirmPassword }, ctx) => {
|
||||
if (confirmPassword !== password) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "The passwords did not match",
|
||||
path: ["password"],
|
||||
});
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "The passwords did not match",
|
||||
path: ["confirmPassword"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export async function signUpController(
|
||||
input: Partial<z.infer<typeof inputSchema>>
|
||||
): Promise<ReturnType<typeof signUpUseCase>> {
|
||||
const { data, error: inputParseError } = inputSchema.safeParse(input);
|
||||
|
||||
if (inputParseError) {
|
||||
throw new InputParseError("Invalid data", { cause: inputParseError });
|
||||
}
|
||||
|
||||
return await signUpUseCase(data);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import "reflect-metadata";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
destroyContainer,
|
||||
initializeContainer,
|
||||
} from "@/di/container.js";
|
||||
import { signInController } from "@/interface-adapters/controllers/auth/sign-in.controller.js";
|
||||
import { InputParseError } from "@/entities/errors/common.js";
|
||||
import { AuthenticationError } from "@/entities/errors/auth.js";
|
||||
|
||||
beforeEach(() => {
|
||||
initializeContainer();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
destroyContainer();
|
||||
});
|
||||
|
||||
describe("signInController", () => {
|
||||
it("returns cookie for valid input", async () => {
|
||||
const cookie = await signInController({
|
||||
username: "alice",
|
||||
password: "password_alice",
|
||||
});
|
||||
expect(cookie).toHaveProperty("name");
|
||||
expect(cookie).toHaveProperty("value");
|
||||
});
|
||||
|
||||
it("throws InputParseError for invalid input", async () => {
|
||||
await expect(
|
||||
signInController({ username: "ab", password: "short" })
|
||||
).rejects.toBeInstanceOf(InputParseError);
|
||||
});
|
||||
|
||||
it("throws AuthenticationError for wrong credentials", async () => {
|
||||
await expect(
|
||||
signInController({ username: "alice", password: "wrongpassword" })
|
||||
).rejects.toBeInstanceOf(AuthenticationError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import "reflect-metadata";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
destroyContainer,
|
||||
initializeContainer,
|
||||
} from "@/di/container.js";
|
||||
import { signOutController } from "@/interface-adapters/controllers/auth/sign-out.controller.js";
|
||||
import { InputParseError } from "@/entities/errors/common.js";
|
||||
|
||||
beforeEach(() => {
|
||||
initializeContainer();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
destroyContainer();
|
||||
});
|
||||
|
||||
describe("signOutController", () => {
|
||||
it("returns blank cookie for valid session", async () => {
|
||||
const cookie = await signOutController("some-session-id");
|
||||
expect(cookie.value).toBe("");
|
||||
});
|
||||
|
||||
it("throws InputParseError when no session ID provided", async () => {
|
||||
await expect(signOutController(undefined)).rejects.toBeInstanceOf(
|
||||
InputParseError
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import "reflect-metadata";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
destroyContainer,
|
||||
initializeContainer,
|
||||
} from "@/di/container.js";
|
||||
import { signUpController } from "@/interface-adapters/controllers/auth/sign-up.controller.js";
|
||||
import { InputParseError } from "@/entities/errors/common.js";
|
||||
|
||||
beforeEach(() => {
|
||||
initializeContainer();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
destroyContainer();
|
||||
});
|
||||
|
||||
describe("signUpController", () => {
|
||||
it("returns session, cookie, and user for valid input", async () => {
|
||||
const result = await signUpController({
|
||||
username: "newuser",
|
||||
password: "securepassword",
|
||||
confirmPassword: "securepassword",
|
||||
});
|
||||
expect(result).toHaveProperty("session");
|
||||
expect(result).toHaveProperty("cookie");
|
||||
expect(result).toHaveProperty("user");
|
||||
});
|
||||
|
||||
it("throws InputParseError when passwords don't match", async () => {
|
||||
await expect(
|
||||
signUpController({
|
||||
username: "newuser",
|
||||
password: "password1",
|
||||
confirmPassword: "password2",
|
||||
})
|
||||
).rejects.toBeInstanceOf(InputParseError);
|
||||
});
|
||||
|
||||
it("throws InputParseError for missing fields", async () => {
|
||||
await expect(signUpController({})).rejects.toBeInstanceOf(InputParseError);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user