From f2459a06c8c735db31a9137a276f22d1b01b026b Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Wed, 9 Sep 2026 16:20:19 +0200 Subject: [PATCH 1/3] test(server-nestjs): assert app events emit surface parity Co-authored-by: Automata --- .../modules/events/app-events.service.spec.ts | 12 ++++++++ .../modules/keycloak/keycloak.service.spec.ts | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/apps/server-nestjs/src/modules/events/app-events.service.spec.ts b/apps/server-nestjs/src/modules/events/app-events.service.spec.ts index 35fa8759c4..889cd6f0a7 100644 --- a/apps/server-nestjs/src/modules/events/app-events.service.spec.ts +++ b/apps/server-nestjs/src/modules/events/app-events.service.spec.ts @@ -175,4 +175,16 @@ describe('appEventsService', () => { data: expect.objectContaining({ args: payload }), })) }) + + describe('emit surface (migration parity)', () => { + const expectedEmitMethods = ['emitProjectEvent', 'emitProjectMemberEvent', 'emitRepositoryEvent'] as const + + it.each(expectedEmitMethods)('exposes %s as a public emit entrypoint', (method) => { + expect(typeof service[method]).toBe('function') + }) + + it('does NOT expose a zone emit entrypoint (zone.* listeners have no emitter yet — legacy owns the route)', () => { + expect('emitZoneEvent' in service).toBe(false) + }) + }) }) diff --git a/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts b/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts index b557a04ccd..cc61f35670 100644 --- a/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts +++ b/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts @@ -526,4 +526,34 @@ describe('keycloakService', () => { expect(keycloak.removeUserFromGroup).toHaveBeenCalledWith('user-2', 'system-managed-id') }) }) + + describe('migration parity: admin-role event path (legacy hook.adminRole.*)', () => { + const adminRoles: AdminRoleWithDetails[] = [ + { id: 'admin-role-1', name: 'admin', permissions: 0n, position: 0, oidcGroup: '/admin-group', type: 'managed' }, + ] + const users: UserWithAdminRoles[] = [ + { id: 'user-1', adminRoleIds: ['admin-role-1'] }, + ] + + it('syncs admin-role OIDC groups via cron reconcile ONLY (no event path exists)', async () => { + datastore.getAllProjects.mockResolvedValue([]) + datastore.getAllAdminRoles.mockResolvedValue(adminRoles) + datastore.getAllUsersWithAdminRoleIds.mockResolvedValue(users) + + const adminGroup = makeGroupRepresentation({ id: 'kc-parity-group-id', name: 'admin', path: '/console/parity-admin' }) + keycloak.getOrCreateGroupByPath.mockResolvedValue(adminGroup) + keycloak.getGroupMembers.mockResolvedValue([]) + + await service.handleCron() + + expect(keycloak.getOrCreateGroupByPath).toHaveBeenCalledWith('/admin-group') + expect(keycloak.addUserToGroup).toHaveBeenCalledWith('user-1', 'kc-parity-group-id') + }) + + it('exposes NO adminRole emit entrypoint on AppEventsService (cutover guard)', async () => { + const { AppEventsService } = await import('../events/app-events.service') + expect(Object.getOwnPropertyNames(AppEventsService.prototype)) + .not.toContain('emitAdminRoleEvent') + }) + }) }) From af9265fc6e43b73e046883327d47690eef8a21a6 Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Fri, 2 Oct 2026 10:39:49 +0200 Subject: [PATCH 2/3] test(server-nestjs): drop cutover guard forbidding emitAdminRoleEvent Co-authored-by: Automata --- .../src/modules/keycloak/keycloak.service.spec.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts b/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts index cc61f35670..c0a82d8dcf 100644 --- a/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts +++ b/apps/server-nestjs/src/modules/keycloak/keycloak.service.spec.ts @@ -549,11 +549,5 @@ describe('keycloakService', () => { expect(keycloak.getOrCreateGroupByPath).toHaveBeenCalledWith('/admin-group') expect(keycloak.addUserToGroup).toHaveBeenCalledWith('user-1', 'kc-parity-group-id') }) - - it('exposes NO adminRole emit entrypoint on AppEventsService (cutover guard)', async () => { - const { AppEventsService } = await import('../events/app-events.service') - expect(Object.getOwnPropertyNames(AppEventsService.prototype)) - .not.toContain('emitAdminRoleEvent') - }) }) }) From c593107122cc9944eca6b799a4095720d9d7384d Mon Sep 17 00:00:00 2001 From: William Phetsinorath Date: Fri, 2 Oct 2026 14:17:20 +0200 Subject: [PATCH 3/3] test(events): drop the emitZoneEvent absence guard superseded by the zone migration Co-authored-by: Automata --- .../src/modules/events/app-events.service.spec.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/server-nestjs/src/modules/events/app-events.service.spec.ts b/apps/server-nestjs/src/modules/events/app-events.service.spec.ts index 889cd6f0a7..d09c44c09b 100644 --- a/apps/server-nestjs/src/modules/events/app-events.service.spec.ts +++ b/apps/server-nestjs/src/modules/events/app-events.service.spec.ts @@ -183,8 +183,5 @@ describe('appEventsService', () => { expect(typeof service[method]).toBe('function') }) - it('does NOT expose a zone emit entrypoint (zone.* listeners have no emitter yet — legacy owns the route)', () => { - expect('emitZoneEvent' in service).toBe(false) - }) }) })