From 08d86f4b3c3b1342fb638f53cef501b597bd84f0 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Mon, 28 Sep 2026 16:17:01 +0200 Subject: [PATCH] test(admin-role): lock member counts against OIDC role ids The member-counts endpoint ignores OIDC-backed role ids: the frozen legacy handler emitted NaN for them, failing response validation (see #2781). Pin the server-nestjs behaviour: OIDC ids excluded, finite values, and output compliant with the z.record(z.number().min(0)) contract. Refs #2781 Co-authored-by: Automata Signed-off-by: William Phetsinorath --- .../admin-role/admin-role.service.spec.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/server-nestjs/src/modules/admin-role/admin-role.service.spec.ts b/apps/server-nestjs/src/modules/admin-role/admin-role.service.spec.ts index 6fd63b4316..b2ee4dcd24 100644 --- a/apps/server-nestjs/src/modules/admin-role/admin-role.service.spec.ts +++ b/apps/server-nestjs/src/modules/admin-role/admin-role.service.spec.ts @@ -1,7 +1,9 @@ import type { EventEmitter2 } from '@nestjs/event-emitter' import type { PrismaService } from '../infrastructure/database/prisma.service' +import { adminRoleContract } from '@cpn-console/shared' import { beforeEach, describe, expect, it, vi } from 'vitest' import { mockDeep } from 'vitest-mock-extended' +import { makeUser } from '../project/project-testing.utils' import { makeAdminRole, makeCreateAdminRoleBody, @@ -92,4 +94,24 @@ describe('adminRoleService', () => { await expect(service.patch([patchedA, patchedB])).resolves.toBeDefined() expect(eventEmitter.emitAsync).toHaveBeenCalledTimes(2) }) + + it('counts members per managed role, ignoring OIDC role ids', async () => { + const managedRole = makeAdminRole({ oidcGroup: '' }) + const otherManagedRole = makeAdminRole({ oidcGroup: '' }) + const oidcRole = makeAdminRole({ oidcGroup: '/console/admin' }) + + prisma.adminRole.findMany.mockResolvedValue([managedRole, otherManagedRole]) + prisma.user.findMany.mockResolvedValue([ + makeUser({ adminRoleIds: [managedRole.id, oidcRole.id] }), + makeUser({ adminRoleIds: [managedRole.id, otherManagedRole.id] }), + ]) + prisma.$transaction.mockImplementation(async (cb: (tx: unknown) => unknown) => cb(prisma)) + + const service = new AdminRoleService(prisma, eventEmitter) + const counts = await service.memberCounts() + + expect(counts).toEqual({ [managedRole.id]: 2, [otherManagedRole.id]: 1 }) + expect(Object.values(counts).every(Number.isFinite)).toBe(true) + expect(adminRoleContract.adminRoleMemberCounts.responses[200].safeParse(counts).success).toBe(true) + }) })