import { tap, expect } from '@git.zone/tstest/tapbundle'; import { AppConnection } from '../ts/reception/classes.appconnection.js'; import { AppConnectionManager } from '../ts/reception/classes.appconnectionmanager.js'; import { User } from '../ts/reception/classes.user.js'; const createTestAppConnectionManager = (optionsArg: { allowedScopes?: string[]; grantedScopes?: string[]; } = {}) => { const activities: Array<{ userId: string; action: string; description: string; metadata?: any }> = []; const alerts: Array<{ eventType: string; organizationId?: string; relatedEntityId?: string }> = []; const user = new User(); user.id = 'user-1'; user.data = { name: 'Admin User', username: 'admin@example.com', email: 'admin@example.com', status: 'active', connectedOrgs: ['org-1'], }; const app = { id: 'app-1', type: 'global', data: { name: 'Finance App', oauthCredentials: { allowedScopes: optionsArg.allowedScopes || ['openid', 'roles', 'billing'], }, }, }; const organization = { id: 'org-1', data: { name: 'Lossless GmbH', slug: 'lossless', }, checkIfUserIsAdmin: async () => true, }; const connection = new AppConnection(); connection.id = 'connection-1'; connection.data = { organizationId: organization.id, appId: app.id, appType: 'global', status: 'active', connectedAt: Date.now(), connectedByUserId: user.id, grantedScopes: optionsArg.grantedScopes || ['openid', 'roles', 'billing'], roleMappings: [], }; connection.save = async () => undefined; const reception = { db: { smartdataDb: {} }, typedrouter: { addTypedRouter: () => undefined }, organizationmanager: { COrganization: { getInstance: async () => organization, }, getAvailableRoleKeys: async () => ['owner', 'admin', 'viewer', 'finance'], validateRoleKey: (roleKeyArg: string) => roleKeyArg.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''), }, appManager: { getAppById: async () => app, }, activityLogManager: { logActivity: async (userId: string, action: string, description: string, metadata?: any) => { activities.push({ userId, action, description, metadata }); }, }, alertManager: { createAlertsForEvent: async (options: { eventType: string; organizationId?: string; relatedEntityId?: string }) => { alerts.push(options); return []; }, }, } as any; const manager = new AppConnectionManager(reception); (manager as any).CAppConnection = { getInstance: async () => connection, }; return { manager, user, connection, activities, alerts, }; }; tap.test('rejects app role mappings with unsupported app scopes', async () => { const { manager, user, connection, activities } = createTestAppConnectionManager({ allowedScopes: ['openid', 'roles'], grantedScopes: ['openid', 'roles', 'billing'], }); await expect(manager.updateAppRoleMappings({ user, organizationId: 'org-1', appId: 'app-1', roleMappings: [{ orgRoleKey: 'finance', appRoles: [], permissions: [], scopes: ['billing'], }], })).rejects.toThrow(); expect(connection.data.roleMappings).toEqual([]); expect(activities).toEqual([]); }); tap.test('rejects app role mappings with ungranted connection scopes', async () => { const { manager, user, connection, activities } = createTestAppConnectionManager({ allowedScopes: ['openid', 'roles', 'billing'], grantedScopes: ['openid', 'roles'], }); await expect(manager.updateAppRoleMappings({ user, organizationId: 'org-1', appId: 'app-1', roleMappings: [{ orgRoleKey: 'finance', appRoles: [], permissions: [], scopes: ['billing'], }], })).rejects.toThrow(); expect(connection.data.roleMappings).toEqual([]); expect(activities).toEqual([]); }); tap.test('updates app role mappings and writes audit activity', async () => { const { manager, user, connection, activities, alerts } = createTestAppConnectionManager(); await manager.updateAppRoleMappings({ user, organizationId: 'org-1', appId: 'app-1', roleMappings: [{ orgRoleKey: ' Finance ', appRoles: ['accountant', 'accountant', ''], permissions: ['invoices:read'], scopes: ['billing'], }], }); expect(connection.data.roleMappings).toEqual([{ orgRoleKey: 'finance', appRoles: ['accountant'], permissions: ['invoices:read'], scopes: ['billing'], }]); expect(activities[0].action).toEqual('org_app_role_mappings_updated'); expect(activities[0].metadata.targetId).toEqual(connection.id); expect(alerts[0].eventType).toEqual('org_app_role_mappings_updated'); }); export default tap.start();