test: add comprehensive tests for player deduplication, permissions, and user management
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
/**
|
||||
* Unit Tests: Tournament Permissions
|
||||
*
|
||||
* Tests the permission system for tournament management
|
||||
* Regression tests for the issue where tournament_admin users were redirected to login
|
||||
*/
|
||||
|
||||
import { describe, test, expect, vi } from 'vitest';
|
||||
import { canManageTournament, ownsTournament, hasRole, getManageableTournaments } from '@/lib/permissions';
|
||||
import { getSession } from '@/lib/auth-simple';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import type { User, Event } from '@prisma/client';
|
||||
|
||||
// Mock the getSession and prisma functions
|
||||
vi.mock('@/lib/auth-simple', () => ({
|
||||
getSession: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
user: {
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
event: {
|
||||
findUnique: vi.fn(),
|
||||
findMany: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// Helper to create mock user
|
||||
const createMockUser = (id: string, email: string, role: string): User => ({
|
||||
id,
|
||||
email,
|
||||
role,
|
||||
emailVerified: false,
|
||||
name: null,
|
||||
image: null,
|
||||
playerId: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
// Helper to create mock tournament
|
||||
const createMockTournament = (id: number, ownerId: string | null): Event => ({
|
||||
id,
|
||||
name: 'Test Tournament',
|
||||
description: null,
|
||||
eventDate: new Date(),
|
||||
eventType: 'tournament',
|
||||
format: 'round_robin',
|
||||
status: 'planned',
|
||||
maxParticipants: null,
|
||||
ownerId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
describe('Tournament Permissions', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('canManageTournament', () => {
|
||||
test('should allow club_admin to manage any tournament', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(
|
||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
const result = await canManageTournament(999);
|
||||
expect(result.allowed).toBe(true);
|
||||
});
|
||||
|
||||
test('should allow tournament_admin to manage their own tournament', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
vi.mocked(prisma.event.findUnique).mockResolvedValue(
|
||||
createMockTournament(1, 'tour-admin-1')
|
||||
);
|
||||
|
||||
const result = await canManageTournament(1);
|
||||
expect(result.allowed).toBe(true);
|
||||
});
|
||||
|
||||
test('should deny tournament_admin from managing other users tournaments', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
vi.mocked(prisma.event.findUnique).mockResolvedValue(
|
||||
createMockTournament(1, 'other-user-1')
|
||||
);
|
||||
|
||||
const result = await canManageTournament(1);
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.reason).toContain('does not own this tournament');
|
||||
});
|
||||
|
||||
test('should deny player from managing tournaments', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'player-1', email: 'player@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
|
||||
const result = await canManageTournament(999);
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.reason).toContain('Insufficient permissions');
|
||||
});
|
||||
|
||||
test('should deny unauthenticated user', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(null);
|
||||
|
||||
const result = await canManageTournament(999);
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.reason).toContain('Not authenticated');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ownsTournament', () => {
|
||||
test('should return true if user owns tournament', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'owner-1', email: 'owner@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.event.findUnique).mockResolvedValue(
|
||||
createMockTournament(1, 'owner-1')
|
||||
);
|
||||
|
||||
const result = await ownsTournament(1);
|
||||
expect(result.allowed).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false if user does not own tournament', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'non-owner-1', email: 'nonowner@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.event.findUnique).mockResolvedValue(
|
||||
createMockTournament(1, 'owner-1')
|
||||
);
|
||||
|
||||
const result = await ownsTournament(1);
|
||||
expect(result.allowed).toBe(false);
|
||||
expect(result.reason).toContain('does not own this tournament');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getManageableTournaments', () => {
|
||||
test('should return all tournaments for club_admin', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'admin-1', email: 'admin@example.com', role: 'club_admin' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
|
||||
const mockTournaments = [
|
||||
createMockTournament(1, 'user-1'),
|
||||
createMockTournament(2, 'user-2'),
|
||||
createMockTournament(3, 'user-3'),
|
||||
];
|
||||
vi.mocked(prisma.event.findMany).mockResolvedValue(mockTournaments);
|
||||
|
||||
const result = await getManageableTournaments();
|
||||
expect(result).toEqual(mockTournaments);
|
||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
||||
where: { eventType: 'tournament' },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
});
|
||||
|
||||
test('should return only owned tournaments for tournament_admin', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com', role: 'tournament_admin' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
|
||||
const mockTournaments = [
|
||||
createMockTournament(1, 'tour-admin-1'),
|
||||
createMockTournament(2, 'tour-admin-1'),
|
||||
];
|
||||
vi.mocked(prisma.event.findMany).mockResolvedValue(mockTournaments);
|
||||
|
||||
const result = await getManageableTournaments();
|
||||
expect(result).toEqual(mockTournaments);
|
||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
eventType: 'tournament',
|
||||
ownerId: 'tour-admin-1'
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
});
|
||||
|
||||
test('should return only non-draft tournaments for players', async () => {
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'player-1', email: 'player@example.com', role: 'player' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
|
||||
const mockTournaments = [
|
||||
createMockTournament(1, 'user-1'),
|
||||
createMockTournament(2, 'user-2'),
|
||||
];
|
||||
vi.mocked(prisma.event.findMany).mockResolvedValue(mockTournaments);
|
||||
|
||||
const result = await getManageableTournaments();
|
||||
expect(result).toEqual(mockTournaments);
|
||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
eventType: 'tournament',
|
||||
status: { not: 'draft' }
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Regression Tests', () => {
|
||||
test('tournament_admin should be able to access edit page for their tournament', async () => {
|
||||
// This simulates the scenario where a tournament_admin user
|
||||
// clicks "Edit" on a tournament they own
|
||||
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
vi.mocked(prisma.event.findUnique).mockResolvedValue(
|
||||
createMockTournament(1, 'tour-admin-1')
|
||||
);
|
||||
|
||||
// This is the permission check that happens on the edit page
|
||||
const result = await canManageTournament(1);
|
||||
|
||||
// Before the fix, this would return false because the page only checked for club_admin
|
||||
// After the fix, it should return true because the user owns the tournament
|
||||
expect(result.allowed).toBe(true);
|
||||
});
|
||||
|
||||
test('club_admin should still be able to manage any tournament', async () => {
|
||||
// This ensures we didn't break the existing club_admin functionality
|
||||
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'club-admin-1', email: 'club@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(
|
||||
createMockUser('club-admin-1', 'club@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
const result = await canManageTournament(999);
|
||||
expect(result.allowed).toBe(true);
|
||||
});
|
||||
|
||||
test('players should still be denied from managing tournaments', async () => {
|
||||
// This ensures we didn't accidentally grant players access
|
||||
|
||||
vi.mocked(getSession).mockResolvedValue({
|
||||
user: { id: 'player-1', email: 'player@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValue(
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
|
||||
const result = await canManageTournament(1);
|
||||
expect(result.allowed).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user