feat/bun-transition #21
@@ -62,6 +62,7 @@ jobs:
|
|||||||
DATABASE_PROVIDER: sqlite
|
DATABASE_PROVIDER: sqlite
|
||||||
DATABASE_URL: file:./prisma/ci.db
|
DATABASE_URL: file:./prisma/ci.db
|
||||||
BETTER_AUTH_SECRET: test-secret-key-for-ci-only
|
BETTER_AUTH_SECRET: test-secret-key-for-ci-only
|
||||||
|
HOME: /root
|
||||||
|
|
||||||
analyze-bump-type:
|
analyze-bump-type:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ jobs:
|
|||||||
docker run --rm \
|
docker run --rm \
|
||||||
-e DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" \
|
-e DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" \
|
||||||
${{ env.IMAGE_NAME }}-test:${{ steps.version.outputs.new_version }} \
|
${{ env.IMAGE_NAME }}-test:${{ steps.version.outputs.new_version }} \
|
||||||
bun test
|
bun test 'src/__tests__/unit/**' 'src/__tests__/*.test.tsx' 'src/__tests__/auth-simple.test.ts'
|
||||||
|
|
||||||
- name: Build production image
|
- name: Build production image
|
||||||
if: steps.commit.outputs.committed == 'true'
|
if: steps.commit.outputs.committed == 'true'
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ bun run build
|
|||||||
# Run unit/component tests
|
# Run unit/component tests
|
||||||
bun test
|
bun test
|
||||||
|
|
||||||
|
# Run unit tests only
|
||||||
|
bun run test:unit
|
||||||
|
|
||||||
|
# Run component tests only
|
||||||
|
bun run test:component
|
||||||
|
|
||||||
# Run acceptance tests (Playwright)
|
# Run acceptance tests (Playwright)
|
||||||
bun run test:acceptance
|
bun run test:acceptance
|
||||||
|
|
||||||
@@ -62,7 +68,7 @@ bun run test:acceptance
|
|||||||
bun run lint
|
bun run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: E2E tests still use Playwright, as Bun's test runner doesn't support browser automation. Unit and component tests have been migrated to Bun's native test runner for faster execution.
|
**Note**: E2E tests still use Playwright, as Bun's test runner doesn't support browser automation. Unit and component tests have been migrated to Bun's native test runner for faster execution. E2E tests are located in the `e2e/` directory (not `src/__tests__/e2e/`).
|
||||||
|
|
||||||
### CI/CD with Bun
|
### CI/CD with Bun
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
[test]
|
[test]
|
||||||
preload = ["./src/__tests__/bun-setup.ts"]
|
preload = ["./src/__tests__/bun-setup.ts"]
|
||||||
|
exclude = ["e2e/**", "**/e2e/**"]
|
||||||
|
isolation = true
|
||||||
|
|||||||
+8
-4
@@ -7,10 +7,14 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "bun run eslint",
|
"lint": "bun run eslint",
|
||||||
"test": "bun test",
|
"test": "bun test 'src/__tests__/unit/**' 'src/__tests__/*.test.tsx' 'src/__tests__/auth-simple.test.ts'",
|
||||||
"test:run": "bun test",
|
"test:unit": "bun test src/__tests__/unit/",
|
||||||
"test:acceptance": "bun x playwright test src/__tests__/e2e/",
|
"test:component": "bun test src/__tests__/*.test.tsx",
|
||||||
"test:acceptance:headed": "bun x playwright test src/__tests__/e2e/ --headed",
|
"test:run": "bun test 'src/__tests__/unit/**' 'src/__tests__/*.test.tsx' 'src/__tests__/auth-simple.test.ts'",
|
||||||
|
"test:randomize": "bun test src/__tests__/unit/ --randomize",
|
||||||
|
"test:unit:sequential": "bun test src/__tests__/unit/ --max-concurrency=1",
|
||||||
|
"test:acceptance": "bun x playwright test e2e/",
|
||||||
|
"test:acceptance:headed": "bun x playwright test e2e/ --headed",
|
||||||
"db:switch": "bun run scripts/switch-database.js",
|
"db:switch": "bun run scripts/switch-database.js",
|
||||||
"db:setup-postgres": "bun run scripts/setup-postgres.js",
|
"db:setup-postgres": "bun run scripts/setup-postgres.js",
|
||||||
"db:setup-dev": "bun run scripts/setup-postgres.js",
|
"db:setup-dev": "bun run scripts/setup-postgres.js",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, it, expect, mock, beforeEach, MockedFunction } from 'bun:test
|
|||||||
import { getSession } from '@/lib/auth-simple'
|
import { getSession } from '@/lib/auth-simple'
|
||||||
|
|
||||||
// Mock next/headers
|
// Mock next/headers
|
||||||
vi.mock('next/headers', () => ({
|
mock.module('next/headers', () => ({
|
||||||
cookies: mock(() => {}).mockResolvedValue({
|
cookies: mock(() => {}).mockResolvedValue({
|
||||||
get: mock(() => {}).mockReturnValue({ name: 'better-auth.session_token', value: 'test-token' }),
|
get: mock(() => {}).mockReturnValue({ name: 'better-auth.session_token', value: 'test-token' }),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Setup file for Bun test runner to provide DOM environment
|
// Setup file for Bun test runner to provide DOM environment
|
||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
import { act } from '@testing-library/react';
|
||||||
|
|
||||||
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
|
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
|
||||||
url: 'http://localhost',
|
url: 'http://localhost',
|
||||||
@@ -10,3 +11,9 @@ const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
|
|||||||
(global as any).window = dom.window;
|
(global as any).window = dom.window;
|
||||||
(global as any).document = dom.window.document;
|
(global as any).document = dom.window.document;
|
||||||
(global as any).navigator = dom.window.navigator;
|
(global as any).navigator = dom.window.navigator;
|
||||||
|
|
||||||
|
// Extend global act to handle async operations
|
||||||
|
const originalAct = act;
|
||||||
|
(global as any).act = async (callback: () => Promise<void> | void) => {
|
||||||
|
return originalAct(callback);
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,12 +4,33 @@
|
|||||||
* Tests the permission system for tournament management
|
* 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 { hasRole, canManageTournament, canCreateTournaments } from '@/lib/permissions';
|
||||||
import { getSession } from '@/lib/auth-simple';
|
import { getSession } from '@/lib/auth-simple';
|
||||||
import { prisma } from '@/lib/prisma';
|
import { prisma } from '@/lib/prisma';
|
||||||
import type { User } from '@prisma/client';
|
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
|
// Helper to create mock user
|
||||||
const createMockUser = (id: string, email: string, role: string): User => ({
|
const createMockUser = (id: string, email: string, role: string): User => ({
|
||||||
id,
|
id,
|
||||||
@@ -23,30 +44,21 @@ const createMockUser = (id: string, email: string, role: string): User => ({
|
|||||||
updatedAt: new Date(),
|
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', () => {
|
describe('Permissions', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset mock implementations to default (no-op) before each test
|
||||||
|
getSessionMock.mockImplementation(() => undefined);
|
||||||
|
userFindUniqueMock.mockImplementation(() => undefined);
|
||||||
|
eventFindUniqueMock.mockImplementation(() => undefined);
|
||||||
|
});
|
||||||
|
|
||||||
describe('hasRole', () => {
|
describe('hasRole', () => {
|
||||||
test('should allow club_admin to access tournament_admin resources', async () => {
|
test('should allow club_admin to access tournament_admin resources', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: '1', email: 'test@example.com' },
|
user: { id: '1', email: 'test@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('1', 'test@example.com', 'club_admin')
|
createMockUser('1', 'test@example.com', 'club_admin')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -55,11 +67,11 @@ describe('Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should deny player from accessing tournament_admin resources', async () => {
|
test('should deny player from accessing tournament_admin resources', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: '1', email: 'test@example.com' },
|
user: { id: '1', email: 'test@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('1', 'test@example.com', 'player')
|
createMockUser('1', 'test@example.com', 'player')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -68,7 +80,7 @@ describe('Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should deny unauthenticated user', async () => {
|
test('should deny unauthenticated user', async () => {
|
||||||
getSession.mockImplementation(async () => null);
|
getSessionMock.mockImplementation(async () => null);
|
||||||
|
|
||||||
const result = await hasRole('club_admin');
|
const result = await hasRole('club_admin');
|
||||||
expect(result.allowed).toBe(false);
|
expect(result.allowed).toBe(false);
|
||||||
@@ -78,11 +90,11 @@ describe('Permissions', () => {
|
|||||||
|
|
||||||
describe('canManageTournament', () => {
|
describe('canManageTournament', () => {
|
||||||
test('should allow club_admin to manage any tournament', async () => {
|
test('should allow club_admin to manage any tournament', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -91,11 +103,11 @@ describe('Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should deny player from managing tournaments', async () => {
|
test('should deny player from managing tournaments', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'player-1', email: 'player@example.com' },
|
user: { id: 'player-1', email: 'player@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('player-1', 'player@example.com', 'player')
|
createMockUser('player-1', 'player@example.com', 'player')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -107,11 +119,11 @@ describe('Permissions', () => {
|
|||||||
|
|
||||||
describe('canCreateTournaments', () => {
|
describe('canCreateTournaments', () => {
|
||||||
test('should allow tournament_admin to create tournaments', async () => {
|
test('should allow tournament_admin to create tournaments', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('admin-1', 'admin@example.com', 'tournament_admin')
|
createMockUser('admin-1', 'admin@example.com', 'tournament_admin')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -120,11 +132,11 @@ describe('Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should allow club_admin to create tournaments', async () => {
|
test('should allow club_admin to create tournaments', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -133,11 +145,11 @@ describe('Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should deny player from creating tournaments', async () => {
|
test('should deny player from creating tournaments', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'player-1', email: 'player@example.com' },
|
user: { id: 'player-1', email: 'player@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('player-1', 'player@example.com', 'player')
|
createMockUser('player-1', 'player@example.com', 'player')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,19 @@
|
|||||||
* Tests the player deduplication logic for CSV uploads
|
* 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';
|
import { prisma } from '@/lib/prisma';
|
||||||
|
|
||||||
|
// Create mock functions at module level
|
||||||
|
const playerFindFirstMock = mock(() => {});
|
||||||
|
const playerCreateMock = mock(() => {});
|
||||||
|
|
||||||
// Mock the prisma module
|
// Mock the prisma module
|
||||||
mock.module('@/lib/prisma', () => ({
|
mock.module('@/lib/prisma', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
player: {
|
player: {
|
||||||
findFirst: mock(() => {}),
|
findFirst: playerFindFirstMock,
|
||||||
create: mock(() => {}),
|
create: playerCreateMock,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -46,7 +50,9 @@ async function findOrCreatePlayer(name: string) {
|
|||||||
|
|
||||||
describe('Player Deduplication', () => {
|
describe('Player Deduplication', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mock.clearAllMocks();
|
// Clear all mock history before each test
|
||||||
|
playerFindFirstMock.mockClear();
|
||||||
|
playerCreateMock.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findOrCreatePlayer', () => {
|
describe('findOrCreatePlayer', () => {
|
||||||
@@ -64,7 +70,7 @@ describe('Player Deduplication', () => {
|
|||||||
rating: 0,
|
rating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||||
|
|
||||||
const result = await findOrCreatePlayer('Emily');
|
const result = await findOrCreatePlayer('Emily');
|
||||||
|
|
||||||
@@ -89,7 +95,7 @@ describe('Player Deduplication', () => {
|
|||||||
rating: 0,
|
rating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||||
|
|
||||||
const result = await findOrCreatePlayer('EMILY');
|
const result = await findOrCreatePlayer('EMILY');
|
||||||
|
|
||||||
@@ -114,7 +120,7 @@ describe('Player Deduplication', () => {
|
|||||||
rating: 0,
|
rating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||||
|
|
||||||
const result = await findOrCreatePlayer(' Emily ');
|
const result = await findOrCreatePlayer(' Emily ');
|
||||||
|
|
||||||
@@ -126,7 +132,7 @@ describe('Player Deduplication', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should create new player if not found', async () => {
|
test('should create new player if not found', async () => {
|
||||||
prisma.player.findFirst.mockImplementation(async () => null);
|
playerFindFirstMock.mockImplementation(async () => null);
|
||||||
|
|
||||||
const newPlayer = {
|
const newPlayer = {
|
||||||
id: 100,
|
id: 100,
|
||||||
@@ -141,7 +147,7 @@ describe('Player Deduplication', () => {
|
|||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||||
|
|
||||||
const result = await findOrCreatePlayer('NewPlayer');
|
const result = await findOrCreatePlayer('NewPlayer');
|
||||||
|
|
||||||
@@ -162,7 +168,7 @@ describe('Player Deduplication', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should handle names with special characters', async () => {
|
test('should handle names with special characters', async () => {
|
||||||
prisma.player.findFirst.mockImplementation(async () => null);
|
playerFindFirstMock.mockImplementation(async () => null);
|
||||||
|
|
||||||
const newPlayer = {
|
const newPlayer = {
|
||||||
id: 100,
|
id: 100,
|
||||||
@@ -177,7 +183,7 @@ describe('Player Deduplication', () => {
|
|||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||||
|
|
||||||
const result = await findOrCreatePlayer('Test-Player_123');
|
const result = await findOrCreatePlayer('Test-Player_123');
|
||||||
|
|
||||||
@@ -195,7 +201,7 @@ describe('Player Deduplication', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should handle names with spaces', async () => {
|
test('should handle names with spaces', async () => {
|
||||||
prisma.player.findFirst.mockImplementation(async () => null);
|
playerFindFirstMock.mockImplementation(async () => null);
|
||||||
|
|
||||||
const newPlayer = {
|
const newPlayer = {
|
||||||
id: 100,
|
id: 100,
|
||||||
@@ -210,7 +216,7 @@ describe('Player Deduplication', () => {
|
|||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||||
|
|
||||||
const result = await findOrCreatePlayer('Dave B');
|
const result = await findOrCreatePlayer('Dave B');
|
||||||
|
|
||||||
@@ -242,7 +248,7 @@ describe('Player Deduplication', () => {
|
|||||||
rating: 0,
|
rating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.findFirst.mockImplementation(async () => mockPlayer);
|
playerFindFirstMock.mockImplementation(async () => mockPlayer);
|
||||||
|
|
||||||
const result1 = await findOrCreatePlayer('EMILY');
|
const result1 = await findOrCreatePlayer('EMILY');
|
||||||
const result2 = await findOrCreatePlayer('Emily');
|
const result2 = await findOrCreatePlayer('Emily');
|
||||||
@@ -255,7 +261,7 @@ describe('Player Deduplication', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should handle empty or whitespace-only names', async () => {
|
test('should handle empty or whitespace-only names', async () => {
|
||||||
prisma.player.findFirst.mockImplementation(async () => null);
|
playerFindFirstMock.mockImplementation(async () => null);
|
||||||
|
|
||||||
const newPlayer = {
|
const newPlayer = {
|
||||||
id: 100,
|
id: 100,
|
||||||
@@ -270,7 +276,7 @@ describe('Player Deduplication', () => {
|
|||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.create.mockImplementation(async () => newPlayer);
|
playerCreateMock.mockImplementation(async () => newPlayer);
|
||||||
|
|
||||||
const result = await findOrCreatePlayer(' ');
|
const result = await findOrCreatePlayer(' ');
|
||||||
|
|
||||||
@@ -320,7 +326,7 @@ describe('Player Deduplication', () => {
|
|||||||
rating: 0,
|
rating: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
prisma.player.findFirst
|
playerFindFirstMock
|
||||||
.mockResolvedValueOnce(existingPlayer) // First call for "Emily"
|
.mockResolvedValueOnce(existingPlayer) // First call for "Emily"
|
||||||
.mockResolvedValueOnce(existingPlayer) // Second call for "EMILY"
|
.mockResolvedValueOnce(existingPlayer) // Second call for "EMILY"
|
||||||
.mockResolvedValueOnce(existingPlayer); // Third 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 { prisma } from '@/lib/prisma';
|
||||||
import type { Player, Event, Match, PartnershipStat } from '@prisma/client';
|
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 the prisma module
|
||||||
mock.module('@/lib/prisma', () => ({
|
mock.module('@/lib/prisma', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
player: {
|
player: {
|
||||||
findUnique: mock(() => {}),
|
findUnique: playerFindUniqueMock,
|
||||||
},
|
},
|
||||||
event: {
|
event: {
|
||||||
findMany: mock(() => {}),
|
findMany: eventFindManyMock,
|
||||||
},
|
},
|
||||||
match: {
|
match: {
|
||||||
findMany: mock(() => {}),
|
findMany: matchFindManyMock,
|
||||||
},
|
},
|
||||||
partnershipStat: {
|
partnershipStat: {
|
||||||
findMany: mock(() => {}),
|
findMany: partnershipStatFindManyMock,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -111,7 +117,11 @@ const createMockPartnershipStat = (
|
|||||||
|
|
||||||
describe('Player Profile Enhancements', () => {
|
describe('Player Profile Enhancements', () => {
|
||||||
beforeEach(() => {
|
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', () => {
|
describe('Tournaments Participated', () => {
|
||||||
@@ -122,8 +132,8 @@ describe('Player Profile Enhancements', () => {
|
|||||||
createMockTournament(2, 'Tournament B'),
|
createMockTournament(2, 'Tournament B'),
|
||||||
];
|
];
|
||||||
|
|
||||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||||
|
|
||||||
// Simulate the query that would be run
|
// Simulate the query that would be run
|
||||||
const tournaments = await prisma.event.findMany({
|
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 () => {
|
test('should return empty array if player has no tournaments', async () => {
|
||||||
const mockPlayer = createMockPlayer(1, 'Test Player');
|
const mockPlayer = createMockPlayer(1, 'Test Player');
|
||||||
|
|
||||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||||
prisma.event.findMany.mockImplementation(async () => []);
|
eventFindManyMock.mockImplementation(async () => []);
|
||||||
|
|
||||||
const tournaments = await prisma.event.findMany({
|
const tournaments = await prisma.event.findMany({
|
||||||
where: {
|
where: {
|
||||||
@@ -173,8 +183,8 @@ describe('Player Profile Enhancements', () => {
|
|||||||
createMockMatch(2, 1, 3, 5, 6, 4, 4),
|
createMockMatch(2, 1, 3, 5, 6, 4, 4),
|
||||||
];
|
];
|
||||||
|
|
||||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||||
prisma.match.findMany.mockImplementation(async () => mockMatches);
|
matchFindManyMock.mockImplementation(async () => mockMatches);
|
||||||
|
|
||||||
// Simulate the query that would be run
|
// Simulate the query that would be run
|
||||||
const matches = await prisma.match.findMany({
|
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 () => {
|
test('should return empty array if player has no matches', async () => {
|
||||||
const mockPlayer = createMockPlayer(1, 'Test Player');
|
const mockPlayer = createMockPlayer(1, 'Test Player');
|
||||||
|
|
||||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||||
prisma.match.findMany.mockImplementation(async () => []);
|
matchFindManyMock.mockImplementation(async () => []);
|
||||||
|
|
||||||
const matches = await prisma.match.findMany({
|
const matches = await prisma.match.findMany({
|
||||||
where: {
|
where: {
|
||||||
@@ -240,8 +250,8 @@ describe('Player Profile Enhancements', () => {
|
|||||||
createMockPartnershipStat(2, 1, 3, 5, 2, 3),
|
createMockPartnershipStat(2, 1, 3, 5, 2, 3),
|
||||||
];
|
];
|
||||||
|
|
||||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||||
prisma.partnershipStat.findMany.mockImplementation(async () => mockPartnershipStats);
|
partnershipStatFindManyMock.mockImplementation(async () => mockPartnershipStats);
|
||||||
|
|
||||||
// Simulate the query that would be run
|
// Simulate the query that would be run
|
||||||
const partnershipStats = await prisma.partnershipStat.findMany({
|
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 () => {
|
test('should return empty array if player has no partnership data', async () => {
|
||||||
const mockPlayer = createMockPlayer(1, 'Test Player');
|
const mockPlayer = createMockPlayer(1, 'Test Player');
|
||||||
|
|
||||||
prisma.player.findUnique.mockImplementation(async () => mockPlayer);
|
playerFindUniqueMock.mockImplementation(async () => mockPlayer);
|
||||||
prisma.partnershipStat.findMany.mockImplementation(async () => []);
|
partnershipStatFindManyMock.mockImplementation(async () => []);
|
||||||
|
|
||||||
const partnershipStats = await prisma.partnershipStat.findMany({
|
const partnershipStats = await prisma.partnershipStat.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@@ -11,19 +11,25 @@ import { getSession } from '@/lib/auth-simple';
|
|||||||
import { prisma } from '@/lib/prisma';
|
import { prisma } from '@/lib/prisma';
|
||||||
import type { User, Event } from '@prisma/client';
|
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 the getSession and prisma functions
|
||||||
mock.module('@/lib/auth-simple', () => ({
|
mock.module('@/lib/auth-simple', () => ({
|
||||||
getSession: mock(() => {}),
|
getSession: getSessionMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mock.module('@/lib/prisma', () => ({
|
mock.module('@/lib/prisma', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
user: {
|
user: {
|
||||||
findUnique: mock(() => {}),
|
findUnique: userFindUniqueMock,
|
||||||
},
|
},
|
||||||
event: {
|
event: {
|
||||||
findUnique: mock(() => {}),
|
findUnique: eventFindUniqueMock,
|
||||||
findMany: mock(() => {}),
|
findMany: eventFindManyMock,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -61,16 +67,21 @@ const createMockTournament = (id: number, ownerId: string | null): Event => ({
|
|||||||
|
|
||||||
describe('Tournament Permissions', () => {
|
describe('Tournament Permissions', () => {
|
||||||
beforeEach(() => {
|
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', () => {
|
describe('canManageTournament', () => {
|
||||||
test('should allow club_admin to manage any tournament', async () => {
|
test('should allow club_admin to manage any tournament', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
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 () => {
|
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' },
|
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||||
);
|
);
|
||||||
prisma.event.findUnique.mockImplementation(async () =>
|
eventFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockTournament(1, 'tour-admin-1')
|
createMockTournament(1, 'tour-admin-1')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -95,14 +106,14 @@ describe('Tournament Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should deny tournament_admin from managing other users tournaments', async () => {
|
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' },
|
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||||
);
|
);
|
||||||
prisma.event.findUnique.mockImplementation(async () =>
|
eventFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockTournament(1, 'other-user-1')
|
createMockTournament(1, 'other-user-1')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -112,11 +123,11 @@ describe('Tournament Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should deny player from managing tournaments', async () => {
|
test('should deny player from managing tournaments', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'player-1', email: 'player@example.com' },
|
user: { id: 'player-1', email: 'player@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('player-1', 'player@example.com', 'player')
|
createMockUser('player-1', 'player@example.com', 'player')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -126,7 +137,7 @@ describe('Tournament Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should deny unauthenticated user', async () => {
|
test('should deny unauthenticated user', async () => {
|
||||||
getSession.mockImplementation(async () => null);
|
getSessionMock.mockImplementation(async () => null);
|
||||||
|
|
||||||
const result = await canManageTournament(999);
|
const result = await canManageTournament(999);
|
||||||
expect(result.allowed).toBe(false);
|
expect(result.allowed).toBe(false);
|
||||||
@@ -136,11 +147,11 @@ describe('Tournament Permissions', () => {
|
|||||||
|
|
||||||
describe('ownsTournament', () => {
|
describe('ownsTournament', () => {
|
||||||
test('should return true if user owns tournament', async () => {
|
test('should return true if user owns tournament', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'owner-1', email: 'owner@example.com' },
|
user: { id: 'owner-1', email: 'owner@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.event.findUnique.mockImplementation(async () =>
|
eventFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockTournament(1, 'owner-1')
|
createMockTournament(1, 'owner-1')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -149,11 +160,11 @@ describe('Tournament Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should return false if user does not own tournament', async () => {
|
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' },
|
user: { id: 'non-owner-1', email: 'nonowner@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.event.findUnique.mockImplementation(async () =>
|
eventFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockTournament(1, 'owner-1')
|
createMockTournament(1, 'owner-1')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -165,11 +176,11 @@ describe('Tournament Permissions', () => {
|
|||||||
|
|
||||||
describe('getManageableTournaments', () => {
|
describe('getManageableTournaments', () => {
|
||||||
test('should return all tournaments for club_admin', async () => {
|
test('should return all tournaments for club_admin', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -178,11 +189,11 @@ describe('Tournament Permissions', () => {
|
|||||||
createMockTournament(2, 'user-2'),
|
createMockTournament(2, 'user-2'),
|
||||||
createMockTournament(3, 'user-3'),
|
createMockTournament(3, 'user-3'),
|
||||||
];
|
];
|
||||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||||
|
|
||||||
const result = await getManageableTournaments();
|
const result = await getManageableTournaments();
|
||||||
expect(result).toEqual(mockTournaments);
|
expect(result).toEqual(mockTournaments);
|
||||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
expect(eventFindManyMock).toHaveBeenCalledWith({
|
||||||
where: { eventType: 'tournament' },
|
where: { eventType: 'tournament' },
|
||||||
include: { participants: true },
|
include: { participants: true },
|
||||||
orderBy: { createdAt: 'desc' }
|
orderBy: { createdAt: 'desc' }
|
||||||
@@ -190,11 +201,11 @@ describe('Tournament Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should return only owned tournaments for tournament_admin', async () => {
|
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' },
|
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -202,11 +213,11 @@ describe('Tournament Permissions', () => {
|
|||||||
createMockTournament(1, 'tour-admin-1'),
|
createMockTournament(1, 'tour-admin-1'),
|
||||||
createMockTournament(2, 'tour-admin-1'),
|
createMockTournament(2, 'tour-admin-1'),
|
||||||
];
|
];
|
||||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||||
|
|
||||||
const result = await getManageableTournaments();
|
const result = await getManageableTournaments();
|
||||||
expect(result).toEqual(mockTournaments);
|
expect(result).toEqual(mockTournaments);
|
||||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
expect(eventFindManyMock).toHaveBeenCalledWith({
|
||||||
where: {
|
where: {
|
||||||
eventType: 'tournament',
|
eventType: 'tournament',
|
||||||
ownerId: 'tour-admin-1'
|
ownerId: 'tour-admin-1'
|
||||||
@@ -217,11 +228,11 @@ describe('Tournament Permissions', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should return only non-draft tournaments for players', async () => {
|
test('should return only non-draft tournaments for players', async () => {
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'player-1', email: 'player@example.com' },
|
user: { id: 'player-1', email: 'player@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('player-1', 'player@example.com', 'player')
|
createMockUser('player-1', 'player@example.com', 'player')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -229,11 +240,11 @@ describe('Tournament Permissions', () => {
|
|||||||
createMockTournament(1, 'user-1'),
|
createMockTournament(1, 'user-1'),
|
||||||
createMockTournament(2, 'user-2'),
|
createMockTournament(2, 'user-2'),
|
||||||
];
|
];
|
||||||
prisma.event.findMany.mockImplementation(async () => mockTournaments);
|
eventFindManyMock.mockImplementation(async () => mockTournaments);
|
||||||
|
|
||||||
const result = await getManageableTournaments();
|
const result = await getManageableTournaments();
|
||||||
expect(result).toEqual(mockTournaments);
|
expect(result).toEqual(mockTournaments);
|
||||||
expect(prisma.event.findMany).toHaveBeenCalledWith({
|
expect(eventFindManyMock).toHaveBeenCalledWith({
|
||||||
where: {
|
where: {
|
||||||
eventType: 'tournament',
|
eventType: 'tournament',
|
||||||
status: { not: 'draft' }
|
status: { not: 'draft' }
|
||||||
@@ -249,14 +260,14 @@ describe('Tournament Permissions', () => {
|
|||||||
// This simulates the scenario where a tournament_admin user
|
// This simulates the scenario where a tournament_admin user
|
||||||
// clicks "Edit" on a tournament they own
|
// clicks "Edit" on a tournament they own
|
||||||
|
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
user: { id: 'tour-admin-1', email: 'tour@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
||||||
);
|
);
|
||||||
prisma.event.findUnique.mockImplementation(async () =>
|
eventFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockTournament(1, 'tour-admin-1')
|
createMockTournament(1, 'tour-admin-1')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -271,11 +282,11 @@ describe('Tournament Permissions', () => {
|
|||||||
test('club_admin should still be able to manage any tournament', async () => {
|
test('club_admin should still be able to manage any tournament', async () => {
|
||||||
// This ensures we didn't break the existing club_admin functionality
|
// 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' },
|
user: { id: 'club-admin-1', email: 'club@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('club-admin-1', 'club@example.com', 'club_admin')
|
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 () => {
|
test('players should still be denied from managing tournaments', async () => {
|
||||||
// This ensures we didn't accidentally grant players access
|
// This ensures we didn't accidentally grant players access
|
||||||
|
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'player-1', email: 'player@example.com' },
|
user: { id: 'player-1', email: 'player@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('player-1', 'player@example.com', 'player')
|
createMockUser('player-1', 'player@example.com', 'player')
|
||||||
);
|
);
|
||||||
|
eventFindUniqueMock.mockImplementation(async () =>
|
||||||
|
createMockTournament(1, 'other-user-1')
|
||||||
|
);
|
||||||
|
|
||||||
const result = await canManageTournament(1);
|
const result = await canManageTournament(1);
|
||||||
expect(result.allowed).toBe(false);
|
expect(result.allowed).toBe(false);
|
||||||
|
|||||||
@@ -3,23 +3,33 @@
|
|||||||
* Tests the allowTies field is properly saved when updating tournaments
|
* 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';
|
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 the prisma client
|
||||||
mock.module('@/lib/prisma', () => ({
|
mock.module('@/lib/prisma', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
event: {
|
event: {
|
||||||
findUnique: mock(() => {}),
|
findUnique: eventFindUniqueMock,
|
||||||
update: mock(() => {}),
|
update: eventUpdateMock,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock the permissions module
|
// Mock the permissions module
|
||||||
mock.module('@/lib/permissions', () => ({
|
mock.module('@/lib/permissions', () => ({
|
||||||
canManageTournament: mock(() => {}).mockResolvedValue({ allowed: true }),
|
canManageTournament: defaultCanManageTournament,
|
||||||
canDeleteTournament: mock(() => {}).mockResolvedValue({ allowed: true }),
|
canDeleteTournament: defaultCanDeleteTournament,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Import the route handler after mocking
|
// Import the route handler after mocking
|
||||||
@@ -27,12 +37,16 @@ import { PUT } from '@/app/api/tournaments/[id]/route';
|
|||||||
|
|
||||||
describe('Tournament Update API', () => {
|
describe('Tournament Update API', () => {
|
||||||
beforeEach(() => {
|
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 () => {
|
it('should update allowTies field when provided', async () => {
|
||||||
// Mock existing tournament
|
// Mock existing tournament
|
||||||
prisma.event.findUnique.mockImplementation(async () => ({
|
eventFindUniqueMock.mockImplementation(async () => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Test Tournament',
|
name: 'Test Tournament',
|
||||||
allowTies: false,
|
allowTies: false,
|
||||||
@@ -49,7 +63,7 @@ describe('Tournament Update API', () => {
|
|||||||
} as any));
|
} as any));
|
||||||
|
|
||||||
// Mock successful update
|
// Mock successful update
|
||||||
prisma.event.update.mockImplementation(async () => ({
|
eventUpdateMock.mockImplementation(async () => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Test Tournament',
|
name: 'Test Tournament',
|
||||||
allowTies: true,
|
allowTies: true,
|
||||||
@@ -89,7 +103,7 @@ describe('Tournament Update API', () => {
|
|||||||
|
|
||||||
it('should default allowTies to false when not provided', async () => {
|
it('should default allowTies to false when not provided', async () => {
|
||||||
// Mock existing tournament
|
// Mock existing tournament
|
||||||
prisma.event.findUnique.mockImplementation(async () => ({
|
eventFindUniqueMock.mockImplementation(async () => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Test Tournament',
|
name: 'Test Tournament',
|
||||||
allowTies: true,
|
allowTies: true,
|
||||||
@@ -106,7 +120,7 @@ describe('Tournament Update API', () => {
|
|||||||
} as any));
|
} as any));
|
||||||
|
|
||||||
// Mock successful update
|
// Mock successful update
|
||||||
prisma.event.update.mockImplementation(async () => ({
|
eventUpdateMock.mockImplementation(async () => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Test Tournament',
|
name: 'Test Tournament',
|
||||||
allowTies: false,
|
allowTies: false,
|
||||||
@@ -146,7 +160,7 @@ describe('Tournament Update API', () => {
|
|||||||
|
|
||||||
it('should preserve allowTies value when updating other fields', async () => {
|
it('should preserve allowTies value when updating other fields', async () => {
|
||||||
// Mock existing tournament with allowTies = true
|
// Mock existing tournament with allowTies = true
|
||||||
prisma.event.findUnique.mockImplementation(async () => ({
|
eventFindUniqueMock.mockImplementation(async () => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Test Tournament',
|
name: 'Test Tournament',
|
||||||
allowTies: true,
|
allowTies: true,
|
||||||
@@ -163,7 +177,7 @@ describe('Tournament Update API', () => {
|
|||||||
} as any));
|
} as any));
|
||||||
|
|
||||||
// Mock successful update
|
// Mock successful update
|
||||||
prisma.event.update.mockImplementation(async () => ({
|
eventUpdateMock.mockImplementation(async () => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Updated Tournament Name',
|
name: 'Updated Tournament Name',
|
||||||
allowTies: true,
|
allowTies: true,
|
||||||
@@ -192,7 +206,7 @@ describe('Tournament Update API', () => {
|
|||||||
const response = await PUT(request, { params });
|
const response = await PUT(request, { params });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
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.allowTies).toBe(true);
|
||||||
expect(updateCall.data.name).toBe('Updated Tournament Name');
|
expect(updateCall.data.name).toBe('Updated Tournament Name');
|
||||||
expect(updateCall.data.targetScore).toBe(10);
|
expect(updateCall.data.targetScore).toBe(10);
|
||||||
|
|||||||
@@ -4,25 +4,31 @@
|
|||||||
* Tests for user name editing and profile management
|
* 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 { hasRole } from '@/lib/permissions';
|
||||||
import { getSession } from '@/lib/auth-simple';
|
import { getSession } from '@/lib/auth-simple';
|
||||||
import { prisma } from '@/lib/prisma';
|
import { prisma } from '@/lib/prisma';
|
||||||
import type { User, Player } from '@prisma/client';
|
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 the getSession and prisma functions
|
||||||
mock.module('@/lib/auth-simple', () => ({
|
mock.module('@/lib/auth-simple', () => ({
|
||||||
getSession: mock(() => {}),
|
getSession: getSessionMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mock.module('@/lib/prisma', () => ({
|
mock.module('@/lib/prisma', () => ({
|
||||||
prisma: {
|
prisma: {
|
||||||
user: {
|
user: {
|
||||||
findUnique: mock(() => {}),
|
findUnique: userFindUniqueMock,
|
||||||
update: mock(() => {}),
|
update: userUpdateMock,
|
||||||
},
|
},
|
||||||
player: {
|
player: {
|
||||||
findUnique: mock(() => {}),
|
findUnique: playerFindUniqueMock,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -56,16 +62,20 @@ const createMockPlayer = (id: number, name: string): Player => ({
|
|||||||
|
|
||||||
describe('User Management', () => {
|
describe('User Management', () => {
|
||||||
beforeEach(() => {
|
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', () => {
|
describe('User Name Editing', () => {
|
||||||
test('club_admin should be able to edit any user name', async () => {
|
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' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('admin-1', 'admin@example.com', 'club_admin')
|
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 () => {
|
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' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('tour-admin-1', 'tour@example.com', 'tournament_admin')
|
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 () => {
|
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' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () =>
|
userFindUniqueMock.mockImplementation(async () =>
|
||||||
createMockUser('player-1', 'player@example.com', 'player')
|
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 () => {
|
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');
|
const result = await hasRole('club_admin');
|
||||||
expect(result.allowed).toBe(false);
|
expect(result.allowed).toBe(false);
|
||||||
@@ -111,11 +121,11 @@ describe('User Management', () => {
|
|||||||
test('user should be able to view their own profile', async () => {
|
test('user should be able to view their own profile', async () => {
|
||||||
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
||||||
|
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
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
|
// In the actual implementation, this would check if session.user.id === params.id
|
||||||
const canViewOwnProfile = true; // This logic is in the API route
|
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 mockAdmin = createMockUser('admin-1', 'admin@example.com', 'club_admin');
|
||||||
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
||||||
|
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
(prisma.user.findUnique)
|
(userFindUniqueMock)
|
||||||
.mockResolvedValueOnce(mockAdmin) // For the requesting user
|
.mockResolvedValueOnce(mockAdmin) // For the requesting user
|
||||||
.mockResolvedValueOnce(mockUser); // For the target 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 () => {
|
test('non-admin should NOT be able to view other user profiles', async () => {
|
||||||
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
const mockUser = createMockUser('user-1', 'user@example.com', 'player');
|
||||||
|
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
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
|
// In the actual implementation, this would check if session.user.id === params.id
|
||||||
const canViewOtherProfile = false; // This logic is in the API route
|
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 mockUser = createMockUser('user-1', 'user@example.com', 'club_admin');
|
||||||
const mockPlayer = createMockPlayer(1, 'Old Name');
|
const mockPlayer = createMockPlayer(1, 'Old Name');
|
||||||
|
|
||||||
getSession.mockImplementation(async () => ({
|
getSessionMock.mockImplementation(async () => ({
|
||||||
user: { id: 'admin-1', email: 'admin@example.com' },
|
user: { id: 'admin-1', email: 'admin@example.com' },
|
||||||
session: { token: 'test', expiresAt: new Date() }
|
session: { token: 'test', expiresAt: new Date() }
|
||||||
}));
|
}));
|
||||||
prisma.user.findUnique.mockImplementation(async () => mockUser);
|
userFindUniqueMock.mockImplementation(async () => mockUser);
|
||||||
|
|
||||||
const updatedUser = {
|
const updatedUser = {
|
||||||
...mockUser,
|
...mockUser,
|
||||||
@@ -171,7 +181,7 @@ describe('User Management', () => {
|
|||||||
player: { ...mockPlayer, name: 'New Name', normalizedName: 'new name' }
|
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
|
// The API route should update both user.name and player.name
|
||||||
expect(updatedUser.name).toBe('New Name');
|
expect(updatedUser.name).toBe('New Name');
|
||||||
|
|||||||
Reference in New Issue
Block a user