229 lines
7.8 KiB
TypeScript
229 lines
7.8 KiB
TypeScript
import 'reflect-metadata';
|
|
|
|
import { Reflector } from '@nestjs/core';
|
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
import request from 'supertest';
|
|
import { vi } from 'vitest';
|
|
import { TeamController } from './TeamController';
|
|
import { TeamService } from './TeamService';
|
|
import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO';
|
|
import { UpdateTeamInput } from './dtos/TeamDto';
|
|
import { AuthenticationGuard } from '../auth/AuthenticationGuard';
|
|
import { AuthorizationGuard } from '../auth/AuthorizationGuard';
|
|
import type { AuthorizationService } from '../auth/AuthorizationService';
|
|
import { FeatureAvailabilityGuard } from '../policy/FeatureAvailabilityGuard';
|
|
import type { PolicyService, PolicySnapshot } from '../policy/PolicyService';
|
|
|
|
describe('TeamController', () => {
|
|
let controller: TeamController;
|
|
let service: ReturnType<typeof vi.mocked<TeamService>>;
|
|
|
|
beforeEach(async () => {
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
controllers: [TeamController],
|
|
providers: [
|
|
{
|
|
provide: TeamService,
|
|
useValue: {
|
|
getAll: vi.fn(),
|
|
getDetails: vi.fn(),
|
|
getMembers: vi.fn(),
|
|
getJoinRequests: vi.fn(),
|
|
create: vi.fn(),
|
|
update: vi.fn(),
|
|
getDriverTeam: vi.fn(),
|
|
getMembership: vi.fn(),
|
|
},
|
|
},
|
|
],
|
|
}).compile();
|
|
|
|
controller = module.get<TeamController>(TeamController);
|
|
service = vi.mocked(module.get(TeamService));
|
|
});
|
|
|
|
describe('getAll', () => {
|
|
it('should return all teams', async () => {
|
|
const result = { teams: [], totalCount: 0 };
|
|
service.getAll.mockResolvedValue(result);
|
|
|
|
const response = await controller.getAll();
|
|
|
|
expect(service.getAll).toHaveBeenCalled();
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('getDetails', () => {
|
|
it('should return team details', async () => {
|
|
const teamId = 'team-123';
|
|
const userId = 'user-456';
|
|
const result = { team: { id: teamId, name: 'Team', tag: 'TAG', description: 'Desc', ownerId: 'owner', leagues: [], isRecruiting: false }, membership: null, canManage: false };
|
|
service.getDetails.mockResolvedValue(result);
|
|
|
|
const mockReq = { user: { userId } } as never;
|
|
|
|
const response = await controller.getDetails(teamId, mockReq);
|
|
|
|
expect(service.getDetails).toHaveBeenCalledWith(teamId, userId);
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('getMembers', () => {
|
|
it('should return team members', async () => {
|
|
const teamId = 'team-123';
|
|
const result = { members: [], totalCount: 0, ownerCount: 0, managerCount: 0, memberCount: 0 };
|
|
service.getMembers.mockResolvedValue(result);
|
|
|
|
const response = await controller.getMembers(teamId);
|
|
|
|
expect(service.getMembers).toHaveBeenCalledWith(teamId);
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('getJoinRequests', () => {
|
|
it('should return join requests', async () => {
|
|
const teamId = 'team-123';
|
|
const result = { requests: [], pendingCount: 0, totalCount: 0 };
|
|
service.getJoinRequests.mockResolvedValue(result);
|
|
|
|
const response = await controller.getJoinRequests(teamId);
|
|
|
|
expect(service.getJoinRequests).toHaveBeenCalledWith(teamId);
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('create', () => {
|
|
it('should create team', async () => {
|
|
const input: CreateTeamInputDTO = { name: 'New Team', tag: 'TAG' };
|
|
const userId = 'user-123';
|
|
const result = { id: 'team-456', success: true };
|
|
service.create.mockResolvedValue(result);
|
|
|
|
const mockReq = { user: { userId } } as never;
|
|
|
|
const response = await controller.create(input, mockReq);
|
|
|
|
expect(service.create).toHaveBeenCalledWith(input, userId);
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('update', () => {
|
|
it('should update team', async () => {
|
|
const teamId = 'team-123';
|
|
const userId = 'user-456';
|
|
const input: UpdateTeamInput = { name: 'Updated Team', updatedBy: userId };
|
|
const result = { success: true };
|
|
service.update.mockResolvedValue(result);
|
|
|
|
const mockReq = { user: { userId } } as never;
|
|
|
|
const response = await controller.update(teamId, input, mockReq);
|
|
|
|
expect(service.update).toHaveBeenCalledWith(teamId, input, userId);
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('getDriverTeam', () => {
|
|
it('should return driver team', async () => {
|
|
const driverId = 'driver-123';
|
|
const result = { team: { id: 'team-456', name: 'Team', tag: 'TAG', description: 'Desc', ownerId: 'owner', leagues: [], isRecruiting: false }, membership: { role: 'member' as const, joinedAt: '2023-01-01', isActive: true }, isOwner: false, canManage: false };
|
|
service.getDriverTeam.mockResolvedValue(result);
|
|
|
|
const response = await controller.getDriverTeam(driverId);
|
|
|
|
expect(service.getDriverTeam).toHaveBeenCalledWith(driverId);
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('getMembership', () => {
|
|
it('should return team membership', async () => {
|
|
const teamId = 'team-123';
|
|
const driverId = 'driver-456';
|
|
const result = { role: 'member' as const, joinedAt: '2023-01-01', isActive: true };
|
|
service.getMembership.mockResolvedValue(result);
|
|
|
|
const response = await controller.getMembership(teamId, driverId);
|
|
|
|
expect(service.getMembership).toHaveBeenCalledWith(teamId, driverId);
|
|
expect(response).toEqual(result);
|
|
});
|
|
});
|
|
|
|
describe('auth guards (HTTP)', () => {
|
|
let app: import("@nestjs/common").INestApplication;
|
|
|
|
const sessionPort: { getCurrentSession: () => Promise<null | { token: string; user: { id: string } }> } = {
|
|
getCurrentSession: vi.fn(async () => null),
|
|
};
|
|
|
|
const authorizationService: Pick<AuthorizationService, 'getRolesForUser'> = {
|
|
getRolesForUser: vi.fn(() => []),
|
|
};
|
|
|
|
const policyService: Pick<PolicyService, 'getSnapshot'> = {
|
|
getSnapshot: vi.fn(async (): Promise<PolicySnapshot> => ({
|
|
policyVersion: 1,
|
|
operationalMode: 'normal',
|
|
maintenanceAllowlist: { view: [], mutate: [] },
|
|
capabilities: {},
|
|
loadedFrom: 'defaults',
|
|
loadedAtIso: new Date(0).toISOString(),
|
|
})),
|
|
};
|
|
|
|
beforeEach(async () => {
|
|
const module = await Test.createTestingModule({
|
|
controllers: [TeamController],
|
|
providers: [
|
|
{
|
|
provide: TeamService,
|
|
useValue: {
|
|
getAll: vi.fn(async () => ({ teams: [], totalCount: 0 })),
|
|
getJoinRequests: vi.fn(async () => ({ requests: [], pendingCount: 0, totalCount: 0 })),
|
|
},
|
|
},
|
|
],
|
|
}).compile();
|
|
|
|
app = module.createNestApplication();
|
|
|
|
const reflector = new Reflector();
|
|
app.useGlobalGuards(
|
|
new AuthenticationGuard(sessionPort as never),
|
|
new AuthorizationGuard(reflector, authorizationService as never),
|
|
new FeatureAvailabilityGuard(reflector, policyService as never),
|
|
);
|
|
|
|
await app.init();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await app?.close();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('allows @Public() endpoint without a session', async () => {
|
|
await request(app.getHttpServer()).get('/teams/all').expect(200);
|
|
});
|
|
|
|
it('denies non-public endpoint by default when not authenticated (401)', async () => {
|
|
await request(app.getHttpServer()).get('/teams/t1/join-requests').expect(401);
|
|
});
|
|
|
|
it('allows non-public endpoint when authenticated via session port', async () => {
|
|
vi.mocked(sessionPort.getCurrentSession).mockResolvedValueOnce({
|
|
token: 't',
|
|
user: { id: 'user-1' },
|
|
});
|
|
|
|
await request(app.getHttpServer()).get('/teams/t1/join-requests').expect(200);
|
|
});
|
|
});
|
|
}); |