"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ability_1 = require("@casl/ability");
const types_1 = require("../ee/serviceAccounts/types");
const projectMemberRole_1 = require("../types/projectMemberRole");
const projectMemberAbility_1 = require("./projectMemberAbility");
const scopeAbilityBuilder_1 = require("./scopeAbilityBuilder");
const serviceAccountAbility_1 = require("./serviceAccountAbility");
const ORG = 'org-1';
const SA_USER = 'sa-user-1';
const PROJ_A = 'proj-a';
const PROJ_B = 'proj-b';
const buildAbility = (scopes) => {
    const builder = new ability_1.AbilityBuilder(ability_1.Ability);
    (0, serviceAccountAbility_1.applyServiceAccountAbilities)({
        organizationUuid: ORG,
        userUuid: SA_USER,
        builder,
        scopes,
    });
    return builder.build();
};
// Mirrors UserModel.applyServiceAccountProjectMemberships:
// applies org SA scopes first, then per-project grants on the same builder.
const buildAbilityWithGrants = (scopes, grants) => {
    const builder = new ability_1.AbilityBuilder(ability_1.Ability);
    (0, serviceAccountAbility_1.applyServiceAccountAbilities)({
        organizationUuid: ORG,
        userUuid: SA_USER,
        builder,
        scopes,
    });
    for (const grant of grants) {
        if ('role' in grant) {
            projectMemberAbility_1.projectMemberAbilities[grant.role]({
                projectUuid: grant.projectUuid,
                userUuid: SA_USER,
                role: grant.role,
            }, builder);
        }
        else {
            (0, scopeAbilityBuilder_1.buildAbilityFromScopes)({
                projectUuid: grant.projectUuid,
                userUuid: SA_USER,
                scopes: grant.customRoleScopes,
                isEnterprise: true,
                permissionsConfig: {
                    pat: { enabled: true, allowedOrgRoles: [] },
                },
            }, builder);
        }
    }
    return builder.build();
};
describe('ServiceAccountScope.SYSTEM_MEMBER', () => {
    it('grants the same abilities as a human Member', () => {
        const ability = buildAbility([types_1.ServiceAccountScope.SYSTEM_MEMBER]);
        expect(ability.can('view', (0, ability_1.subject)('OrganizationMemberProfile', {
            organizationUuid: ORG,
        }))).toBe(true);
        expect(ability.can('view', (0, ability_1.subject)('PinnedItems', { organizationUuid: ORG }))).toBe(true);
    });
    it('grants no org-wide content abilities', () => {
        const ability = buildAbility([types_1.ServiceAccountScope.SYSTEM_MEMBER]);
        expect(ability.can('view', (0, ability_1.subject)('Dashboard', {
            organizationUuid: ORG,
            inheritsFromOrgOrProject: true,
        }))).toBe(false);
        expect(ability.can('view', (0, ability_1.subject)('Project', { organizationUuid: ORG }))).toBe(false);
    });
});
describe('SYSTEM_MEMBER + project_memberships (system roles)', () => {
    // Parity: SA with a system-role grant should view/manage in that project
    // the same way a human ProjectMember with that role does. Drives off
    // projectMemberAbilities so adding a 6th role would surface here.
    const expectations = [
        {
            role: projectMemberRole_1.ProjectMemberRole.VIEWER,
            canViewProject: true,
            canManageProject: false,
        },
        {
            role: projectMemberRole_1.ProjectMemberRole.INTERACTIVE_VIEWER,
            canViewProject: true,
            canManageProject: false,
        },
        {
            role: projectMemberRole_1.ProjectMemberRole.EDITOR,
            canViewProject: true,
            canManageProject: false,
        },
        {
            role: projectMemberRole_1.ProjectMemberRole.DEVELOPER,
            canViewProject: true,
            canManageProject: false,
        },
        {
            role: projectMemberRole_1.ProjectMemberRole.ADMIN,
            canViewProject: true,
            canManageProject: true,
        },
    ];
    it.each(expectations)('$role grants view=$canViewProject, manage=$canManageProject on the granted project only', ({ role, canViewProject, canManageProject }) => {
        const ability = buildAbilityWithGrants([types_1.ServiceAccountScope.SYSTEM_MEMBER], [{ projectUuid: PROJ_A, role }]);
        expect(ability.can('view', (0, ability_1.subject)('Project', {
            organizationUuid: ORG,
            projectUuid: PROJ_A,
        }))).toBe(canViewProject);
        expect(ability.can('manage', (0, ability_1.subject)('Project', {
            organizationUuid: ORG,
            projectUuid: PROJ_A,
        }))).toBe(canManageProject);
        // No bleed-through to an ungranted project.
        expect(ability.can('view', (0, ability_1.subject)('Project', {
            organizationUuid: ORG,
            projectUuid: PROJ_B,
        }))).toBe(false);
    });
    it('covers every key of projectMemberAbilities (drift sentinel)', () => {
        // If a new system role lands without a matching expectations row,
        // this assertion fails loudly — preventing silent gaps in coverage.
        expect(new Set(expectations.map((e) => e.role))).toEqual(new Set(Object.keys(projectMemberAbility_1.projectMemberAbilities)));
    });
});
describe('SYSTEM_MEMBER + project_memberships (custom roles)', () => {
    it('honors custom-role scopes on the granted project, not elsewhere', () => {
        const ability = buildAbilityWithGrants([types_1.ServiceAccountScope.SYSTEM_MEMBER], [
            {
                projectUuid: PROJ_A,
                customRoleScopes: ['view:Dashboard'],
            },
        ]);
        expect(ability.can('view', (0, ability_1.subject)('Dashboard', {
            organizationUuid: ORG,
            projectUuid: PROJ_A,
            inheritsFromOrgOrProject: true,
        }))).toBe(true);
        // Same action, different project → denied.
        expect(ability.can('view', (0, ability_1.subject)('Dashboard', {
            organizationUuid: ORG,
            projectUuid: PROJ_B,
            inheritsFromOrgOrProject: true,
        }))).toBe(false);
        // Scope NOT granted → denied even on the right project.
        expect(ability.can('manage', (0, ability_1.subject)('Dashboard', {
            organizationUuid: ORG,
            projectUuid: PROJ_A,
            inheritsFromOrgOrProject: true,
        }))).toBe(false);
    });
    it('mixes system + custom grants across projects on the same SA', () => {
        const ability = buildAbilityWithGrants([types_1.ServiceAccountScope.SYSTEM_MEMBER], [
            { projectUuid: PROJ_A, role: projectMemberRole_1.ProjectMemberRole.VIEWER },
            { projectUuid: PROJ_B, customRoleScopes: ['view:Dashboard'] },
        ]);
        // System Viewer on A: can view the project itself.
        expect(ability.can('view', (0, ability_1.subject)('Project', {
            organizationUuid: ORG,
            projectUuid: PROJ_A,
        }))).toBe(true);
        // Custom role on B lacks view:Project, so the Project subject is denied
        // even though Dashboard would be allowed.
        expect(ability.can('view', (0, ability_1.subject)('Project', {
            organizationUuid: ORG,
            projectUuid: PROJ_B,
        }))).toBe(false);
        expect(ability.can('view', (0, ability_1.subject)('Dashboard', {
            organizationUuid: ORG,
            projectUuid: PROJ_B,
            inheritsFromOrgOrProject: true,
        }))).toBe(true);
    });
    it('grants are additive — SYSTEM_MEMBER org abilities still apply', () => {
        // Project grant doesn't shadow the org-level OrganizationMemberProfile
        // view that SYSTEM_MEMBER itself provides.
        const ability = buildAbilityWithGrants([types_1.ServiceAccountScope.SYSTEM_MEMBER], [{ projectUuid: PROJ_A, role: projectMemberRole_1.ProjectMemberRole.VIEWER }]);
        expect(ability.can('view', (0, ability_1.subject)('OrganizationMemberProfile', {
            organizationUuid: ORG,
        }))).toBe(true);
    });
});
//# sourceMappingURL=serviceAccountAbility.test.js.map