fix: address penalty catalog review findings
This commit is contained in:
@@ -26,6 +26,11 @@ type AuthenticatedRequest = { user: { id: number } };
|
||||
export class PenaltyController {
|
||||
constructor(private readonly service: PenaltyService) {}
|
||||
|
||||
@Get()
|
||||
getUserPenalties(@Request() request: AuthenticatedRequest) {
|
||||
return this.service.getUserPenalties(request.user.id);
|
||||
}
|
||||
|
||||
@Get(':teamId')
|
||||
getTeamPenalties(
|
||||
@Request() request: AuthenticatedRequest,
|
||||
|
||||
@@ -20,6 +20,7 @@ describe('penalty catalog HTTP boundary', () => {
|
||||
createdAt: '2026-01-02T00:00:00.000Z',
|
||||
};
|
||||
const service = {
|
||||
getUserPenalties: jest.fn(() => [penalty]),
|
||||
getTeamPenalties: jest.fn(() => [penalty]),
|
||||
createPenalty: jest.fn(() => penalty),
|
||||
updatePenalty: jest.fn(() => penalty),
|
||||
@@ -62,6 +63,15 @@ describe('penalty catalog HTTP boundary', () => {
|
||||
expect(service.getTeamPenalties).toHaveBeenCalledWith(42, 5);
|
||||
});
|
||||
|
||||
it('keeps the authenticated cross-team catalog route available', async () => {
|
||||
await request(app.getHttpServer()).get('/api/v1/penalty').expect(401);
|
||||
await request(app.getHttpServer())
|
||||
.get('/api/v1/penalty')
|
||||
.set('Authorization', 'Bearer user')
|
||||
.expect(200, [penalty]);
|
||||
expect(service.getUserPenalties).toHaveBeenCalledWith(42);
|
||||
});
|
||||
|
||||
it('validates and strips create fields before invoking the service', async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/v1/penalty')
|
||||
|
||||
@@ -55,6 +55,33 @@ describe('PenaltyService catalog management', () => {
|
||||
expect(access.assertMember).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('keeps the authenticated cross-team catalog route compatible and safely mapped', async () => {
|
||||
readRepository.find.mockResolvedValue([
|
||||
{
|
||||
id: 8,
|
||||
description: 'Zu spät',
|
||||
amount: '5.50',
|
||||
createdAt: new Date('2026-01-02T00:00:00.000Z'),
|
||||
team: { id: 5, secret: 'hidden' },
|
||||
},
|
||||
]);
|
||||
|
||||
await expect(service.getUserPenalties(42)).resolves.toEqual([
|
||||
{
|
||||
id: 8,
|
||||
description: 'Zu spät',
|
||||
amount: 5.5,
|
||||
createdAt: new Date('2026-01-02T00:00:00.000Z'),
|
||||
},
|
||||
]);
|
||||
expect(readRepository.find).toHaveBeenCalledWith({
|
||||
where: {
|
||||
team: { players: { user: { id: 42 }, active: true } },
|
||||
},
|
||||
order: { description: 'ASC' },
|
||||
});
|
||||
});
|
||||
|
||||
it('authorizes team reads, sorts them, and maps only safe fields', async () => {
|
||||
readRepository.find.mockResolvedValue([
|
||||
{
|
||||
@@ -136,11 +163,7 @@ describe('PenaltyService catalog management', () => {
|
||||
writeRepository.save.mockImplementation(async (value) => value);
|
||||
|
||||
await expect(
|
||||
service.updatePenalty(
|
||||
8,
|
||||
{ description: ' Neu ', amount: 2.5 },
|
||||
42,
|
||||
),
|
||||
service.updatePenalty(8, { description: ' Neu ', amount: 2.5 }, 42),
|
||||
).resolves.toMatchObject({ id: 8, description: 'Neu', amount: 2.5 });
|
||||
expect(duplicateQuery.andWhere).toHaveBeenCalledWith(
|
||||
'penalty.id != :penaltyId',
|
||||
@@ -194,10 +217,7 @@ describe('PenaltyService catalog management', () => {
|
||||
logger.info.mockRejectedValue(new Error('audit unavailable'));
|
||||
|
||||
await expect(
|
||||
service.createPenalty(
|
||||
{ teamId: 5, description: 'Neu', amount: 2 },
|
||||
42,
|
||||
),
|
||||
service.createPenalty({ teamId: 5, description: 'Neu', amount: 2 }, 42),
|
||||
).rejects.toThrow('audit unavailable');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,6 +25,16 @@ export class PenaltyService {
|
||||
private readonly logger: LoggingService,
|
||||
) {}
|
||||
|
||||
async getUserPenalties(userId: number): Promise<PenaltyResponseDTO[]> {
|
||||
const penalties = await this.repository.find({
|
||||
where: {
|
||||
team: { players: { user: { id: userId }, active: true } },
|
||||
},
|
||||
order: { description: 'ASC' },
|
||||
});
|
||||
return penalties.map((penalty) => this.toResponse(penalty));
|
||||
}
|
||||
|
||||
async getTeamPenalties(
|
||||
userId: number,
|
||||
teamId: number,
|
||||
@@ -133,7 +143,10 @@ export class PenaltyService {
|
||||
return penalty;
|
||||
}
|
||||
|
||||
private async lockTeam(manager: EntityManager, teamId: number): Promise<Team> {
|
||||
private async lockTeam(
|
||||
manager: EntityManager,
|
||||
teamId: number,
|
||||
): Promise<Team> {
|
||||
const team = await manager
|
||||
.getRepository(Team)
|
||||
.createQueryBuilder('team')
|
||||
|
||||
Reference in New Issue
Block a user