import { requireRole } from '@/gateway/rbac/rbac'; import { OperatorRole } from '@/gateway/auth/types'; import { TestHelpers } from '../utils/test-helpers'; import { Response, NextFunction } from 'express'; describe('RBAC (Role-Based Access Control)', () => { let makerOperator: any; let checkerOperator: any; let adminOperator: any; beforeAll(async () => { makerOperator = await TestHelpers.createTestOperator('TEST_RBAC_MAKER', 'MAKER' as any); checkerOperator = await TestHelpers.createTestOperator('TEST_RBAC_CHECKER', 'CHECKER' as any); adminOperator = await TestHelpers.createTestOperator('TEST_RBAC_ADMIN', 'ADMIN' as any); }); afterAll(async () => { await TestHelpers.cleanDatabase(); }); describe('requireRole', () => { it('should allow MAKER role for MAKER endpoints', async () => { const middleware = requireRole(OperatorRole.MAKER); const req = { operator: { id: makerOperator.id, operatorId: makerOperator.operatorId, role: OperatorRole.MAKER, }, } as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; const next = jest.fn() as NextFunction; middleware(req, res, next); // Middleware is synchronous, next should be called immediately expect(next).toHaveBeenCalled(); // No error passed }); it('should allow ADMIN role for MAKER endpoints', async () => { const middleware = requireRole(OperatorRole.MAKER); const req = { operator: { id: adminOperator.id, operatorId: adminOperator.operatorId, role: OperatorRole.ADMIN, }, } as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; const next = jest.fn() as NextFunction; middleware(req, res, next); // Middleware is synchronous, next should be called immediately expect(next).toHaveBeenCalled(); // No error passed }); it('should reject CHECKER role for MAKER-only endpoints', async () => { const middleware = requireRole(OperatorRole.MAKER); const req = { operator: { id: checkerOperator.id, operatorId: checkerOperator.operatorId, role: OperatorRole.CHECKER, }, } as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; const next = jest.fn() as NextFunction; await middleware(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); it('should allow CHECKER role for CHECKER endpoints', async () => { const middleware = requireRole(OperatorRole.CHECKER); const req = { operator: { id: checkerOperator.id, operatorId: checkerOperator.operatorId, role: OperatorRole.CHECKER, }, } as any; const res = {} as Response; const next = jest.fn() as NextFunction; await middleware(req, res, next); // Middleware is synchronous, next should be called immediately expect(next).toHaveBeenCalled(); }); it('should allow ADMIN role for CHECKER endpoints', async () => { const middleware = requireRole(OperatorRole.CHECKER); const req = { operator: { id: adminOperator.id, operatorId: adminOperator.operatorId, role: OperatorRole.ADMIN, }, } as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; const next = jest.fn() as NextFunction; middleware(req, res, next); // Middleware is synchronous, next should be called immediately expect(next).toHaveBeenCalled(); }); it('should require ADMIN role for ADMIN-only endpoints', async () => { const middleware = requireRole(OperatorRole.ADMIN); const req = { operator: { id: adminOperator.id, operatorId: adminOperator.operatorId, role: OperatorRole.ADMIN, }, } as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; const next = jest.fn() as NextFunction; middleware(req, res, next); // Middleware is synchronous, next should be called immediately expect(next).toHaveBeenCalled(); }); it('should reject MAKER role for ADMIN-only endpoints', async () => { const middleware = requireRole(OperatorRole.ADMIN); const req = { operator: { id: makerOperator.id, operatorId: makerOperator.operatorId, role: OperatorRole.MAKER, }, } as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; const next = jest.fn() as NextFunction; await middleware(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); }); describe('Dual Control Enforcement', () => { it('should enforce MAKER can initiate but not approve', () => { // MAKER can use MAKER endpoints const makerMiddleware = requireRole(OperatorRole.MAKER); const makerReq = { operator: { id: makerOperator.id, role: OperatorRole.MAKER, }, } as any; const res = {} as Response; const next = jest.fn() as NextFunction; makerMiddleware(makerReq, res, next); expect(next).toHaveBeenCalledWith(); // MAKER cannot use CHECKER endpoints const checkerMiddleware = requireRole(OperatorRole.CHECKER); const checkerRes = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; checkerMiddleware(makerReq, checkerRes, next); expect(checkerRes.status).toHaveBeenCalledWith(403); }); it('should enforce CHECKER can approve but not initiate', () => { // CHECKER can use CHECKER endpoints const checkerMiddleware = requireRole(OperatorRole.CHECKER); const checkerReq = { operator: { id: checkerOperator.id, operatorId: checkerOperator.operatorId, role: OperatorRole.CHECKER, }, } as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; const next = jest.fn() as NextFunction; checkerMiddleware(checkerReq, res, next); expect(next).toHaveBeenCalledWith(); // CHECKER cannot use MAKER-only endpoints (if restricted) const makerMiddleware = requireRole(OperatorRole.MAKER); const makerRes = { status: jest.fn().mockReturnThis(), json: jest.fn(), } as unknown as Response; makerMiddleware(checkerReq, makerRes, next); expect(makerRes.status).toHaveBeenCalledWith(403); }); }); });