import { Test, TestingModule } from '@nestjs/testing'; import { ConflictException, NotFoundException } from '@nestjs/common'; import { RulesService } from './rules.service'; import { PrismaService } from '../../prisma/prisma.service'; describe('RulesService', () => { let service: RulesService; const mockPrisma: any = { tenantRule: { findMany: jest.fn(), findUnique: jest.fn(), findFirst: jest.fn(), create: jest.fn(), update: jest.fn(), delete: jest.fn() }, }; beforeEach(async () => { jest.clearAllMocks(); const module: TestingModule = await Test.createTestingModule({ providers: [ RulesService, { provide: PrismaService, useValue: mockPrisma }, ], }).compile(); service = module.get(RulesService); }); describe('list', () => { it('returns rules ordered by priority', async () => { mockPrisma.tenantRule.findMany.mockResolvedValue([ { id: 'r1', tenantId: 'tnt-1', matchType: 'HASHTAG', matchValue: '#important', action: 'FLAG', priority: 0, isActive: true, createdAt: new Date('2026-01-01'), updatedAt: new Date('2026-01-01') }, { id: 'r2', tenantId: 'tnt-1', matchType: 'PREFIX', matchValue: '/tower', action: 'FLAG', priority: 1, isActive: true, createdAt: new Date('2026-01-02'), updatedAt: new Date('2026-01-02') }, ]); const res = await service.list('tnt-1'); expect(res).toHaveLength(2); expect(res[0].matchValue).toBe('#important'); expect(mockPrisma.tenantRule.findMany).toHaveBeenCalledWith(expect.objectContaining({ where: { tenantId: 'tnt-1' } })); }); }); describe('create', () => { it('creates and returns a rule', async () => { mockPrisma.tenantRule.findUnique.mockResolvedValue(null); mockPrisma.tenantRule.create.mockResolvedValue({ id: 'r1', tenantId: 'tnt-1', matchType: 'HASHTAG', matchValue: '#event', action: 'FLAG', priority: 0, isActive: true, createdAt: new Date('2026-01-01'), updatedAt: new Date('2026-01-01'), }); const res = await service.create('tnt-1', { matchType: 'HASHTAG', matchValue: '#event', action: 'FLAG' }); expect(res.matchValue).toBe('#event'); }); it('rejects duplicate rule', async () => { mockPrisma.tenantRule.findUnique.mockResolvedValue({ id: 'existing' }); await expect(service.create('tnt-1', { matchType: 'HASHTAG', matchValue: '#event', action: 'FLAG' })).rejects.toThrow(ConflictException); }); }); describe('update', () => { it('updates and returns a rule', async () => { mockPrisma.tenantRule.findFirst.mockResolvedValue({ id: 'r1', tenantId: 'tnt-1' }); mockPrisma.tenantRule.update.mockResolvedValue({ id: 'r1', tenantId: 'tnt-1', matchType: 'HASHTAG', matchValue: '#event', action: 'AUTO_APPROVE', priority: 0, isActive: true, createdAt: new Date('2026-01-01'), updatedAt: new Date('2026-01-02'), }); const res = await service.update('tnt-1', 'r1', { action: 'AUTO_APPROVE' }); expect(res.action).toBe('AUTO_APPROVE'); }); it('throws on non-existent rule', async () => { mockPrisma.tenantRule.findFirst.mockResolvedValue(null); await expect(service.update('tnt-1', 'missing', { action: 'AUTO_APPROVE' })).rejects.toThrow(NotFoundException); }); }); describe('remove', () => { it('deletes a rule', async () => { mockPrisma.tenantRule.findFirst.mockResolvedValue({ id: 'r1', tenantId: 'tnt-1' }); mockPrisma.tenantRule.delete.mockResolvedValue({}); await expect(service.remove('tnt-1', 'r1')).resolves.toBeUndefined(); }); it('throws on non-existent rule', async () => { mockPrisma.tenantRule.findFirst.mockResolvedValue(null); await expect(service.remove('tnt-1', 'missing')).rejects.toThrow(NotFoundException); }); }); });