fix(tst-005): use v4-compliant UUIDs, replace __proto__ with hackField for non-whitelisted test
This commit is contained in:
parent
8d6a2c2086
commit
9cf210976c
1 changed files with 13 additions and 25 deletions
|
|
@ -8,6 +8,7 @@
|
||||||
* - BadRequestException for invalid status filter
|
* - BadRequestException for invalid status filter
|
||||||
*
|
*
|
||||||
* GoalsService is mocked — no real DB needed.
|
* GoalsService is mocked — no real DB needed.
|
||||||
|
* UUIDs are v4-compliant (version nibble=4, variant nibble=8) so @IsUUID() passes.
|
||||||
*/
|
*/
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import {
|
import {
|
||||||
|
|
@ -24,24 +25,19 @@ import { Test, type TestingModule } from '@nestjs/testing';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { APP_GUARD } from '@nestjs/core';
|
import { APP_GUARD } from '@nestjs/core';
|
||||||
import { SetMetadata } from '@nestjs/common';
|
|
||||||
import { GoalsController } from '../goals/goals.controller';
|
import { GoalsController } from '../goals/goals.controller';
|
||||||
import { GoalsService } from '../goals/goals.service';
|
import { GoalsService } from '../goals/goals.service';
|
||||||
|
|
||||||
// ── Stub auth guard ────────────────────────────────────────────────────────
|
// ── Stub auth guard ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const IS_PUBLIC = 'isPublic';
|
|
||||||
const Public = () => SetMetadata(IS_PUBLIC, true);
|
|
||||||
void Public; // referenced in controller via global metadata
|
|
||||||
|
|
||||||
const FAKE_SESSION = {
|
const FAKE_SESSION = {
|
||||||
requestId: 'req-1',
|
requestId: 'req-1',
|
||||||
correlationId: 'cor-1',
|
correlationId: 'cor-1',
|
||||||
sessionId: 'ses-1',
|
sessionId: 'ses-1',
|
||||||
userId: '00000000-0000-0000-0000-000000000001',
|
userId: '1a000000-0000-4000-8000-000000000001',
|
||||||
tenantId: '00000000-0000-0000-0000-000000000002',
|
tenantId: '1a000000-0000-4000-8000-000000000002',
|
||||||
workspaceId: '00000000-0000-0000-0000-000000000003',
|
workspaceId: '1a000000-0000-4000-8000-000000000003',
|
||||||
membershipId: '00000000-0000-0000-0000-000000000004',
|
membershipId: '1a000000-0000-4000-8000-000000000004',
|
||||||
role: 'owner' as const,
|
role: 'owner' as const,
|
||||||
roles: ['owner' as const],
|
roles: ['owner' as const],
|
||||||
permissions: ['*'],
|
permissions: ['*'],
|
||||||
|
|
@ -54,10 +50,6 @@ const FAKE_SESSION = {
|
||||||
class StubAuthGuard implements CanActivate {
|
class StubAuthGuard implements CanActivate {
|
||||||
constructor(private readonly reflector: Reflector) {}
|
constructor(private readonly reflector: Reflector) {}
|
||||||
canActivate(context: ExecutionContext): boolean {
|
canActivate(context: ExecutionContext): boolean {
|
||||||
const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
|
|
||||||
context.getHandler(), context.getClass(),
|
|
||||||
]);
|
|
||||||
if (isPublic) return true;
|
|
||||||
const req = context.switchToHttp().getRequest<{ headers: Record<string, string>; session?: typeof FAKE_SESSION }>();
|
const req = context.switchToHttp().getRequest<{ headers: Record<string, string>; session?: typeof FAKE_SESSION }>();
|
||||||
const auth = req.headers['authorization'] ?? '';
|
const auth = req.headers['authorization'] ?? '';
|
||||||
if (!auth.startsWith('Bearer ') || !auth.slice(7).trim()) {
|
if (!auth.startsWith('Bearer ') || !auth.slice(7).trim()) {
|
||||||
|
|
@ -71,9 +63,9 @@ class StubAuthGuard implements CanActivate {
|
||||||
// ── Mock service ───────────────────────────────────────────────────────────
|
// ── Mock service ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const MOCK_GOAL = {
|
const MOCK_GOAL = {
|
||||||
id: '00000000-0000-0000-0000-aaaa00000001',
|
id: '1a000000-0000-4000-8000-aaaa00000001',
|
||||||
tenantId: FAKE_SESSION.tenantId,
|
tenantId: '1a000000-0000-4000-8000-000000000002',
|
||||||
ownerUserId: FAKE_SESSION.userId,
|
ownerUserId: '1a000000-0000-4000-8000-000000000001',
|
||||||
horizon: 'Q4 2026',
|
horizon: 'Q4 2026',
|
||||||
metric: 'MRR',
|
metric: 'MRR',
|
||||||
target: '100k',
|
target: '100k',
|
||||||
|
|
@ -149,7 +141,7 @@ describe('TST-005: Goals API Integration', () => {
|
||||||
.set('Authorization', AUTH)
|
.set('Authorization', AUTH)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
expect(mockGoalsService.list).toHaveBeenCalledWith(
|
expect(mockGoalsService.list).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ userId: FAKE_SESSION.userId }),
|
expect.objectContaining({ userId: '1a000000-0000-4000-8000-000000000001' }),
|
||||||
'on_track',
|
'on_track',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -187,7 +179,7 @@ describe('TST-005: Goals API Integration', () => {
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.post('/v1/goals')
|
.post('/v1/goals')
|
||||||
.set('Authorization', AUTH)
|
.set('Authorization', AUTH)
|
||||||
.send({ horizon: 'Q4 2026', metric: 'MRR', target: '100k', __proto__: 'injection' })
|
.send({ horizon: 'Q4 2026', metric: 'MRR', target: '100k', hackField: 'injection' })
|
||||||
.expect(400);
|
.expect(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -203,11 +195,9 @@ describe('TST-005: Goals API Integration', () => {
|
||||||
// ── PATCH /v1/goals/:id ───────────────────────────────────────────────────
|
// ── PATCH /v1/goals/:id ───────────────────────────────────────────────────
|
||||||
|
|
||||||
describe('PATCH /v1/goals/:id', () => {
|
describe('PATCH /v1/goals/:id', () => {
|
||||||
const VALID_UUID = '00000000-0000-0000-0000-aaaa00000001';
|
|
||||||
|
|
||||||
it('returns 200 with valid UUID and valid body', async () => {
|
it('returns 200 with valid UUID and valid body', async () => {
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.patch(`/v1/goals/${VALID_UUID}`)
|
.patch('/v1/goals/1a000000-0000-4000-8000-aaaa00000001')
|
||||||
.set('Authorization', AUTH)
|
.set('Authorization', AUTH)
|
||||||
.send({ status: 'on_track' })
|
.send({ status: 'on_track' })
|
||||||
.expect(200);
|
.expect(200);
|
||||||
|
|
@ -223,7 +213,7 @@ describe('TST-005: Goals API Integration', () => {
|
||||||
|
|
||||||
it('returns 400 for invalid status value in body', async () => {
|
it('returns 400 for invalid status value in body', async () => {
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.patch(`/v1/goals/${VALID_UUID}`)
|
.patch('/v1/goals/1a000000-0000-4000-8000-aaaa00000001')
|
||||||
.set('Authorization', AUTH)
|
.set('Authorization', AUTH)
|
||||||
.send({ status: 'flying' })
|
.send({ status: 'flying' })
|
||||||
.expect(400);
|
.expect(400);
|
||||||
|
|
@ -233,11 +223,9 @@ describe('TST-005: Goals API Integration', () => {
|
||||||
// ── DELETE /v1/goals/:id ──────────────────────────────────────────────────
|
// ── DELETE /v1/goals/:id ──────────────────────────────────────────────────
|
||||||
|
|
||||||
describe('DELETE /v1/goals/:id', () => {
|
describe('DELETE /v1/goals/:id', () => {
|
||||||
const VALID_UUID = '00000000-0000-0000-0000-aaaa00000001';
|
|
||||||
|
|
||||||
it('returns 200 with valid UUID', async () => {
|
it('returns 200 with valid UUID', async () => {
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.delete(`/v1/goals/${VALID_UUID}`)
|
.delete('/v1/goals/1a000000-0000-4000-8000-aaaa00000001')
|
||||||
.set('Authorization', AUTH)
|
.set('Authorization', AUTH)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
expect(mockGoalsService.softDelete).toHaveBeenCalledTimes(1);
|
expect(mockGoalsService.softDelete).toHaveBeenCalledTimes(1);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue