brackt/app/routes/leagues/__tests__/team-management.test.ts

455 lines
13 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { removeTeamOwner, assignTeamOwner } from '~/models/team';
import { findAllUsers, isUserAdminByClerkId } from '~/models/user';
// Helper function to create mock team objects with all required properties
const createMockTeam = (overrides: {
id: string;
ownerId: string | null;
name?: string;
seasonId?: string;
draftPosition?: number;
}) => ({
id: overrides.id,
name: overrides.name || `Team ${overrides.id}`,
seasonId: overrides.seasonId || 'season-1',
ownerId: overrides.ownerId,
logoUrl: null,
draftPosition: overrides.draftPosition || 1,
createdAt: new Date(),
updatedAt: new Date(),
});
// Mock the models
vi.mock('~/models/team', () => ({
removeTeamOwner: vi.fn(),
assignTeamOwner: vi.fn(),
findTeamsBySeasonId: vi.fn(),
createManyTeams: vi.fn(),
deleteTeam: vi.fn(),
}));
vi.mock('~/models/user', () => ({
findAllUsers: vi.fn(),
findUserByClerkId: vi.fn(),
isUserAdminByClerkId: vi.fn(),
}));
vi.mock('~/models/league', () => ({
findLeagueById: vi.fn(),
updateLeague: vi.fn(),
deleteLeague: vi.fn(),
}));
vi.mock('~/models/commissioner', () => ({
isCommissioner: vi.fn(),
}));
vi.mock('~/models/season', () => ({
findCurrentSeasonWithSports: vi.fn(),
updateSeason: vi.fn(),
}));
vi.mock('~/models/sports-season', () => ({
findAllSportsSeasons: vi.fn(),
}));
vi.mock('~/models/season-sport', () => ({
unlinkSportFromSeason: vi.fn(),
linkMultipleSportsToSeason: vi.fn(),
}));
vi.mock('~/models/draft-slot', () => ({
findDraftSlotsBySeasonId: vi.fn(),
setDraftOrder: vi.fn(),
randomizeDraftOrder: vi.fn(),
}));
describe('Team Management - Remove Owner', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should successfully remove an owner from a team', async () => {
const teamId = 'team-1';
const mockTeam = createMockTeam({ id: teamId, ownerId: null, name: 'Team 1' });
vi.mocked(removeTeamOwner).mockResolvedValue(mockTeam);
const result = await removeTeamOwner(teamId);
expect(removeTeamOwner).toHaveBeenCalledWith(teamId);
expect(result.ownerId).toBeNull();
expect(result.id).toBe(teamId);
});
it('should handle errors when removing owner fails', async () => {
const teamId = 'team-1';
const error = new Error('Database error');
vi.mocked(removeTeamOwner).mockRejectedValue(error);
await expect(removeTeamOwner(teamId)).rejects.toThrow('Database error');
});
it('should allow commissioners to remove owners', async () => {
// This would be tested in the action handler
const teamId = 'team-1';
const mockTeam = createMockTeam({ id: teamId, ownerId: null, name: 'Team 1' });
vi.mocked(removeTeamOwner).mockResolvedValue(mockTeam);
const result = await removeTeamOwner(teamId);
expect(result.ownerId).toBeNull();
});
});
describe('Team Management - Assign Owner', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should successfully assign an owner to a team', async () => {
const teamId = 'team-1';
const userClerkId = 'user-clerk-123';
const mockTeam = createMockTeam({ id: teamId, ownerId: userClerkId, name: 'Team 1' });
vi.mocked(assignTeamOwner).mockResolvedValue(mockTeam);
const result = await assignTeamOwner(teamId, userClerkId);
expect(assignTeamOwner).toHaveBeenCalledWith(teamId, userClerkId);
expect(result.ownerId).toBe(userClerkId);
expect(result.id).toBe(teamId);
});
it('should handle errors when assigning owner fails', async () => {
const teamId = 'team-1';
const userClerkId = 'user-clerk-123';
const error = new Error('Database error');
vi.mocked(assignTeamOwner).mockRejectedValue(error);
await expect(assignTeamOwner(teamId, userClerkId)).rejects.toThrow('Database error');
});
it('should only allow admins to assign owners', async () => {
const userId = 'admin-user-id';
// Mock admin check
vi.mocked(isUserAdminByClerkId).mockResolvedValue(true);
const isAdmin = await isUserAdminByClerkId(userId);
expect(isAdmin).toBe(true);
expect(isUserAdminByClerkId).toHaveBeenCalledWith(userId);
});
it('should prevent non-admins from assigning owners', async () => {
const userId = 'regular-user-id';
// Mock non-admin check
vi.mocked(isUserAdminByClerkId).mockResolvedValue(false);
const isAdmin = await isUserAdminByClerkId(userId);
expect(isAdmin).toBe(false);
});
it('should replace existing owner when assigning new owner', async () => {
const teamId = 'team-1';
const oldOwnerClerkId = 'user-clerk-old';
const newOwnerClerkId = 'user-clerk-new';
const mockTeam = createMockTeam({ id: teamId, ownerId: newOwnerClerkId, name: 'Team 1' });
vi.mocked(assignTeamOwner).mockResolvedValue(mockTeam);
const result = await assignTeamOwner(teamId, newOwnerClerkId);
expect(result.ownerId).toBe(newOwnerClerkId);
expect(result.ownerId).not.toBe(oldOwnerClerkId);
});
});
describe('Team Management - Admin User List', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should fetch all users for admin dropdown', async () => {
const mockUsers = [
{
id: 'user-1',
clerkId: 'clerk-1',
email: 'user1@example.com',
username: 'user1',
displayName: 'User One',
firstName: 'User',
lastName: 'One',
imageUrl: null,
isAdmin: false,
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: 'user-2',
clerkId: 'clerk-2',
email: 'user2@example.com',
username: 'user2',
displayName: 'User Two',
firstName: 'User',
lastName: 'Two',
imageUrl: null,
isAdmin: false,
createdAt: new Date(),
updatedAt: new Date(),
},
];
vi.mocked(findAllUsers).mockResolvedValue(mockUsers);
const users = await findAllUsers();
expect(findAllUsers).toHaveBeenCalled();
expect(users).toHaveLength(2);
expect(users[0].displayName).toBe('User One');
expect(users[1].displayName).toBe('User Two');
});
it('should only fetch users when user is admin', async () => {
const userId = 'admin-user-id';
vi.mocked(isUserAdminByClerkId).mockResolvedValue(true);
const isAdmin = await isUserAdminByClerkId(userId);
if (isAdmin) {
const mockUsers = [
{
id: 'user-1',
clerkId: 'clerk-1',
email: 'user1@example.com',
username: 'user1',
displayName: 'User One',
firstName: 'User',
lastName: 'One',
imageUrl: null,
isAdmin: false,
createdAt: new Date(),
updatedAt: new Date(),
},
];
vi.mocked(findAllUsers).mockResolvedValue(mockUsers);
const users = await findAllUsers();
expect(users).toHaveLength(1);
}
expect(isAdmin).toBe(true);
});
it('should return empty array for non-admin users', async () => {
const userId = 'regular-user-id';
vi.mocked(isUserAdminByClerkId).mockResolvedValue(false);
const isAdmin = await isUserAdminByClerkId(userId);
// Simulate loader behavior: only fetch users if admin
const users = isAdmin ? await findAllUsers() : [];
expect(isAdmin).toBe(false);
expect(users).toHaveLength(0);
expect(findAllUsers).not.toHaveBeenCalled();
});
});
describe('Team Management - Authorization', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should verify admin status before allowing assignment', async () => {
const userId = 'test-user-id';
vi.mocked(isUserAdminByClerkId).mockResolvedValue(true);
const isAdmin = await isUserAdminByClerkId(userId);
expect(isAdmin).toBe(true);
expect(isUserAdminByClerkId).toHaveBeenCalledWith(userId);
});
it('should reject assignment for non-admin users', async () => {
const userId = 'regular-user-id';
vi.mocked(isUserAdminByClerkId).mockResolvedValue(false);
const isAdmin = await isUserAdminByClerkId(userId);
expect(isAdmin).toBe(false);
// Simulate action handler logic
if (!isAdmin) {
expect(assignTeamOwner).not.toHaveBeenCalled();
}
});
it('should allow commissioners to remove owners regardless of admin status', async () => {
// Commissioners can remove owners even if they're not admins
const teamId = 'team-1';
const mockTeam = createMockTeam({ id: teamId, ownerId: null, name: 'Team 1' });
vi.mocked(removeTeamOwner).mockResolvedValue(mockTeam);
const result = await removeTeamOwner(teamId);
expect(removeTeamOwner).toHaveBeenCalledWith(teamId);
expect(result.ownerId).toBeNull();
});
});
describe('Team Management - Edge Cases', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should prevent assigning user who already owns a team in the league', async () => {
const userClerkId = 'user-clerk-1';
// Simulate that user already owns team-1
const existingTeams = [
createMockTeam({ id: 'team-1', ownerId: userClerkId, name: 'Team 1' }),
createMockTeam({ id: 'team-2', ownerId: null, name: 'Team 2' }),
];
// Check if user already has a team
const userAlreadyHasTeam = existingTeams.some(team => team.ownerId === userClerkId);
expect(userAlreadyHasTeam).toBe(true);
// Should not call assignTeamOwner if user already has a team
if (userAlreadyHasTeam) {
expect(assignTeamOwner).not.toHaveBeenCalled();
}
});
it('should handle removing owner from team that has no owner', async () => {
const teamId = 'team-1';
const mockTeam = createMockTeam({ id: teamId, ownerId: null, name: 'Team 1' });
vi.mocked(removeTeamOwner).mockResolvedValue(mockTeam);
const result = await removeTeamOwner(teamId);
expect(result.ownerId).toBeNull();
});
it('should handle assigning owner to team that already has owner', async () => {
const teamId = 'team-1';
const newOwnerClerkId = 'user-clerk-new';
const mockTeam = createMockTeam({ id: teamId, ownerId: newOwnerClerkId, name: 'Team 1' });
vi.mocked(assignTeamOwner).mockResolvedValue(mockTeam);
const result = await assignTeamOwner(teamId, newOwnerClerkId);
expect(result.ownerId).toBe(newOwnerClerkId);
});
it('should handle invalid team ID gracefully', async () => {
const invalidTeamId = 'invalid-team-id';
const error = new Error('Team not found');
vi.mocked(removeTeamOwner).mockRejectedValue(error);
await expect(removeTeamOwner(invalidTeamId)).rejects.toThrow('Team not found');
});
it('should handle invalid user clerk ID gracefully', async () => {
const teamId = 'team-1';
const invalidUserClerkId = 'invalid-clerk-id';
const error = new Error('User not found');
vi.mocked(assignTeamOwner).mockRejectedValue(error);
await expect(assignTeamOwner(teamId, invalidUserClerkId)).rejects.toThrow('User not found');
});
});
describe('Team Management - Integration Scenarios', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should handle complete workflow: assign then remove owner', async () => {
const teamId = 'team-1';
const userClerkId = 'user-clerk-123';
// First, assign owner
const assignedTeam = createMockTeam({ id: teamId, ownerId: userClerkId, name: 'Team 1' });
vi.mocked(assignTeamOwner).mockResolvedValue(assignedTeam);
const assignResult = await assignTeamOwner(teamId, userClerkId);
expect(assignResult.ownerId).toBe(userClerkId);
// Then, remove owner
const removedTeam = createMockTeam({ id: teamId, ownerId: null, name: 'Team 1' });
vi.mocked(removeTeamOwner).mockResolvedValue(removedTeam);
const removeResult = await removeTeamOwner(teamId);
expect(removeResult.ownerId).toBeNull();
});
it('should handle reassigning owner from one user to another', async () => {
const teamId = 'team-1';
const firstUserClerkId = 'user-clerk-1';
const secondUserClerkId = 'user-clerk-2';
// Assign first owner
const firstAssignment = createMockTeam({ id: teamId, ownerId: firstUserClerkId, name: 'Team 1' });
vi.mocked(assignTeamOwner).mockResolvedValueOnce(firstAssignment);
const firstResult = await assignTeamOwner(teamId, firstUserClerkId);
expect(firstResult.ownerId).toBe(firstUserClerkId);
// Reassign to second owner
const secondAssignment = createMockTeam({ id: teamId, ownerId: secondUserClerkId, name: 'Team 1' });
vi.mocked(assignTeamOwner).mockResolvedValueOnce(secondAssignment);
const secondResult = await assignTeamOwner(teamId, secondUserClerkId);
expect(secondResult.ownerId).toBe(secondUserClerkId);
expect(secondResult.ownerId).not.toBe(firstUserClerkId);
});
it('should handle multiple teams with different ownership states', async () => {
const teams = [
{ id: 'team-1', ownerId: 'user-1' },
{ id: 'team-2', ownerId: null },
{ id: 'team-3', ownerId: 'user-2' },
];
// Remove owner from team-1
vi.mocked(removeTeamOwner).mockResolvedValueOnce(
createMockTeam({ id: 'team-1', ownerId: null, name: 'Team 1', draftPosition: 1 })
);
const result1 = await removeTeamOwner('team-1');
expect(result1.ownerId).toBeNull();
// Assign owner to team-2
vi.mocked(assignTeamOwner).mockResolvedValueOnce(
createMockTeam({ id: 'team-2', ownerId: 'user-3', name: 'Team 2', draftPosition: 2 })
);
const result2 = await assignTeamOwner('team-2', 'user-3');
expect(result2.ownerId).toBe('user-3');
// Leave team-3 unchanged
expect(teams[2].ownerId).toBe('user-2');
});
});