From 0e92b24bf066a9db0dcee4e287193e6a4616617a Mon Sep 17 00:00:00 2001 From: maaz519 Date: Thu, 28 May 2026 00:55:13 +0530 Subject: [PATCH] feat(api): add GroupsModule with GET /groups endpoint Implements TDD-driven GroupsService and GroupsController with full unit test coverage. GET /groups returns all groups ordered by name, selecting id, name, platform, platformId, isActive, and accountId fields. Co-Authored-By: Claude Sonnet 4.6 --- .../modules/groups/groups.controller.spec.ts | 26 +++++++++++++++ .../src/modules/groups/groups.controller.ts | 12 +++++++ apps/api/src/modules/groups/groups.module.ts | 9 +++++ .../src/modules/groups/groups.service.spec.ts | 33 +++++++++++++++++++ apps/api/src/modules/groups/groups.service.ts | 14 ++++++++ 5 files changed, 94 insertions(+) create mode 100644 apps/api/src/modules/groups/groups.controller.spec.ts create mode 100644 apps/api/src/modules/groups/groups.controller.ts create mode 100644 apps/api/src/modules/groups/groups.module.ts create mode 100644 apps/api/src/modules/groups/groups.service.spec.ts create mode 100644 apps/api/src/modules/groups/groups.service.ts diff --git a/apps/api/src/modules/groups/groups.controller.spec.ts b/apps/api/src/modules/groups/groups.controller.spec.ts new file mode 100644 index 0000000..fa81063 --- /dev/null +++ b/apps/api/src/modules/groups/groups.controller.spec.ts @@ -0,0 +1,26 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { GroupsController } from './groups.controller'; +import { GroupsService } from './groups.service'; + +const mockGroups = [ + { id: 'grp_1', name: 'Alpha', platform: 'whatsapp', platformId: '111@g.us', isActive: true, accountId: 'acc_1' }, +]; +const mockService = { list: jest.fn().mockResolvedValue(mockGroups) }; + +describe('GroupsController', () => { + let controller: GroupsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [GroupsController], + providers: [{ provide: GroupsService, useValue: mockService }], + }).compile(); + controller = module.get(GroupsController); + }); + + it('returns groups from service', async () => { + const result = await controller.list(); + expect(result).toEqual(mockGroups); + expect(mockService.list).toHaveBeenCalled(); + }); +}); diff --git a/apps/api/src/modules/groups/groups.controller.ts b/apps/api/src/modules/groups/groups.controller.ts new file mode 100644 index 0000000..0e16891 --- /dev/null +++ b/apps/api/src/modules/groups/groups.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { GroupsService } from './groups.service'; + +@Controller('groups') +export class GroupsController { + constructor(private readonly groupsService: GroupsService) {} + + @Get() + list() { + return this.groupsService.list(); + } +} diff --git a/apps/api/src/modules/groups/groups.module.ts b/apps/api/src/modules/groups/groups.module.ts new file mode 100644 index 0000000..8cf870b --- /dev/null +++ b/apps/api/src/modules/groups/groups.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { GroupsController } from './groups.controller'; +import { GroupsService } from './groups.service'; + +@Module({ + controllers: [GroupsController], + providers: [GroupsService], +}) +export class GroupsModule {} diff --git a/apps/api/src/modules/groups/groups.service.spec.ts b/apps/api/src/modules/groups/groups.service.spec.ts new file mode 100644 index 0000000..d7bdb1a --- /dev/null +++ b/apps/api/src/modules/groups/groups.service.spec.ts @@ -0,0 +1,33 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { GroupsService } from './groups.service'; +import { PrismaService } from '../../prisma/prisma.service'; + +const mockGroups = [ + { id: 'grp_1', name: 'Alpha', platform: 'whatsapp', platformId: '111@g.us', isActive: true, accountId: 'acc_1' }, + { id: 'grp_2', name: 'Beta', platform: 'whatsapp', platformId: '222@g.us', isActive: true, accountId: null }, +]; + +describe('GroupsService', () => { + let service: GroupsService; + const mockPrisma = { group: { findMany: jest.fn().mockResolvedValue(mockGroups) } }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + GroupsService, + { provide: PrismaService, useValue: mockPrisma }, + ], + }).compile(); + service = module.get(GroupsService); + }); + + it('returns all groups ordered by name', async () => { + const result = await service.list(); + expect(result).toHaveLength(2); + expect(result[0].name).toBe('Alpha'); + expect(mockPrisma.group.findMany).toHaveBeenCalledWith({ + orderBy: { name: 'asc' }, + select: { id: true, name: true, platform: true, platformId: true, isActive: true, accountId: true }, + }); + }); +}); diff --git a/apps/api/src/modules/groups/groups.service.ts b/apps/api/src/modules/groups/groups.service.ts new file mode 100644 index 0000000..4720430 --- /dev/null +++ b/apps/api/src/modules/groups/groups.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../../prisma/prisma.service'; + +@Injectable() +export class GroupsService { + constructor(private readonly prisma: PrismaService) {} + + list() { + return this.prisma.group.findMany({ + orderBy: { name: 'asc' }, + select: { id: true, name: true, platform: true, platformId: true, isActive: true, accountId: true }, + }); + } +}