feat: add accounts and movies tables in migrations
- Created migration for accounts table with fields: id, username, password, role_id, created_at, updated_at. - Created migration for movies table with fields: id, title, overview, poster_path, backdrop_path, release_date, tmdb_id. refactor: update package.json scripts and dependencies - Changed dev script to use bun instead of tsx. - Added build:migrate script for migration. - Updated devDependencies for bun and oxlint. fix: refactor database connection and migration execution - Updated PgDatabase to use SQL from bun. - Refactored migration execution logic to read SQL files and execute them. feat: implement account creation and validation logic - Updated AccountEntity to use username instead of email. - Added validation for username format and password strength. - Implemented account repository methods for finding by username and inserting accounts. test: add tests for account entity, repository, and service - Created tests for AccountEntity to validate username and password. - Added tests for AccountRepository to ensure correct database interactions. - Implemented tests for AccountService to validate registration and login logic. chore: remove outdated tests and files - Deleted old tests related to email-based account management. - Cleaned up unused imports and files to streamline the codebase.
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import { AccountEntity } from "../src/domain/account/entity/AccountEntity";
|
||||
import {
|
||||
InvalidEmailFormatError,
|
||||
WeakPasswordError,
|
||||
} from "../src/domain/account/errors/AccountErrors";
|
||||
import { MIN_PASSWORD_LENGTH } from "../src/domain/account/validation/AccountValidation";
|
||||
|
||||
describe("AccountEntity", () => {
|
||||
describe("create", () => {
|
||||
it("should create an account with valid email and password", () => {
|
||||
const email = "test@example.com";
|
||||
const password = "a".repeat(MIN_PASSWORD_LENGTH); // Use minimum length
|
||||
|
||||
const account = AccountEntity.create(email, password);
|
||||
|
||||
expect(account.email).toBe(email);
|
||||
expect(account.roleId).toBe(1);
|
||||
expect(account.id).toBeDefined();
|
||||
expect(account.createdAt).toBeInstanceOf(Date);
|
||||
expect(account.updatedAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it("should throw InvalidEmailFormatError for invalid email", () => {
|
||||
const invalidEmail = "invalid-email";
|
||||
const password = "password123";
|
||||
|
||||
expect(() => {
|
||||
AccountEntity.create(invalidEmail, password);
|
||||
}).toThrow(InvalidEmailFormatError);
|
||||
});
|
||||
|
||||
it("should throw WeakPasswordError for short password", () => {
|
||||
const email = "test@example.com";
|
||||
const shortPassword = "a".repeat(MIN_PASSWORD_LENGTH - 1); // One less than minimum
|
||||
|
||||
expect(() => {
|
||||
AccountEntity.create(email, shortPassword);
|
||||
}).toThrow(WeakPasswordError);
|
||||
});
|
||||
});
|
||||
|
||||
describe("verifyPassword", () => {
|
||||
it("should return true for correct password", () => {
|
||||
const account = AccountEntity.create("test@example.com", "password123");
|
||||
|
||||
expect(account.verifyPassword("password123")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for incorrect password", () => {
|
||||
const account = AccountEntity.create("test@example.com", "password123");
|
||||
|
||||
expect(account.verifyPassword("wrongpassword")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,130 +0,0 @@
|
||||
import { describe, it, expect, jest, beforeEach } from "bun:test";
|
||||
import AccountRepository from "../src/domain/account/repository/AccoutRepository";
|
||||
import { AccountEntity } from "../src/domain/account/entity/AccountEntity";
|
||||
|
||||
describe("AccountRepository", () => {
|
||||
let accountRepository: AccountRepository;
|
||||
let mockDatabase: {
|
||||
ping: ReturnType<typeof jest.fn>;
|
||||
fetchAll: ReturnType<typeof jest.fn>;
|
||||
fetchOne: ReturnType<typeof jest.fn>;
|
||||
execute: ReturnType<typeof jest.fn>;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockDatabase = {
|
||||
ping: jest.fn(() => Promise.resolve()),
|
||||
fetchAll: jest.fn(() => Promise.resolve([])),
|
||||
fetchOne: jest.fn(() => Promise.resolve(undefined)),
|
||||
execute: jest.fn(() => Promise.resolve()),
|
||||
};
|
||||
accountRepository = new AccountRepository(mockDatabase as any);
|
||||
});
|
||||
|
||||
describe("findByEmail", () => {
|
||||
it("should return account when found", async () => {
|
||||
const email = "test@example.com";
|
||||
const mockResult = {
|
||||
id: "123",
|
||||
email: email,
|
||||
password: "hashedPassword",
|
||||
role_id: 1,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
};
|
||||
|
||||
mockDatabase.fetchOne.mockImplementation(() =>
|
||||
Promise.resolve(mockResult),
|
||||
);
|
||||
|
||||
const result = await accountRepository.findByEmail(email);
|
||||
|
||||
expect(result).toBeInstanceOf(AccountEntity);
|
||||
expect(result?.email).toBe(email);
|
||||
expect(mockDatabase.fetchOne).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"SELECT id, email, password, role_id, created_at, updated_at",
|
||||
),
|
||||
[email],
|
||||
);
|
||||
});
|
||||
|
||||
it("should return null when account not found", async () => {
|
||||
const email = "nonexistent@example.com";
|
||||
|
||||
mockDatabase.fetchOne.mockImplementation(() =>
|
||||
Promise.resolve(undefined),
|
||||
);
|
||||
|
||||
const result = await accountRepository.findByEmail(email);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("findById", () => {
|
||||
it("should return account when found", async () => {
|
||||
const id = "123";
|
||||
const mockResult = {
|
||||
id: id,
|
||||
email: "test@example.com",
|
||||
password: "hashedPassword",
|
||||
role_id: 1,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
};
|
||||
|
||||
mockDatabase.fetchOne.mockImplementation(() =>
|
||||
Promise.resolve(mockResult),
|
||||
);
|
||||
|
||||
const result = await accountRepository.findById(id);
|
||||
|
||||
expect(result).toBeInstanceOf(AccountEntity);
|
||||
expect(result?.id).toBe(id);
|
||||
expect(mockDatabase.fetchOne).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"SELECT id, email, password, role_id, created_at, updated_at",
|
||||
),
|
||||
[id],
|
||||
);
|
||||
});
|
||||
|
||||
it("should return null when account not found", async () => {
|
||||
const id = "nonexistent";
|
||||
|
||||
mockDatabase.fetchOne.mockImplementation(() =>
|
||||
Promise.resolve(undefined),
|
||||
);
|
||||
|
||||
const result = await accountRepository.findById(id);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("save", () => {
|
||||
it("should save account successfully", async () => {
|
||||
const account = AccountEntity.create("test@example.com", "password123");
|
||||
|
||||
mockDatabase.fetchOne.mockImplementation(() =>
|
||||
Promise.resolve({ id: account.id }),
|
||||
);
|
||||
|
||||
const result = await accountRepository.save(account);
|
||||
|
||||
expect(result).toBe(account.id);
|
||||
expect(mockDatabase.fetchOne).toHaveBeenCalledWith(
|
||||
expect.stringContaining("INSERT INTO accounts"),
|
||||
expect.arrayContaining([
|
||||
account.id,
|
||||
account.email,
|
||||
expect.any(String),
|
||||
account.roleId,
|
||||
account.createdAt,
|
||||
account.updatedAt,
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,108 +0,0 @@
|
||||
import { describe, it, expect, jest, beforeEach } from "bun:test";
|
||||
import AccountService from "../src/domain/account/service/AccountService";
|
||||
import { AccountEntity } from "../src/domain/account/entity/AccountEntity";
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
AccountAlreadyExistsError,
|
||||
BadPasswordError,
|
||||
} from "../src/domain/account/errors/AccountErrors";
|
||||
|
||||
describe("AccountService", () => {
|
||||
let accountService: AccountService;
|
||||
let mockAccountRepository: {
|
||||
findByEmail: ReturnType<typeof jest.fn>;
|
||||
save: ReturnType<typeof jest.fn>;
|
||||
findById: ReturnType<typeof jest.fn>;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockAccountRepository = {
|
||||
findByEmail: jest.fn(() => Promise.resolve(null)),
|
||||
save: jest.fn(() => Promise.resolve("123")),
|
||||
findById: jest.fn(() => Promise.resolve(null)),
|
||||
};
|
||||
accountService = new AccountService(mockAccountRepository as any);
|
||||
});
|
||||
|
||||
describe("createAccount", () => {
|
||||
it("should create a new account successfully", async () => {
|
||||
const email = "test@example.com";
|
||||
const password = "password123";
|
||||
|
||||
mockAccountRepository.findByEmail.mockImplementation(() =>
|
||||
Promise.resolve(null),
|
||||
);
|
||||
mockAccountRepository.save.mockImplementation(() =>
|
||||
Promise.resolve("123"),
|
||||
);
|
||||
|
||||
const result = await accountService.register(email, password);
|
||||
|
||||
expect(result).toBeInstanceOf(AccountEntity);
|
||||
expect(result.email).toBe(email);
|
||||
expect(mockAccountRepository.findByEmail).toHaveBeenCalledWith(email);
|
||||
expect(mockAccountRepository.save).toHaveBeenCalledWith(
|
||||
expect.any(AccountEntity),
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw error if account already exists", async () => {
|
||||
const email = "test@example.com";
|
||||
const password = "password123";
|
||||
const existingAccount = AccountEntity.create(email, password);
|
||||
|
||||
mockAccountRepository.findByEmail.mockImplementation(() =>
|
||||
Promise.resolve(existingAccount),
|
||||
);
|
||||
|
||||
await expect(accountService.register(email, password)).rejects.toThrow(
|
||||
AccountAlreadyExistsError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("login", () => {
|
||||
it("should login successfully with correct credentials", async () => {
|
||||
const email = "test@example.com";
|
||||
const password = "password123";
|
||||
const account = AccountEntity.create(email, password);
|
||||
|
||||
mockAccountRepository.findByEmail.mockImplementation(() =>
|
||||
Promise.resolve(account),
|
||||
);
|
||||
|
||||
const result = await accountService.login(email, password);
|
||||
|
||||
expect(result).toBe(account);
|
||||
expect(mockAccountRepository.findByEmail).toHaveBeenCalledWith(email);
|
||||
});
|
||||
|
||||
it("should throw error if account not found", async () => {
|
||||
const email = "test@example.com";
|
||||
const password = "password123";
|
||||
|
||||
mockAccountRepository.findByEmail.mockImplementation(() =>
|
||||
Promise.resolve(null),
|
||||
);
|
||||
|
||||
await expect(accountService.login(email, password)).rejects.toThrow(
|
||||
AccountNotFoundError,
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw error if password is incorrect", async () => {
|
||||
const email = "test@example.com";
|
||||
const password = "password123";
|
||||
const wrongPassword = "wrongpassword";
|
||||
const account = AccountEntity.create(email, password);
|
||||
|
||||
mockAccountRepository.findByEmail.mockImplementation(() =>
|
||||
Promise.resolve(account),
|
||||
);
|
||||
|
||||
await expect(accountService.login(email, wrongPassword)).rejects.toThrow(
|
||||
BadPasswordError,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
80
tests/account/AccountEntity.test.ts
Normal file
80
tests/account/AccountEntity.test.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import {
|
||||
InvalidUsernameFormatError,
|
||||
WeakPasswordError,
|
||||
} from "../../src/domain/account/errors/AccountErrors";
|
||||
import {
|
||||
MAX_USERNAME_LENGTH,
|
||||
MIN_PASSWORD_LENGTH,
|
||||
} from "../../src/domain/account/validation/AccountValidation";
|
||||
import { AccountEntity } from "../../src/domain/account/entity/AccountEntity";
|
||||
|
||||
describe("AccountEntity", () => {
|
||||
describe("create", () => {
|
||||
it("should create an account with valid username and password", async () => {
|
||||
const email = "testaccount";
|
||||
const password = "a".repeat(MIN_PASSWORD_LENGTH);
|
||||
|
||||
const account = await AccountEntity.create(email, password);
|
||||
|
||||
expect(account.username).toBe(email);
|
||||
expect(account.roleId).toBe(1);
|
||||
expect(account).toBeDefined();
|
||||
});
|
||||
|
||||
it("should throw InvalidUsernameFormatError for invalid username", () => {
|
||||
const invalidUsername = "a".repeat(MAX_USERNAME_LENGTH + 1);
|
||||
const password = "a".repeat(MIN_PASSWORD_LENGTH);
|
||||
|
||||
expect(AccountEntity.create(invalidUsername, password)).rejects.toThrow(
|
||||
InvalidUsernameFormatError,
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw WeakPasswordError for short password", () => {
|
||||
const username = "testaccount";
|
||||
const shortPassword = "a".repeat(MIN_PASSWORD_LENGTH - 1);
|
||||
|
||||
expect(AccountEntity.create(username, shortPassword)).rejects.toThrow(
|
||||
WeakPasswordError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("verifyPassword", () => {
|
||||
it("should return true for correct password", async () => {
|
||||
const createAccount = await AccountEntity.create(
|
||||
"test@example.com",
|
||||
"password123",
|
||||
);
|
||||
|
||||
const account = new AccountEntity(
|
||||
1,
|
||||
createAccount.username,
|
||||
createAccount.hashedPassword,
|
||||
createAccount.roleId,
|
||||
new Date(),
|
||||
new Date(),
|
||||
);
|
||||
|
||||
expect(await account.verifyPassword("password123")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for incorrect password", async () => {
|
||||
const createAccount = await AccountEntity.create(
|
||||
"test@example.com",
|
||||
"password123",
|
||||
);
|
||||
const account = new AccountEntity(
|
||||
1,
|
||||
createAccount.username,
|
||||
createAccount.hashedPassword,
|
||||
createAccount.roleId,
|
||||
new Date(),
|
||||
new Date(),
|
||||
);
|
||||
|
||||
expect(await account.verifyPassword("wrongpassword")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
74
tests/account/AccountRepository.test.ts
Normal file
74
tests/account/AccountRepository.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
||||
import AccountRepository from "../../src/domain/account/repository/AccoutRepository";
|
||||
import { AccountEntity } from "../../src/domain/account/entity/AccountEntity";
|
||||
import { initTestDatabase } from "../utils";
|
||||
import type { DatabaseInterface } from "../../src/database/DatabaseInterface";
|
||||
|
||||
describe("AccountRepository", () => {
|
||||
let accountRepository: AccountRepository;
|
||||
let database: DatabaseInterface;
|
||||
|
||||
beforeEach(async () => {
|
||||
database = await initTestDatabase();
|
||||
accountRepository = new AccountRepository(database);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await database?.close();
|
||||
});
|
||||
|
||||
describe("findByUsername", () => {
|
||||
it("should return account when found", async () => {
|
||||
const username = "testaccount";
|
||||
|
||||
const createAccount = await AccountEntity.create(username, "password123");
|
||||
await accountRepository.insert(createAccount);
|
||||
|
||||
const result = await accountRepository.findByUsername(username);
|
||||
|
||||
expect(result).toBeInstanceOf(AccountEntity);
|
||||
expect(result?.username).toBe(username);
|
||||
});
|
||||
|
||||
it("should return null when account not found", async () => {
|
||||
const username = "nonexistentaccount";
|
||||
const result = await accountRepository.findByUsername(username);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("findById", () => {
|
||||
it("should return account when found", async () => {
|
||||
const createAccount = await AccountEntity.create(
|
||||
"test@example.com",
|
||||
"password123",
|
||||
);
|
||||
|
||||
const newId = await accountRepository.insert(createAccount);
|
||||
const result = await accountRepository.findById(newId);
|
||||
|
||||
expect(result).toBeInstanceOf(AccountEntity);
|
||||
expect(result?.id).toBe(newId);
|
||||
});
|
||||
|
||||
it("should return null when account not found", async () => {
|
||||
const id = 999;
|
||||
const result = await accountRepository.findById(id);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("insert", () => {
|
||||
it("should insert account successfully", async () => {
|
||||
const account = await AccountEntity.create(
|
||||
"test@example.com",
|
||||
"password123",
|
||||
);
|
||||
const result = await accountRepository.insert(account);
|
||||
|
||||
expect(result).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
145
tests/account/AccountService.test.ts
Normal file
145
tests/account/AccountService.test.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { describe, it, expect, jest, beforeEach } from "bun:test";
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
AccountAlreadyExistsError,
|
||||
BadPasswordError,
|
||||
} from "../../src/domain/account/errors/AccountErrors";
|
||||
import AccountService from "../../src/domain/account/service/AccountService";
|
||||
import { AccountEntity } from "../../src/domain/account/entity/AccountEntity";
|
||||
|
||||
describe("AccountService", () => {
|
||||
let accountService: AccountService;
|
||||
let mockAccountRepository: {
|
||||
findByUsername: ReturnType<typeof jest.fn>;
|
||||
insert: ReturnType<typeof jest.fn>;
|
||||
findById: ReturnType<typeof jest.fn>;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockAccountRepository = {
|
||||
findByUsername: jest.fn(() => Promise.resolve(null)),
|
||||
insert: jest.fn(() => Promise.resolve("123")),
|
||||
findById: jest.fn(() => Promise.resolve(null)),
|
||||
};
|
||||
accountService = new AccountService(mockAccountRepository as any);
|
||||
});
|
||||
|
||||
describe("createAccount", () => {
|
||||
it("should create a new account successfully", async () => {
|
||||
const username = "testusername";
|
||||
const password = "password123";
|
||||
|
||||
mockAccountRepository.findByUsername.mockImplementation(() =>
|
||||
Promise.resolve(null),
|
||||
);
|
||||
|
||||
mockAccountRepository.findById.mockImplementation(() =>
|
||||
Promise.resolve(
|
||||
new AccountEntity(
|
||||
123,
|
||||
username,
|
||||
"hashedpassword",
|
||||
1,
|
||||
new Date(),
|
||||
new Date(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
mockAccountRepository.insert.mockImplementation(() =>
|
||||
Promise.resolve(123),
|
||||
);
|
||||
|
||||
const result = await accountService.register(username, password);
|
||||
|
||||
expect(result).toBeInstanceOf(AccountEntity);
|
||||
expect(result.username).toBe(username);
|
||||
expect(mockAccountRepository.findByUsername).toHaveBeenCalledWith(
|
||||
username,
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw error if account already exists", async () => {
|
||||
const username = "testusername";
|
||||
const password = "password123";
|
||||
const existingAccount = await AccountEntity.create(username, password);
|
||||
|
||||
mockAccountRepository.findByUsername.mockImplementation(() =>
|
||||
Promise.resolve(existingAccount),
|
||||
);
|
||||
|
||||
expect(accountService.register(username, password)).rejects.toThrow(
|
||||
AccountAlreadyExistsError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("login", () => {
|
||||
it("should login successfully with correct credentials", async () => {
|
||||
const username = "testaccount";
|
||||
const password = "password123";
|
||||
|
||||
const createAccount = await AccountEntity.create(username, password);
|
||||
const account = new AccountEntity(
|
||||
1,
|
||||
createAccount.username,
|
||||
createAccount.hashedPassword,
|
||||
createAccount.roleId,
|
||||
new Date(),
|
||||
new Date(),
|
||||
);
|
||||
|
||||
mockAccountRepository.findByUsername.mockImplementation(() =>
|
||||
Promise.resolve(account),
|
||||
);
|
||||
|
||||
const result = await accountService.login(username, password);
|
||||
|
||||
expect({ username: result.username, roleId: result.roleId }).toEqual({
|
||||
username: account.username,
|
||||
roleId: account.roleId,
|
||||
});
|
||||
|
||||
expect(mockAccountRepository.findByUsername).toHaveBeenCalledWith(
|
||||
username,
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw error if account not found", async () => {
|
||||
const username = "testaccount";
|
||||
const password = "password123";
|
||||
|
||||
mockAccountRepository.findByUsername.mockImplementation(() =>
|
||||
Promise.resolve(null),
|
||||
);
|
||||
|
||||
expect(accountService.login(username, password)).rejects.toThrow(
|
||||
AccountNotFoundError,
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw error if password is incorrect", async () => {
|
||||
const username = "testaccount";
|
||||
const password = "password123";
|
||||
const wrongPassword = "wrongpassword";
|
||||
|
||||
const createAccount = await AccountEntity.create(username, password);
|
||||
const account = new AccountEntity(
|
||||
1,
|
||||
createAccount.username,
|
||||
createAccount.hashedPassword,
|
||||
createAccount.roleId,
|
||||
new Date(),
|
||||
new Date(),
|
||||
);
|
||||
|
||||
mockAccountRepository.findByUsername.mockImplementation(() =>
|
||||
Promise.resolve(account),
|
||||
);
|
||||
|
||||
expect(accountService.login(username, wrongPassword)).rejects.toThrow(
|
||||
BadPasswordError,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,62 +1,14 @@
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import {
|
||||
emailSchema,
|
||||
passwordSchema,
|
||||
loginSchema,
|
||||
registerSchema,
|
||||
EMAIL_REGEX,
|
||||
MIN_PASSWORD_LENGTH,
|
||||
} from "../src/domain/account/validation/AccountValidation";
|
||||
import { AccountEntity } from "../src/domain/account/entity/AccountEntity";
|
||||
import {
|
||||
InvalidEmailFormatError,
|
||||
WeakPasswordError,
|
||||
} from "../src/domain/account/errors/AccountErrors";
|
||||
} from "../../src/domain/account/validation/AccountValidation";
|
||||
import { WeakPasswordError } from "../../src/domain/account/errors/AccountErrors";
|
||||
import { AccountEntity } from "../../src/domain/account/entity/AccountEntity";
|
||||
|
||||
describe("AccountValidation", () => {
|
||||
describe("Email validation consistency", () => {
|
||||
const validEmails = [
|
||||
"test@example.com",
|
||||
"user.name@domain.co.uk",
|
||||
"firstname+lastname@example.org",
|
||||
];
|
||||
|
||||
const invalidEmails = [
|
||||
"invalid-email",
|
||||
"@example.com",
|
||||
"test@",
|
||||
"test",
|
||||
"test@domain",
|
||||
"",
|
||||
];
|
||||
|
||||
it("should validate same emails in Zod and Entity", () => {
|
||||
validEmails.forEach((email) => {
|
||||
expect(emailSchema.safeParse(email).success).toBe(true);
|
||||
expect(() => AccountEntity.create(email, "password123")).not.toThrow(
|
||||
InvalidEmailFormatError,
|
||||
);
|
||||
});
|
||||
|
||||
invalidEmails.forEach((email) => {
|
||||
expect(emailSchema.safeParse(email).success).toBe(false);
|
||||
expect(() => AccountEntity.create(email, "password123")).toThrow(
|
||||
InvalidEmailFormatError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should use same regex pattern", () => {
|
||||
validEmails.forEach((email) => {
|
||||
expect(EMAIL_REGEX.test(email)).toBe(true);
|
||||
});
|
||||
|
||||
invalidEmails.forEach((email) => {
|
||||
expect(EMAIL_REGEX.test(email)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Password validation consistency", () => {
|
||||
const validPasswords = ["password123", "abcdefgh", "12345678", "P@ssw0rd!"];
|
||||
|
||||
@@ -91,8 +43,11 @@ describe("AccountValidation", () => {
|
||||
|
||||
describe("Complete schemas", () => {
|
||||
it("should validate login schema correctly", () => {
|
||||
const validLogin = { email: "test@example.com", password: "password123" };
|
||||
const invalidLogin = { email: "invalid", password: "123" };
|
||||
const validLogin = {
|
||||
username: "testaccount",
|
||||
password: "password123",
|
||||
};
|
||||
const invalidLogin = { username: "ii", password: "123" };
|
||||
|
||||
expect(loginSchema.safeParse(validLogin).success).toBe(true);
|
||||
expect(loginSchema.safeParse(invalidLogin).success).toBe(false);
|
||||
@@ -100,10 +55,10 @@ describe("AccountValidation", () => {
|
||||
|
||||
it("should validate register schema correctly", () => {
|
||||
const validRegister = {
|
||||
email: "test@example.com",
|
||||
username: "testaccount",
|
||||
password: "password123",
|
||||
};
|
||||
const invalidRegister = { email: "invalid", password: "123" };
|
||||
const invalidRegister = { username: "ii", password: "123" };
|
||||
|
||||
expect(registerSchema.safeParse(validRegister).success).toBe(true);
|
||||
expect(registerSchema.safeParse(invalidRegister).success).toBe(false);
|
||||
@@ -121,11 +76,17 @@ describe("AccountValidation", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("should return consistent email error message", () => {
|
||||
const result = emailSchema.safeParse("invalid-email");
|
||||
it("should return consistent username error message", () => {
|
||||
const result = registerSchema.safeParse({
|
||||
username: "ii",
|
||||
password: "validPassword123",
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0]?.message).toBe("Format d'email invalide");
|
||||
expect(result.error.issues[0]?.message).toContain(
|
||||
"doit contenir au moins",
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
20
tests/utils.ts
Normal file
20
tests/utils.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import path from "node:path";
|
||||
import { loadTestConfiguration } from "../src/config";
|
||||
import Migration from "../src/database/Migration";
|
||||
import PgDatabase from "../src/database/PgDatabase";
|
||||
import type { DatabaseInterface } from "../src/database/DatabaseInterface";
|
||||
|
||||
export async function initTestDatabase(): Promise<DatabaseInterface> {
|
||||
const testConfiguration = loadTestConfiguration();
|
||||
|
||||
const database = PgDatabase.fromOptions(testConfiguration.database);
|
||||
await database.ping();
|
||||
|
||||
await database.exec("DROP SCHEMA public CASCADE");
|
||||
await database.exec("CREATE SCHEMA public");
|
||||
|
||||
const migration = new Migration(database);
|
||||
await migration.execute(path.resolve("migrations"));
|
||||
|
||||
return database;
|
||||
}
|
||||
Reference in New Issue
Block a user