test(260805-d0r): add failing tests for groups in getUserAccess
- Regression: user in a group without any module grant stays visible - Cross-tenant: membership in a foreign tenant's group is excluded - Origin (MANUAL/LDAP), empty-modules case, stable alpha sort
This commit is contained in:
@@ -15,6 +15,7 @@ function makeFakePrisma() {
|
||||
const groups = new Map<string, any>();
|
||||
const users = new Map<string, any>();
|
||||
const memberships = new Map<string, Set<string>>(); // groupId -> Set<userId>
|
||||
const membershipSources = new Map<string, string>(); // `${groupId}::${userId}` -> source
|
||||
const activations = new Map<string, any>(); // key: tenantId::moduleId
|
||||
const grants = new Map<string, any>();
|
||||
let grantCounter = 0;
|
||||
@@ -47,10 +48,11 @@ function makeFakePrisma() {
|
||||
__seedUser(user: { id: string; tenantId: string }) {
|
||||
users.set(user.id, user);
|
||||
},
|
||||
__seedMembership(groupId: string, userId: string) {
|
||||
__seedMembership(groupId: string, userId: string, source: string = 'MANUAL') {
|
||||
const set = memberships.get(groupId) ?? new Set<string>();
|
||||
set.add(userId);
|
||||
memberships.set(groupId, set);
|
||||
membershipSources.set(`${groupId}::${userId}`, source);
|
||||
},
|
||||
__seedActivation(a: {
|
||||
tenantId: string;
|
||||
@@ -145,6 +147,25 @@ function makeFakePrisma() {
|
||||
return { count };
|
||||
},
|
||||
},
|
||||
groupMembership: {
|
||||
findMany: async ({ where }: any) => {
|
||||
const userId = where.userId;
|
||||
const tenantId = where.group.tenantId;
|
||||
const rows: any[] = [];
|
||||
for (const [groupId, memberSet] of memberships.entries()) {
|
||||
if (!memberSet.has(userId)) continue;
|
||||
const group = groups.get(groupId);
|
||||
if (!group || group.tenantId !== tenantId) continue;
|
||||
rows.push({
|
||||
groupId,
|
||||
userId,
|
||||
source: membershipSources.get(`${groupId}::${userId}`) ?? 'MANUAL',
|
||||
group: { id: group.id, name: group.name },
|
||||
});
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -383,13 +404,14 @@ describe('ModuleGrantsService.getUserAccess', () => {
|
||||
|
||||
const result = await service.getUserAccess('t1', 'u1');
|
||||
|
||||
expect(result).toEqual([
|
||||
expect(result.modules).toEqual([
|
||||
{
|
||||
module: { id: 'mod-1', category: 'ops', name: 'Modul Eins' },
|
||||
viaGroups: ['Gruppe A'],
|
||||
direct: false,
|
||||
},
|
||||
]);
|
||||
expect(result.groups).toEqual([{ id: 'g1', name: 'Gruppe A', source: 'MANUAL' }]);
|
||||
});
|
||||
|
||||
it('adjacency: ein Direkt-Grant UND ein Gruppen-Grant auf dasselbe Modul erscheinen gleichzeitig, keiner verdrängt den anderen', async () => {
|
||||
@@ -402,8 +424,8 @@ describe('ModuleGrantsService.getUserAccess', () => {
|
||||
|
||||
const result = await service.getUserAccess('t1', 'u1');
|
||||
|
||||
expect(result[0].viaGroups).toEqual(['Gruppe A']);
|
||||
expect(result[0].direct).toBe(true);
|
||||
expect(result.modules[0].viaGroups).toEqual(['Gruppe A']);
|
||||
expect(result.modules[0].direct).toBe(true);
|
||||
});
|
||||
|
||||
it('wirft NotFoundException für eine userId aus einem anderen Mandanten', async () => {
|
||||
@@ -416,6 +438,71 @@ describe('ModuleGrantsService.getUserAccess', () => {
|
||||
NotFoundException,
|
||||
);
|
||||
});
|
||||
|
||||
it('REGRESSION: Mitglied einer Gruppe ohne Modul-Freigabe bleibt sichtbar', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
seedBase(prisma);
|
||||
prisma.__seedMembership('g1', 'u1');
|
||||
// Bewusst KEIN Grant für g1 auf mod-1.
|
||||
const service = new ModuleGrantsService(prisma as any);
|
||||
|
||||
const result = await service.getUserAccess('t1', 'u1');
|
||||
|
||||
expect(result.groups).toEqual([{ id: 'g1', name: 'Gruppe A', source: 'MANUAL' }]);
|
||||
expect(result.modules.every((m: any) => m.viaGroups.length === 0)).toBe(true);
|
||||
});
|
||||
|
||||
it('Cross-Tenant: eine Mitgliedschaft in einer Gruppe eines fremden Mandanten erscheint nicht in groups', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
seedBase(prisma);
|
||||
prisma.__seedGroup({ id: 'g-foreign', tenantId: 't2', name: 'Fremde Gruppe' });
|
||||
prisma.__seedMembership('g-foreign', 'u1');
|
||||
const service = new ModuleGrantsService(prisma as any);
|
||||
|
||||
const result = await service.getUserAccess('t1', 'u1');
|
||||
|
||||
expect(result.groups).toEqual([]);
|
||||
});
|
||||
|
||||
it('Herkunft: eine mit source LDAP geseedete Mitgliedschaft kommt mit source LDAP zurück', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
seedBase(prisma);
|
||||
prisma.__seedMembership('g1', 'u1', 'LDAP');
|
||||
const service = new ModuleGrantsService(prisma as any);
|
||||
|
||||
const result = await service.getUserAccess('t1', 'u1');
|
||||
|
||||
expect(result.groups).toEqual([{ id: 'g1', name: 'Gruppe A', source: 'LDAP' }]);
|
||||
});
|
||||
|
||||
it('ohne aktives Modul im Mandanten: modules ist leer, groups trotzdem befüllt', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
prisma.__seedGroup({ id: 'g1', tenantId: 't1', name: 'Gruppe A' });
|
||||
prisma.__seedUser({ id: 'u1', tenantId: 't1' });
|
||||
// keine Activation geseedet
|
||||
prisma.__seedMembership('g1', 'u1');
|
||||
const service = new ModuleGrantsService(prisma as any);
|
||||
|
||||
const result = await service.getUserAccess('t1', 'u1');
|
||||
|
||||
expect(result.modules).toEqual([]);
|
||||
expect(result.groups).toEqual([{ id: 'g1', name: 'Gruppe A', source: 'MANUAL' }]);
|
||||
});
|
||||
|
||||
it('Sortierung: groups ist alphabetisch nach name stabil über wiederholte Aufrufe', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
seedBase(prisma);
|
||||
prisma.__seedGroup({ id: 'g2', tenantId: 't1', name: 'Alpha' });
|
||||
prisma.__seedMembership('g1', 'u1');
|
||||
prisma.__seedMembership('g2', 'u1');
|
||||
const service = new ModuleGrantsService(prisma as any);
|
||||
|
||||
const first = await service.getUserAccess('t1', 'u1');
|
||||
const second = await service.getUserAccess('t1', 'u1');
|
||||
|
||||
expect(first.groups.map((g: any) => g.name)).toEqual(['Alpha', 'Gruppe A']);
|
||||
expect(second.groups.map((g: any) => g.name)).toEqual(['Alpha', 'Gruppe A']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ModuleGrantsService — Logging (D-23)', () => {
|
||||
|
||||
Reference in New Issue
Block a user