feat: initial commit

This commit is contained in:
2026-04-09 20:52:10 -03:00
commit 1a92cc6c11
86 changed files with 19533 additions and 0 deletions

View File

@@ -0,0 +1,244 @@
import { SessionClaims } from '@/lib/feature/user/clerk.model';
import {
CreateUserModel,
UpdateUserModel,
} from '@/lib/feature/user/user.model';
import {
getUserByEmail,
saveUser,
syncUser,
updateUser,
} from '@/lib/feature/user/user.service';
import { clerkClient } from '@clerk/nextjs/server';
import { AbstractStartedContainer } from 'testcontainers';
import { startTestDB } from '~/tests/setup/setup-db';
jest.mock('@clerk/nextjs/server', () => ({
clerkClient: jest.fn(),
}));
describe('UserService', () => {
let container: AbstractStartedContainer;
beforeAll(async () => {
container = await startTestDB();
}, 1_000_000);
afterAll(async () => {
await container.stop();
}, 1_000_000);
test('can save user', async () => {
const userToSave: CreateUserModel = {
name: 'Test User',
email: 'test@email.com',
role: 'user',
};
const savedUser = await saveUser(userToSave);
expect(savedUser.id).toBeDefined();
expect(savedUser.name).toBe(userToSave.name);
expect(savedUser.email).toBe(userToSave.email);
expect(savedUser.role).toBe(userToSave.role);
expect(savedUser.role).toBe(userToSave.role);
expect(savedUser.externalId).toBeDefined(); // Default to true if not set
});
test('cannot save user with existing email', async () => {
const userToSave: CreateUserModel = {
name: 'Test User',
email: 'test@email.com',
role: 'user',
};
await expect(saveUser(userToSave)).rejects.toThrow(
`User with email ${userToSave.email} already exists`
);
});
test('can getUserByEmail', async () => {
const user = await getUserByEmail('test@email.com');
expect(user).toBeDefined();
expect(user?.email).toBe('test@email.com');
expect(user?.name).toBe('Test User');
expect(user?.role).toBe('user');
expect(user?.externalId).toBeDefined(); // Default to true if not set
});
test('cannot getUserByEmail with non-existing email', async () => {
await expect(getUserByEmail('missing@email.com')).resolves.toBeNull();
});
test('can update user', async () => {
const dataToUpdate: UpdateUserModel = {
name: 'Updated User',
role: 'admin',
};
const user = await getUserByEmail('test@email.com');
expect(user).toBeDefined();
const updatedUser = await updateUser(user!.id, dataToUpdate);
expect(updatedUser).toBeDefined();
expect(updatedUser.id).toBe(user!.id);
expect(updatedUser.name).toBe(dataToUpdate.name);
expect(updatedUser.role).toBe(dataToUpdate.role);
expect(updatedUser.email).toBe(user!.email);
expect(updatedUser.externalId).toBeDefined(); // Default to true if not set
});
test('cannot update non-existing user', async () => {
const dataToUpdate: UpdateUserModel = {
name: 'Updated User',
role: 'admin',
};
await expect(updateUser('9999', dataToUpdate)).rejects.toThrow(
`User with ID 9999 not found`
);
});
test('can sync admin user', async () => {
process.env.CLERK_ORG_ID = 'test-org-id';
const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({
data: [
{
organization: {
id: process.env.CLERK_ORG_ID,
},
role: 'org:admin',
},
],
});
(clerkClient as jest.Mock).mockResolvedValue({
users: {
getOrganizationMembershipList:
mockGetOrganizationMembershipList,
},
});
const sessionClaims = {
user: {
full_name: 'Updated Name',
email: 'test@email.com',
username: 'test',
public_metadata: {
role: 'admin',
},
},
} as SessionClaims;
const syncedUser = await syncUser(sessionClaims);
expect(syncedUser).toBeDefined();
expect(syncedUser.name).toBe('Updated Name');
expect(syncedUser.email).toBe('test@email.com');
expect(syncedUser.role).toBe('admin');
});
test('can sync internal user', async () => {
process.env.CLERK_ORG_ID = 'test-org-id';
const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({
data: [
{
organization: {
id: process.env.CLERK_ORG_ID,
},
role: 'org:member',
},
],
});
(clerkClient as jest.Mock).mockResolvedValue({
users: {
getOrganizationMembershipList:
mockGetOrganizationMembershipList,
},
});
const sessionClaims = {
user: {
full_name: 'Updated Name',
email: 'test@email.com',
username: 'test',
public_metadata: {
role: 'internal',
},
},
} as SessionClaims;
const syncedUser = await syncUser(sessionClaims);
expect(syncedUser).toBeDefined();
expect(syncedUser.name).toBe('Updated Name');
expect(syncedUser.email).toBe('test@email.com');
expect(syncedUser.role).toBe('internal');
});
test('can sync user', async () => {
process.env.CLERK_ORG_ID = 'test-org-id';
const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({
data: [],
});
(clerkClient as jest.Mock).mockResolvedValue({
users: {
getOrganizationMembershipList:
mockGetOrganizationMembershipList,
},
});
const sessionClaims = {
user: {
full_name: 'Updated Name',
email: 'test@email.com',
username: 'test',
public_metadata: {
role: 'user',
},
},
} as SessionClaims;
const syncedUser = await syncUser(sessionClaims);
expect(syncedUser).toBeDefined();
expect(syncedUser.name).toBe('Updated Name');
expect(syncedUser.email).toBe('test@email.com');
expect(syncedUser.role).toBe('user');
});
test('can sync saving new user', async () => {
process.env.CLERK_ORG_ID = 'test-org-id';
const mockGetOrganizationMembershipList = jest.fn().mockResolvedValue({
data: [],
});
(clerkClient as jest.Mock).mockResolvedValue({
users: {
getOrganizationMembershipList:
mockGetOrganizationMembershipList,
},
});
const sessionClaims = {
user: {
full_name: 'Updated Name',
email: 'new@email.com',
username: 'test',
public_metadata: {
role: 'user',
},
},
} as SessionClaims;
const syncedUser = await syncUser(sessionClaims);
expect(syncedUser).toBeDefined();
expect(syncedUser.name).toBe('Updated Name');
expect(syncedUser.email).toBe('new@email.com');
expect(syncedUser.role).toBe('user');
});
});

21
tests/setup/setup-db.ts Normal file
View File

@@ -0,0 +1,21 @@
import { configAppDataSource } from '@/lib/db/data-source';
import { PostgreSqlContainer } from '@testcontainers/postgresql';
import 'reflect-metadata';
const runMigrations = async (url: string) => {
const dataSource = configAppDataSource(url);
if (!dataSource.isInitialized) {
await dataSource.initialize();
}
await dataSource.runMigrations();
};
export const startTestDB = async () => {
const container = await new PostgreSqlContainer('postgres:16').start();
await runMigrations(container.getConnectionUri());
return container;
};