refactor: improve test structure for Bun compatibility
- Update all test files to use named mock variables instead of inline mocks - Clear mock history in beforeEach instead of using mock.restore() - Add default mock implementations stored at module level - Document that tests should not use --randomize flag due to mock.module() limitations - All 89 unit tests pass consistently without randomization
This commit is contained in:
@@ -4,12 +4,33 @@
|
||||
* Tests the permission system for tournament management
|
||||
*/
|
||||
|
||||
import { describe, test, expect, mock } from 'bun:test';
|
||||
import { describe, test, expect, mock, beforeEach } from 'bun:test';
|
||||
import { hasRole, canManageTournament, canCreateTournaments } from '@/lib/permissions';
|
||||
import { getSession } from '@/lib/auth-simple';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import type { User } from '@prisma/client';
|
||||
|
||||
// Create mock functions at module level
|
||||
const getSessionMock = mock(() => {});
|
||||
const userFindUniqueMock = mock(() => {});
|
||||
const eventFindUniqueMock = mock(() => {});
|
||||
|
||||
// Mock the getSession and prisma functions
|
||||
mock.module('@/lib/auth-simple', () => ({
|
||||
getSession: getSessionMock,
|
||||
}));
|
||||
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
user: {
|
||||
findUnique: userFindUniqueMock,
|
||||
},
|
||||
event: {
|
||||
findUnique: eventFindUniqueMock,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// Helper to create mock user
|
||||
const createMockUser = (id: string, email: string, role: string): User => ({
|
||||
id,
|
||||
@@ -23,30 +44,21 @@ const createMockUser = (id: string, email: string, role: string): User => ({
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
// Mock the getSession and prisma functions
|
||||
mock.module('@/lib/auth-simple', () => ({
|
||||
getSession: mock(() => {}),
|
||||
}));
|
||||
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
user: {
|
||||
findUnique: mock(() => {}),
|
||||
},
|
||||
event: {
|
||||
findUnique: mock(() => {}),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('Permissions', () => {
|
||||
beforeEach(() => {
|
||||
// Reset mock implementations to default (no-op) before each test
|
||||
getSessionMock.mockImplementation(() => undefined);
|
||||
userFindUniqueMock.mockImplementation(() => undefined);
|
||||
eventFindUniqueMock.mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
describe('hasRole', () => {
|
||||
test('should allow club_admin to access tournament_admin resources', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: '1', email: 'test@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('1', 'test@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
@@ -55,11 +67,11 @@ describe('Permissions', () => {
|
||||
});
|
||||
|
||||
test('should deny player from accessing tournament_admin resources', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: '1', email: 'test@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('1', 'test@example.com', 'player')
|
||||
);
|
||||
|
||||
@@ -68,7 +80,7 @@ describe('Permissions', () => {
|
||||
});
|
||||
|
||||
test('should deny unauthenticated user', async () => {
|
||||
getSession.mockImplementation(async () => null);
|
||||
getSessionMock.mockImplementation(async () => null);
|
||||
|
||||
const result = await hasRole('club_admin');
|
||||
expect(result.allowed).toBe(false);
|
||||
@@ -78,11 +90,11 @@ describe('Permissions', () => {
|
||||
|
||||
describe('canManageTournament', () => {
|
||||
test('should allow club_admin to manage any tournament', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
@@ -91,11 +103,11 @@ describe('Permissions', () => {
|
||||
});
|
||||
|
||||
test('should deny player from managing tournaments', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'player-1', email: 'player@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
|
||||
@@ -107,11 +119,11 @@ describe('Permissions', () => {
|
||||
|
||||
describe('canCreateTournaments', () => {
|
||||
test('should allow tournament_admin to create tournaments', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('admin-1', 'admin@example.com', 'tournament_admin')
|
||||
);
|
||||
|
||||
@@ -120,11 +132,11 @@ describe('Permissions', () => {
|
||||
});
|
||||
|
||||
test('should allow club_admin to create tournaments', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
@@ -133,11 +145,11 @@ describe('Permissions', () => {
|
||||
});
|
||||
|
||||
test('should deny player from creating tournaments', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'player-1', email: 'player@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
|
||||
|
||||
@@ -4,15 +4,19 @@
|
||||
* Tests the player deduplication logic for CSV uploads
|
||||
*/
|
||||
|
||||
import { describe, test, expect, mock, beforeEach } from 'bun:test';
|
||||
import { describe, test, expect, mock, beforeEach,} from 'bun:test';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
// Create mock functions at module level
|
||||
const playerFindFirstMock = mock(() => {});
|
||||
const playerCreateMock = mock(() => {});
|
||||
|
||||
// Mock the prisma module
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
player: {
|
||||
findFirst: mock(() => {}),
|
||||
create: mock(() => {}),
|
||||
findFirst: playerFindFirstMock,
|
||||
create: playerCreateMock,
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -46,7 +50,9 @@ async function findOrCreatePlayer(name: string) {
|
||||
|
||||
describe('Player Deduplication', () => {
|
||||
beforeEach(() => {
|
||||
mock.clearAllMocks();
|
||||
// Clear all mock history before each test
|
||||
playerFindFirstMock.mockClear();
|
||||
playerCreateMock.mockClear();
|
||||
});
|
||||
|
||||
describe('findOrCreatePlayer', () => {
|
||||
@@ -64,7 +70,7 @@ describe('Player Deduplication', () => {
|
||||
rating: 0,
|
||||
};
|
||||
|
||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
||||
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||
|
||||
const result = await findOrCreatePlayer('Emily');
|
||||
|
||||
@@ -89,7 +95,7 @@ describe('Player Deduplication', () => {
|
||||
rating: 0,
|
||||
};
|
||||
|
||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
||||
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||
|
||||
const result = await findOrCreatePlayer('EMILY');
|
||||
|
||||
@@ -114,7 +120,7 @@ describe('Player Deduplication', () => {
|
||||
rating: 0,
|
||||
};
|
||||
|
||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
||||
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||
|
||||
const result = await findOrCreatePlayer(' Emily ');
|
||||
|
||||
@@ -126,7 +132,7 @@ describe('Player Deduplication', () => {
|
||||
});
|
||||
|
||||
test('should create new player if not found', async () => {
|
||||
prisma.player.findFirst.mockImplementation(async () => null);
|
||||
playerFindFirstMock.mockImplementation(async () => null);
|
||||
|
||||
const newPlayer = {
|
||||
id: 100,
|
||||
@@ -141,7 +147,7 @@ describe('Player Deduplication', () => {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
||||
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||
|
||||
const result = await findOrCreatePlayer('NewPlayer');
|
||||
|
||||
@@ -162,7 +168,7 @@ describe('Player Deduplication', () => {
|
||||
});
|
||||
|
||||
test('should handle names with special characters', async () => {
|
||||
prisma.player.findFirst.mockImplementation(async () => null);
|
||||
playerFindFirstMock.mockImplementation(async () => null);
|
||||
|
||||
const newPlayer = {
|
||||
id: 100,
|
||||
@@ -177,7 +183,7 @@ describe('Player Deduplication', () => {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
||||
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||
|
||||
const result = await findOrCreatePlayer('Test-Player_123');
|
||||
|
||||
@@ -195,7 +201,7 @@ describe('Player Deduplication', () => {
|
||||
});
|
||||
|
||||
test('should handle names with spaces', async () => {
|
||||
prisma.player.findFirst.mockImplementation(async () => null);
|
||||
playerFindFirstMock.mockImplementation(async () => null);
|
||||
|
||||
const newPlayer = {
|
||||
id: 100,
|
||||
@@ -210,7 +216,7 @@ describe('Player Deduplication', () => {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
||||
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||
|
||||
const result = await findOrCreatePlayer('Dave B');
|
||||
|
||||
@@ -242,7 +248,7 @@ describe('Player Deduplication', () => {
|
||||
rating: 0,
|
||||
};
|
||||
|
||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
||||
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||
|
||||
const result1 = await findOrCreatePlayer('EMILY');
|
||||
const result2 = await findOrCreatePlayer('Emily');
|
||||
@@ -255,7 +261,7 @@ describe('Player Deduplication', () => {
|
||||
});
|
||||
|
||||
test('should handle empty or whitespace-only names', async () => {
|
||||
prisma.player.findFirst.mockImplementation(async () => null);
|
||||
playerFindFirstMock.mockImplementation(async () => null);
|
||||
|
||||
const newPlayer = {
|
||||
id: 100,
|
||||
@@ -270,7 +276,7 @@ describe('Player Deduplication', () => {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
||||
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||
|
||||
const result = await findOrCreatePlayer(' ');
|
||||
|
||||
@@ -320,7 +326,7 @@ describe('Player Deduplication', () => {
|
||||
rating: 0,
|
||||
};
|
||||
|
||||
prisma.player.findFirst
|
||||
playerFindFirstMock
|
||||
.mockResolvedValueOnce(existingPlayer) // First call for "Emily"
|
||||
.mockResolvedValueOnce(existingPlayer) // Second call for "EMILY"
|
||||
.mockResolvedValueOnce(existingPlayer); // Third call for " Emily "
|
||||
|
||||
@@ -11,20 +11,26 @@ import { describe, test, expect, mock, beforeEach } from 'bun:test';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import type { Player, Event, Match, PartnershipStat } from '@prisma/client';
|
||||
|
||||
// Create mock functions at module level
|
||||
const playerFindUniqueMock = mock(() => {});
|
||||
const eventFindManyMock = mock(() => {});
|
||||
const matchFindManyMock = mock(() => {});
|
||||
const partnershipStatFindManyMock = mock(() => {});
|
||||
|
||||
// Mock the prisma module
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
player: {
|
||||
findUnique: mock(() => {}),
|
||||
findUnique: playerFindUniqueMock,
|
||||
},
|
||||
event: {
|
||||
findMany: mock(() => {}),
|
||||
findMany: eventFindManyMock,
|
||||
},
|
||||
match: {
|
||||
findMany: mock(() => {}),
|
||||
findMany: matchFindManyMock,
|
||||
},
|
||||
partnershipStat: {
|
||||
findMany: mock(() => {}),
|
||||
findMany: partnershipStatFindManyMock,
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -111,7 +117,11 @@ const createMockPartnershipStat = (
|
||||
|
||||
describe('Player Profile Enhancements', () => {
|
||||
beforeEach(() => {
|
||||
mock.clearAllMocks();
|
||||
// Reset mock implementations to default (no-op) before each test
|
||||
playerFindUniqueMock.mockImplementation(() => undefined);
|
||||
eventFindManyMock.mockImplementation(() => undefined);
|
||||
matchFindManyMock.mockImplementation(() => undefined);
|
||||
partnershipStatFindManyMock.mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
describe('Tournaments Participated', () => {
|
||||
@@ -122,8 +132,8 @@ describe('Player Profile Enhancements', () => {
|
||||
createMockTournament(2, 'Tournament B'),
|
||||
];
|
||||
|
||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
||||
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||
|
||||
// Simulate the query that would be run
|
||||
const tournaments = await prisma.event.findMany({
|
||||
@@ -145,8 +155,8 @@ describe('Player Profile Enhancements', () => {
|
||||
test('should return empty array if player has no tournaments', async () => {
|
||||
const mockPlayer = createMockPlayer(1, 'Test Player');
|
||||
|
||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
||||
prisma.event.findMany.mockImplementation(async () => []);
|
||||
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||
eventFindManyMock.mockImplementation(async () => []);
|
||||
|
||||
const tournaments = await prisma.event.findMany({
|
||||
where: {
|
||||
@@ -173,8 +183,8 @@ describe('Player Profile Enhancements', () => {
|
||||
createMockMatch(2, 1, 3, 5, 6, 4, 4),
|
||||
];
|
||||
|
||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
||||
prisma.match.findMany.mockImplementation(async () => mockMatches);
|
||||
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||
matchFindManyMock.mockImplementation(async () => mockMatches);
|
||||
|
||||
// Simulate the query that would be run
|
||||
const matches = await prisma.match.findMany({
|
||||
@@ -204,8 +214,8 @@ describe('Player Profile Enhancements', () => {
|
||||
test('should return empty array if player has no matches', async () => {
|
||||
const mockPlayer = createMockPlayer(1, 'Test Player');
|
||||
|
||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
||||
prisma.match.findMany.mockImplementation(async () => []);
|
||||
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||
matchFindManyMock.mockImplementation(async () => []);
|
||||
|
||||
const matches = await prisma.match.findMany({
|
||||
where: {
|
||||
@@ -240,8 +250,8 @@ describe('Player Profile Enhancements', () => {
|
||||
createMockPartnershipStat(2, 1, 3, 5, 2, 3),
|
||||
];
|
||||
|
||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
||||
prisma.partnershipStat.findMany.mockImplementation(async () => mockPartnershipStats);
|
||||
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||
partnershipStatFindManyMock.mockImplementation(async () => mockPartnershipStats);
|
||||
|
||||
// Simulate the query that would be run
|
||||
const partnershipStats = await prisma.partnershipStat.findMany({
|
||||
@@ -265,8 +275,8 @@ describe('Player Profile Enhancements', () => {
|
||||
test('should return empty array if player has no partnership data', async () => {
|
||||
const mockPlayer = createMockPlayer(1, 'Test Player');
|
||||
|
||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
||||
prisma.partnershipStat.findMany.mockImplementation(async () => []);
|
||||
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||
partnershipStatFindManyMock.mockImplementation(async () => []);
|
||||
|
||||
const partnershipStats = await prisma.partnershipStat.findMany({
|
||||
where: {
|
||||
|
||||
@@ -11,19 +11,25 @@ import { getSession } from '@/lib/auth-simple';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import type { User, Event } from '@prisma/client';
|
||||
|
||||
// Create mock functions first
|
||||
const getSessionMock = mock(() => {});
|
||||
const userFindUniqueMock = mock(() => {});
|
||||
const eventFindUniqueMock = mock(() => {});
|
||||
const eventFindManyMock = mock(() => {});
|
||||
|
||||
// Mock the getSession and prisma functions
|
||||
mock.module('@/lib/auth-simple', () => ({
|
||||
getSession: mock(() => {}),
|
||||
getSession: getSessionMock,
|
||||
}));
|
||||
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
user: {
|
||||
findUnique: mock(() => {}),
|
||||
findUnique: userFindUniqueMock,
|
||||
},
|
||||
event: {
|
||||
findUnique: mock(() => {}),
|
||||
findMany: mock(() => {}),
|
||||
findUnique: eventFindUniqueMock,
|
||||
findMany: eventFindManyMock,
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -61,16 +67,21 @@ const createMockTournament = (id: number, ownerId: string | null): Event => ({
|
||||
|
||||
describe('Tournament Permissions', () => {
|
||||
beforeEach(() => {
|
||||
mock.clearAllMocks();
|
||||
// Reset mock implementations to default (no-op) before each test
|
||||
// This prevents pollution from previous tests
|
||||
getSessionMock.mockImplementation(() => undefined);
|
||||
userFindUniqueMock.mockImplementation(() => undefined);
|
||||
eventFindUniqueMock.mockImplementation(() => undefined);
|
||||
eventFindManyMock.mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
describe('canManageTournament', () => {
|
||||
test('should allow club_admin to manage any tournament', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
@@ -79,14 +90,14 @@ describe('Tournament Permissions', () => {
|
||||
});
|
||||
|
||||
test('should allow tournament_admin to manage their own tournament', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
prisma.event.findUnique.mockImplementation(async () =>
|
||||
eventFindUniqueMock.mockImplementation(async () =>
|
||||
createMockTournament(1, 'tour-admin-1')
|
||||
);
|
||||
|
||||
@@ -95,14 +106,14 @@ describe('Tournament Permissions', () => {
|
||||
});
|
||||
|
||||
test('should deny tournament_admin from managing other users tournaments', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
prisma.event.findUnique.mockImplementation(async () =>
|
||||
eventFindUniqueMock.mockImplementation(async () =>
|
||||
createMockTournament(1, 'other-user-1')
|
||||
);
|
||||
|
||||
@@ -112,11 +123,11 @@ describe('Tournament Permissions', () => {
|
||||
});
|
||||
|
||||
test('should deny player from managing tournaments', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'player-1', email: 'player@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
|
||||
@@ -126,7 +137,7 @@ describe('Tournament Permissions', () => {
|
||||
});
|
||||
|
||||
test('should deny unauthenticated user', async () => {
|
||||
getSession.mockImplementation(async () => null);
|
||||
getSessionMock.mockImplementation(async () => null);
|
||||
|
||||
const result = await canManageTournament(999);
|
||||
expect(result.allowed).toBe(false);
|
||||
@@ -136,11 +147,11 @@ describe('Tournament Permissions', () => {
|
||||
|
||||
describe('ownsTournament', () => {
|
||||
test('should return true if user owns tournament', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'owner-1', email: 'owner@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.event.findUnique.mockImplementation(async () =>
|
||||
eventFindUniqueMock.mockImplementation(async () =>
|
||||
createMockTournament(1, 'owner-1')
|
||||
);
|
||||
|
||||
@@ -149,11 +160,11 @@ describe('Tournament Permissions', () => {
|
||||
});
|
||||
|
||||
test('should return false if user does not own tournament', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'non-owner-1', email: 'nonowner@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.event.findUnique.mockImplementation(async () =>
|
||||
eventFindUniqueMock.mockImplementation(async () =>
|
||||
createMockTournament(1, 'owner-1')
|
||||
);
|
||||
|
||||
@@ -165,11 +176,11 @@ describe('Tournament Permissions', () => {
|
||||
|
||||
describe('getManageableTournaments', () => {
|
||||
test('should return all tournaments for club_admin', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
@@ -178,11 +189,11 @@ describe('Tournament Permissions', () => {
|
||||
createMockTournament(2, 'user-2'),
|
||||
createMockTournament(3, 'user-3'),
|
||||
];
|
||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
||||
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||
|
||||
const result = await getManageableTournaments();
|
||||
expect(result).toEqual(mockTournaments);
|
||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
||||
expect(eventFindManyMock).toHaveBeenCalledWith({
|
||||
where: { eventType: 'tournament' },
|
||||
include: { participants: true },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
@@ -190,11 +201,11 @@ describe('Tournament Permissions', () => {
|
||||
});
|
||||
|
||||
test('should return only owned tournaments for tournament_admin', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
|
||||
@@ -202,11 +213,11 @@ describe('Tournament Permissions', () => {
|
||||
createMockTournament(1, 'tour-admin-1'),
|
||||
createMockTournament(2, 'tour-admin-1'),
|
||||
];
|
||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
||||
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||
|
||||
const result = await getManageableTournaments();
|
||||
expect(result).toEqual(mockTournaments);
|
||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
||||
expect(eventFindManyMock).toHaveBeenCalledWith({
|
||||
where: {
|
||||
eventType: 'tournament',
|
||||
ownerId: 'tour-admin-1'
|
||||
@@ -217,11 +228,11 @@ describe('Tournament Permissions', () => {
|
||||
});
|
||||
|
||||
test('should return only non-draft tournaments for players', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'player-1', email: 'player@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
|
||||
@@ -229,11 +240,11 @@ describe('Tournament Permissions', () => {
|
||||
createMockTournament(1, 'user-1'),
|
||||
createMockTournament(2, 'user-2'),
|
||||
];
|
||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
||||
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||
|
||||
const result = await getManageableTournaments();
|
||||
expect(result).toEqual(mockTournaments);
|
||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
||||
expect(eventFindManyMock).toHaveBeenCalledWith({
|
||||
where: {
|
||||
eventType: 'tournament',
|
||||
status: { not: 'draft' }
|
||||
@@ -249,14 +260,14 @@ describe('Tournament Permissions', () => {
|
||||
// This simulates the scenario where a tournament_admin user
|
||||
// clicks "Edit" on a tournament they own
|
||||
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
prisma.event.findUnique.mockImplementation(async () =>
|
||||
eventFindUniqueMock.mockImplementation(async () =>
|
||||
createMockTournament(1, 'tour-admin-1')
|
||||
);
|
||||
|
||||
@@ -271,11 +282,11 @@ describe('Tournament Permissions', () => {
|
||||
test('club_admin should still be able to manage any tournament', async () => {
|
||||
// This ensures we didn't break the existing club_admin functionality
|
||||
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'club-admin-1', email: 'club@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('club-admin-1', 'club@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
@@ -286,13 +297,16 @@ describe('Tournament Permissions', () => {
|
||||
test('players should still be denied from managing tournaments', async () => {
|
||||
// This ensures we didn't accidentally grant players access
|
||||
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'player-1', email: 'player@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
eventFindUniqueMock.mockImplementation(async () =>
|
||||
createMockTournament(1, 'other-user-1')
|
||||
);
|
||||
|
||||
const result = await canManageTournament(1);
|
||||
expect(result.allowed).toBe(false);
|
||||
|
||||
@@ -3,23 +3,33 @@
|
||||
* Tests the allowTies field is properly saved when updating tournaments
|
||||
*/
|
||||
|
||||
import { describe, it, expect, mock, beforeEach } from 'bun:test';
|
||||
import { describe, it, expect, mock, beforeEach,} from 'bun:test';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
|
||||
// Create mock functions at module level
|
||||
const eventFindUniqueMock = mock(() => {});
|
||||
const eventUpdateMock = mock(() => {});
|
||||
const canManageTournamentMock = mock(() => {});
|
||||
const canDeleteTournamentMock = mock(() => {});
|
||||
|
||||
// Store default implementations
|
||||
const defaultCanManageTournament = canManageTournamentMock.mockResolvedValue({ allowed: true });
|
||||
const defaultCanDeleteTournament = canDeleteTournamentMock.mockResolvedValue({ allowed: true });
|
||||
|
||||
// Mock the prisma client
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
event: {
|
||||
findUnique: mock(() => {}),
|
||||
update: mock(() => {}),
|
||||
findUnique: eventFindUniqueMock,
|
||||
update: eventUpdateMock,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock the permissions module
|
||||
mock.module('@/lib/permissions', () => ({
|
||||
canManageTournament: mock(() => {}).mockResolvedValue({ allowed: true }),
|
||||
canDeleteTournament: mock(() => {}).mockResolvedValue({ allowed: true }),
|
||||
canManageTournament: defaultCanManageTournament,
|
||||
canDeleteTournament: defaultCanDeleteTournament,
|
||||
}));
|
||||
|
||||
// Import the route handler after mocking
|
||||
@@ -27,12 +37,16 @@ import { PUT } from '@/app/api/tournaments/[id]/route';
|
||||
|
||||
describe('Tournament Update API', () => {
|
||||
beforeEach(() => {
|
||||
mock.clearAllMocks();
|
||||
// Clear all mock history before each test
|
||||
eventFindUniqueMock.mockClear();
|
||||
eventUpdateMock.mockClear();
|
||||
canManageTournamentMock.mockClear();
|
||||
canDeleteTournamentMock.mockClear();
|
||||
});
|
||||
|
||||
it('should update allowTies field when provided', async () => {
|
||||
// Mock existing tournament
|
||||
prisma.event.findUnique.mockImplementation(async () => ({
|
||||
eventFindUniqueMock.mockImplementation(async () => ({
|
||||
id: 1,
|
||||
name: 'Test Tournament',
|
||||
allowTies: false,
|
||||
@@ -49,7 +63,7 @@ describe('Tournament Update API', () => {
|
||||
} as any));
|
||||
|
||||
// Mock successful update
|
||||
prisma.event.update.mockImplementation(async () => ({
|
||||
eventUpdateMock.mockImplementation(async () => ({
|
||||
id: 1,
|
||||
name: 'Test Tournament',
|
||||
allowTies: true,
|
||||
@@ -89,7 +103,7 @@ describe('Tournament Update API', () => {
|
||||
|
||||
it('should default allowTies to false when not provided', async () => {
|
||||
// Mock existing tournament
|
||||
prisma.event.findUnique.mockImplementation(async () => ({
|
||||
eventFindUniqueMock.mockImplementation(async () => ({
|
||||
id: 1,
|
||||
name: 'Test Tournament',
|
||||
allowTies: true,
|
||||
@@ -106,7 +120,7 @@ describe('Tournament Update API', () => {
|
||||
} as any));
|
||||
|
||||
// Mock successful update
|
||||
prisma.event.update.mockImplementation(async () => ({
|
||||
eventUpdateMock.mockImplementation(async () => ({
|
||||
id: 1,
|
||||
name: 'Test Tournament',
|
||||
allowTies: false,
|
||||
@@ -146,7 +160,7 @@ describe('Tournament Update API', () => {
|
||||
|
||||
it('should preserve allowTies value when updating other fields', async () => {
|
||||
// Mock existing tournament with allowTies = true
|
||||
prisma.event.findUnique.mockImplementation(async () => ({
|
||||
eventFindUniqueMock.mockImplementation(async () => ({
|
||||
id: 1,
|
||||
name: 'Test Tournament',
|
||||
allowTies: true,
|
||||
@@ -163,7 +177,7 @@ describe('Tournament Update API', () => {
|
||||
} as any));
|
||||
|
||||
// Mock successful update
|
||||
prisma.event.update.mockImplementation(async () => ({
|
||||
eventUpdateMock.mockImplementation(async () => ({
|
||||
id: 1,
|
||||
name: 'Updated Tournament Name',
|
||||
allowTies: true,
|
||||
@@ -192,7 +206,7 @@ describe('Tournament Update API', () => {
|
||||
const response = await PUT(request, { params });
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
const updateCall = prisma.event.update.mock.calls[0][0];
|
||||
const updateCall = eventUpdateMock.mock.calls[0][0];
|
||||
expect(updateCall.data.allowTies).toBe(true);
|
||||
expect(updateCall.data.name).toBe('Updated Tournament Name');
|
||||
expect(updateCall.data.targetScore).toBe(10);
|
||||
|
||||
@@ -4,25 +4,31 @@
|
||||
* Tests for user name editing and profile management
|
||||
*/
|
||||
|
||||
import { describe, test, expect, mock, beforeEach } from 'bun:test';
|
||||
import { describe, test, expect, mock, beforeEach,} from 'bun:test';
|
||||
import { hasRole } from '@/lib/permissions';
|
||||
import { getSession } from '@/lib/auth-simple';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import type { User, Player } from '@prisma/client';
|
||||
|
||||
// Create mock functions at module level
|
||||
const getSessionMock = mock(() => {});
|
||||
const userFindUniqueMock = mock(() => {});
|
||||
const userUpdateMock = mock(() => {});
|
||||
const playerFindUniqueMock = mock(() => {});
|
||||
|
||||
// Mock the getSession and prisma functions
|
||||
mock.module('@/lib/auth-simple', () => ({
|
||||
getSession: mock(() => {}),
|
||||
getSession: getSessionMock,
|
||||
}));
|
||||
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
prisma: {
|
||||
user: {
|
||||
findUnique: mock(() => {}),
|
||||
update: mock(() => {}),
|
||||
findUnique: userFindUniqueMock,
|
||||
update: userUpdateMock,
|
||||
},
|
||||
player: {
|
||||
findUnique: mock(() => {}),
|
||||
findUnique: playerFindUniqueMock,
|
||||
},
|
||||
},
|
||||
}));
|
||||
@@ -56,16 +62,20 @@ const createMockPlayer = (id: number, name: string): Player => ({
|
||||
|
||||
describe('User Management', () => {
|
||||
beforeEach(() => {
|
||||
mock.clearAllMocks();
|
||||
// Reset mock implementations to default (no-op) before each test
|
||||
getSessionMock.mockImplementation(() => undefined);
|
||||
userFindUniqueMock.mockImplementation(() => undefined);
|
||||
userUpdateMock.mockImplementation(() => undefined);
|
||||
playerFindUniqueMock.mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
describe('User Name Editing', () => {
|
||||
test('club_admin should be able to edit any user name', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||
);
|
||||
|
||||
@@ -74,11 +84,11 @@ describe('User Management', () => {
|
||||
});
|
||||
|
||||
test('tournament_admin should NOT be able to edit user names', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||
);
|
||||
|
||||
@@ -87,11 +97,11 @@ describe('User Management', () => {
|
||||
});
|
||||
|
||||
test('player should NOT be able to edit user names', async () => {
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () =>
|
||||
userFindUniqueMock.mockImplementation(async () =>
|
||||
createMockUser('player-1', 'player@example.com', 'player')
|
||||
);
|
||||
|
||||
@@ -100,7 +110,7 @@ describe('User Management', () => {
|
||||
});
|
||||
|
||||
test('unauthenticated user should NOT be able to edit user names', async () => {
|
||||
getSession.mockImplementation(async () => null);
|
||||
getSessionMock.mockImplementation(async () => null);
|
||||
|
||||
const result = await hasRole('club_admin');
|
||||
expect(result.allowed).toBe(false);
|
||||
@@ -111,11 +121,11 @@ describe('User Management', () => {
|
||||
test('user should be able to view their own profile', async () => {
|
||||
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
||||
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () => mockUser);
|
||||
userFindUniqueMock.mockImplementation(async () => mockUser);
|
||||
|
||||
// In the actual implementation, this would check if session.user.id === params.id
|
||||
const canViewOwnProfile = true; // This logic is in the API route
|
||||
@@ -126,11 +136,11 @@ describe('User Management', () => {
|
||||
const mockAdmin = createMockUser('admin-1', 'admin@example.com', 'club_admin');
|
||||
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
||||
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
(prisma.user.findUnique)
|
||||
(userFindUniqueMock)
|
||||
.mockResolvedValueOnce(mockAdmin) // For the requesting user
|
||||
.mockResolvedValueOnce(mockUser); // For the target user
|
||||
|
||||
@@ -142,11 +152,11 @@ describe('User Management', () => {
|
||||
test('non-admin should NOT be able to view other user profiles', async () => {
|
||||
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
||||
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () => mockUser);
|
||||
userFindUniqueMock.mockImplementation(async () => mockUser);
|
||||
|
||||
// In the actual implementation, this would check if session.user.id === params.id
|
||||
const canViewOtherProfile = false; // This logic is in the API route
|
||||
@@ -159,11 +169,11 @@ describe('User Management', () => {
|
||||
const mockUser = createMockUser('user-1', 'user@example.com', 'club_admin');
|
||||
const mockPlayer = createMockPlayer(1, 'Old Name');
|
||||
|
||||
getSession.mockImplementation(async () => ({
|
||||
getSessionMock.mockImplementation(async () => ({
|
||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||
session: { token: 'test', expiresAt: new Date() }
|
||||
}));
|
||||
prisma.user.findUnique.mockImplementation(async () => mockUser);
|
||||
userFindUniqueMock.mockImplementation(async () => mockUser);
|
||||
|
||||
const updatedUser = {
|
||||
...mockUser,
|
||||
@@ -171,7 +181,7 @@ describe('User Management', () => {
|
||||
player: { ...mockPlayer, name: 'New Name', normalizedName: 'new name' }
|
||||
};
|
||||
|
||||
prisma.user.update.mockImplementation(async () => updatedUser);
|
||||
userUpdateMock.mockImplementation(async () => updatedUser);
|
||||
|
||||
// The API route should update both user.name and player.name
|
||||
expect(updatedUser.name).toBe('New Name');
|
||||
|
||||
Reference in New Issue
Block a user