good forst commit
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { MyService } from './my.service';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
|
||||
describe('MyService', () => {
|
||||
let service: MyService;
|
||||
const mockPrisma: any = {
|
||||
towerUser: { findFirst: jest.fn(), delete: jest.fn() },
|
||||
consentRecord: {
|
||||
findMany: jest.fn(),
|
||||
findFirst: jest.fn(),
|
||||
update: jest.fn(),
|
||||
updateMany: jest.fn(),
|
||||
create: jest.fn(),
|
||||
deleteMany: jest.fn(),
|
||||
},
|
||||
group: { findFirst: jest.fn() },
|
||||
memberOptOut: { create: jest.fn(), deleteMany: jest.fn() },
|
||||
towerSession: { deleteMany: jest.fn() },
|
||||
$transaction: jest.fn(),
|
||||
};
|
||||
const mockAudit = { log: jest.fn() };
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
mockPrisma.$transaction.mockImplementation((cb: (tx: typeof mockPrisma) => Promise<unknown>) => cb(mockPrisma));
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
MyService,
|
||||
{ provide: PrismaService, useValue: mockPrisma },
|
||||
{ provide: AuditService, useValue: mockAudit },
|
||||
],
|
||||
}).compile();
|
||||
service = module.get<MyService>(MyService);
|
||||
});
|
||||
|
||||
describe('getProfile', () => {
|
||||
it('returns member profile', async () => {
|
||||
mockPrisma.towerUser.findFirst.mockResolvedValue({
|
||||
id: 'u-1',
|
||||
tenantId: 'tnt-1',
|
||||
jid: '1234@s.whatsapp.net',
|
||||
displayName: 'Alice',
|
||||
createdAt: new Date('2026-01-01T00:00:00Z'),
|
||||
});
|
||||
const res = await service.getProfile('u-1', 'tnt-1');
|
||||
expect(res.id).toBe('u-1');
|
||||
expect(res.displayName).toBe('Alice');
|
||||
});
|
||||
|
||||
it('throws when not found', async () => {
|
||||
mockPrisma.towerUser.findFirst.mockResolvedValue(null);
|
||||
await expect(service.getProfile('u-x', 'tnt-1')).rejects.toThrow(NotFoundException);
|
||||
});
|
||||
});
|
||||
|
||||
describe('listGroups', () => {
|
||||
it('returns groups with consent metadata', async () => {
|
||||
mockPrisma.consentRecord.findMany.mockResolvedValue([
|
||||
{
|
||||
group: { id: 'g-1', name: 'UP Parivar' },
|
||||
tenantId: 'tnt-1',
|
||||
groupId: 'g-1',
|
||||
scopes: ['INGEST', 'DISPLAY'],
|
||||
retentionDays: 90,
|
||||
policyVersion: 'v1',
|
||||
status: 'GRANTED',
|
||||
effectiveAt: new Date('2026-01-01T00:00:00Z'),
|
||||
},
|
||||
]);
|
||||
const res = await service.listGroups('u-1', 'tnt-1');
|
||||
expect(res).toHaveLength(1);
|
||||
expect(res[0].name).toBe('UP Parivar');
|
||||
expect(res[0].scopes).toContain('INGEST');
|
||||
});
|
||||
});
|
||||
|
||||
describe('optOut', () => {
|
||||
it('throws when no matching consent', async () => {
|
||||
mockPrisma.consentRecord.findMany.mockResolvedValue([]);
|
||||
await expect(service.optOut('u-1', 'tnt-1', { groupId: 'g-1' })).rejects.toThrow(NotFoundException);
|
||||
});
|
||||
|
||||
it('revokes consent and creates MemberOptOut', async () => {
|
||||
mockPrisma.consentRecord.findMany.mockResolvedValue([
|
||||
{ id: 'c-1', groupId: 'g-1', scopes: ['INGEST', 'DISPLAY'] },
|
||||
]);
|
||||
const res = await service.optOut('u-1', 'tnt-1', { groupId: 'g-1', reason: 'SELF_PORTAL' });
|
||||
expect(res.revoked).toBe(1);
|
||||
expect(mockPrisma.consentRecord.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { id: 'c-1' },
|
||||
data: expect.objectContaining({ status: 'REVOKED' }),
|
||||
}),
|
||||
);
|
||||
expect(mockPrisma.memberOptOut.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ data: expect.objectContaining({ reason: 'SELF_PORTAL' }) }),
|
||||
);
|
||||
expect(mockAudit.log).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ action: 'MEMBER_OPT_OUT' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('optIn', () => {
|
||||
it('rejects empty scopes', async () => {
|
||||
await expect(
|
||||
service.optIn('u-1', 'tnt-1', { groupId: 'g-1', scopes: [] }),
|
||||
).rejects.toThrow(BadRequestException);
|
||||
});
|
||||
|
||||
it('creates a new consent record', async () => {
|
||||
mockPrisma.group.findFirst.mockResolvedValue({ id: 'g-1', tenantId: 'tnt-1' });
|
||||
mockPrisma.towerUser.findFirst.mockResolvedValue({ id: 'u-1', tenantId: 'tnt-1' });
|
||||
mockPrisma.consentRecord.findFirst.mockResolvedValue(null);
|
||||
mockPrisma.consentRecord.create.mockResolvedValue({ id: 'c-new' });
|
||||
const res = await service.optIn('u-1', 'tnt-1', { groupId: 'g-1', scopes: ['INGEST'] });
|
||||
expect(res.ok).toBe(true);
|
||||
expect(res.consentId).toBe('c-new');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteAccount', () => {
|
||||
it('cascades deletes and writes audit', async () => {
|
||||
const res = await service.deleteAccount('u-1', 'tnt-1');
|
||||
expect(res.ok).toBe(true);
|
||||
expect(mockPrisma.consentRecord.deleteMany).toHaveBeenCalled();
|
||||
expect(mockPrisma.towerSession.deleteMany).toHaveBeenCalled();
|
||||
expect(mockAudit.log).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ action: 'MEMBER_DELETED', resourceId: 'u-1' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user