Files
euchre_camp/src/__tests__/unit/permissions.test.ts
T
david 861e14503b
Release / release (push) Failing after 9s
Build CI Images / build-ci-base (push) Failing after 20s
feat: SDLC database separation for CI/testing (#35)
## Summary
- Fix `isProductionDatabase()` to allow CI database (`euchre_camp_ci`)
- Add database schema reset before CI test runs
- Create `global.teardown.ts` for cleanup (CI: full reset, dev/prod: selective cleanup)
- Add `acceptance-tests` job to PR workflow with CI database
- Create `sync-prod-to-dev.js` script for one-way prod→dev sync
- Add `just` recipes: `sync-dev`, `test-prod`, `reset-ci-db`
- Store credentials in `.credentials` (gitignored) with unique CI user

## Testing
Verified against CI database:
- Schema reset works
- Migrations apply correctly
- Test users created successfully
- 219 tests pass (slow but working)

## Next Steps
- Set `CI_DATABASE_URL` as Gitea repository variable

Reviewed-on: #35
Co-authored-by: David Gwilliam <dhgwilliam@gmail.com>
Co-committed-by: David Gwilliam <dhgwilliam@gmail.com>
2026-05-11 06:40:05 +00:00

160 lines
5.2 KiB
TypeScript

/**
* Unit Tests: Permissions
*
* Tests the permission system for tournament management
*/
import { describe, test, expect, mock, beforeEach } from 'bun:test';
import { hasRole, canManageTournament, canCreateTournaments } from '@/lib/permissions';
import { getSession } from '@/lib/auth-simple';
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,
email,
role,
emailVerified: false,
name: null,
image: null,
playerId: null,
createdAt: new Date(),
updatedAt: new Date(),
});
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 () => {
getSessionMock.mockImplementation(async () => ({
user: { id: '1', email: 'test@example.com' },
session: { token: 'test', expiresAt: new Date() }
}));
userFindUniqueMock.mockImplementation(async () =>
createMockUser('1', 'test@example.com', 'club_admin')
);
const result = await hasRole('tournament_admin');
expect(result.allowed).toBe(true);
});
test('should deny player from accessing tournament_admin resources', async () => {
getSessionMock.mockImplementation(async () => ({
user: { id: '1', email: 'test@example.com' },
session: { token: 'test', expiresAt: new Date() }
}));
userFindUniqueMock.mockImplementation(async () =>
createMockUser('1', 'test@example.com', 'player')
);
const result = await hasRole('tournament_admin');
expect(result.allowed).toBe(false);
});
test('should deny unauthenticated user', async () => {
getSessionMock.mockImplementation(async () => null);
const result = await hasRole('club_admin');
expect(result.allowed).toBe(false);
expect(result.reason).toContain('Not authenticated');
});
});
describe('canManageTournament', () => {
test('should allow club_admin to manage any tournament', async () => {
getSessionMock.mockImplementation(async () => ({
user: { id: 'admin-1', email: 'admin@example.com' },
session: { token: 'test', expiresAt: new Date() }
}));
userFindUniqueMock.mockImplementation(async () =>
createMockUser('admin-1', 'admin@example.com', 'club_admin')
);
const result = await canManageTournament(999);
expect(result.allowed).toBe(true);
});
test('should deny player from managing tournaments', async () => {
getSessionMock.mockImplementation(async () => ({
user: { id: 'player-1', email: 'player@example.com' },
session: { token: 'test', expiresAt: new Date() }
}));
userFindUniqueMock.mockImplementation(async () =>
createMockUser('player-1', 'player@example.com', 'player')
);
const result = await canManageTournament(999);
expect(result.allowed).toBe(false);
expect(result.reason).toContain('Insufficient permissions');
});
});
describe('canCreateTournaments', () => {
test('should allow tournament_admin to create tournaments', async () => {
getSessionMock.mockImplementation(async () => ({
user: { id: 'admin-1', email: 'admin@example.com' },
session: { token: 'test', expiresAt: new Date() }
}));
userFindUniqueMock.mockImplementation(async () =>
createMockUser('admin-1', 'admin@example.com', 'tournament_admin')
);
const result = await canCreateTournaments();
expect(result.allowed).toBe(true);
});
test('should allow club_admin to create tournaments', async () => {
getSessionMock.mockImplementation(async () => ({
user: { id: 'admin-1', email: 'admin@example.com' },
session: { token: 'test', expiresAt: new Date() }
}));
userFindUniqueMock.mockImplementation(async () =>
createMockUser('admin-1', 'admin@example.com', 'club_admin')
);
const result = await canCreateTournaments();
expect(result.allowed).toBe(true);
});
test('should deny player from creating tournaments', async () => {
getSessionMock.mockImplementation(async () => ({
user: { id: 'player-1', email: 'player@example.com' },
session: { token: 'test', expiresAt: new Date() }
}));
userFindUniqueMock.mockImplementation(async () =>
createMockUser('player-1', 'player@example.com', 'player')
);
const result = await canCreateTournaments();
expect(result.allowed).toBe(false);
});
});
});