Files
Benny JooandDevin AI ab21c7f805 refactor: Cal.diy (#28903)
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com

This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main:

- Rebrand Cal.com to Cal.diy across the entire codebase
- Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions
- Switch license from AGPL-3.0 to MIT
- Remove docs/ directory (migrated to Nextra at cal.diy)
- Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc.
- Clean up .env.example for self-hosted Cal.diy
- Update Docker image references to calcom/cal.diy
- Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork
- Add PR welcome bot for Cal.diy contributors
- Fix API v2 breaking changes oasdiff ignore entries
- Replace Blacksmith CI runners with default GitHub Actions

3893 files changed, 20789 insertions(+), 411020 deletions(-)

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* rip out org related comments in api v2

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-04-15 09:52:36 -03:00

781 lines
26 KiB
TypeScript

import type { IFeatureRepository } from "@calcom/features/flags/repositories/FeatureRepository";
import type { ITeamFeatureRepository } from "@calcom/features/flags/repositories/TeamFeatureRepository";
import type { IUserFeatureRepository } from "@calcom/features/flags/repositories/UserFeatureRepository";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { ErrorWithCode } from "@calcom/lib/errors";
import type { TeamFeatures, UserFeatures } from "@calcom/prisma/client";
import type { Mock } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { IFeatureOptInServiceDeps } from "./FeatureOptInService";
import { FeatureOptInService } from "./FeatureOptInService";
const mockIsFeatureAllowedForScope: Mock = vi.fn();
const mockFindAllByUserId: Mock = vi.fn();
const mockMembershipFindUnique: Mock = vi.fn();
const mockMembershipFindMany: Mock = vi.fn();
// Mock the OPT_IN_FEATURES config
vi.mock("../config", () => {
const mockFeatures = [
{
slug: "test-feature-1",
i18n: { title: "test_feature_1_title", name: "test_feature_1", description: "test_feature_1_desc" },
policy: "permissive",
},
{
slug: "test-feature-2",
i18n: { title: "test_feature_2_title", name: "test_feature_2", description: "test_feature_2_desc" },
policy: "permissive",
},
{
slug: "org-only-feature",
i18n: { title: "org_only_title", name: "org_only", description: "org_only_desc" },
policy: "permissive",
scope: ["org"],
},
{
slug: "team-only-feature",
i18n: { title: "team_only_title", name: "team_only", description: "team_only_desc" },
policy: "permissive",
scope: ["team"],
},
{
slug: "user-only-feature",
i18n: { title: "user_only_title", name: "user_only", description: "user_only_desc" },
policy: "permissive",
scope: ["user"],
},
{
slug: "strict-feature",
i18n: { title: "strict_feature_title", name: "strict_feature", description: "strict_feature_desc" },
policy: "strict",
},
];
return {
OPT_IN_FEATURES: mockFeatures,
isOptInFeature: (slug: string) => mockFeatures.some((feature) => feature.slug === slug),
getOptInFeaturesForScope: () => mockFeatures,
get isFeatureAllowedForScope(): Mock {
return mockIsFeatureAllowedForScope;
},
getOptInFeatureConfig: (slug: string) => mockFeatures.find((feature) => feature.slug === slug),
};
});
// Mock MembershipRepository
vi.mock("@calcom/features/membership/repositories/MembershipRepository", () => ({
MembershipRepository: class {
findAllByUserId(...args: unknown[]): unknown {
return mockFindAllByUserId(...args);
}
},
}));
// Mock PermissionCheckService - needs to be a class that can be instantiated
let mockCheckPermission: Mock = vi.fn();
vi.mock("@calcom/features/pbac/services/permission-check.service", () => ({
PermissionCheckService: class {
checkPermission(...args: unknown[]): unknown {
return mockCheckPermission(...args);
}
},
}));
let mockFindOwnedTeamsByUserId: Mock = vi.fn();
// Mock prisma
vi.mock("@calcom/prisma", () => ({
prisma: {
membership: {
findUnique: (...args: unknown[]) => mockMembershipFindUnique(...args),
findMany: (...args: unknown[]) => mockMembershipFindMany(...args),
},
},
}));
// Helper to create mock TeamFeatures
function createMockTeamFeature(teamId: number, featureId: string, enabled: boolean): TeamFeatures {
return {
teamId,
featureId,
enabled,
assignedBy: "test",
updatedAt: new Date(),
};
}
// Helper to create mock UserFeatures
function createMockUserFeature(userId: number, featureId: string, enabled: boolean): UserFeatures {
return {
userId,
featureId,
enabled,
assignedBy: "test",
updatedAt: new Date(),
};
}
describe("FeatureOptInService", () => {
let mockFeatureRepo: {
findAll: ReturnType<typeof vi.fn>;
};
let mockTeamFeatureRepo: {
findByTeamIdAndFeatureId: ReturnType<typeof vi.fn>;
findByTeamIdsAndFeatureIds: ReturnType<typeof vi.fn>;
upsert: ReturnType<typeof vi.fn>;
delete: ReturnType<typeof vi.fn>;
findAutoOptInByTeamId: ReturnType<typeof vi.fn>;
findAutoOptInByTeamIds: ReturnType<typeof vi.fn>;
setAutoOptIn: ReturnType<typeof vi.fn>;
};
let mockUserFeatureRepo: {
findByUserIdAndFeatureId: ReturnType<typeof vi.fn>;
findByUserIdAndFeatureIds: ReturnType<typeof vi.fn>;
upsert: ReturnType<typeof vi.fn>;
delete: ReturnType<typeof vi.fn>;
findAutoOptInByUserId: ReturnType<typeof vi.fn>;
setAutoOptIn: ReturnType<typeof vi.fn>;
};
let mockDeps: IFeatureOptInServiceDeps;
let service: FeatureOptInService;
beforeEach(() => {
vi.resetAllMocks();
mockIsFeatureAllowedForScope.mockReturnValue(true);
mockFeatureRepo = {
findAll: vi.fn(),
};
mockTeamFeatureRepo = {
findByTeamIdAndFeatureId: vi.fn(),
findByTeamIdsAndFeatureIds: vi.fn(),
upsert: vi.fn(),
delete: vi.fn(),
findAutoOptInByTeamId: vi.fn(),
findAutoOptInByTeamIds: vi.fn(),
setAutoOptIn: vi.fn(),
};
mockUserFeatureRepo = {
findByUserIdAndFeatureId: vi.fn(),
findByUserIdAndFeatureIds: vi.fn(),
upsert: vi.fn(),
delete: vi.fn(),
findAutoOptInByUserId: vi.fn(),
setAutoOptIn: vi.fn(),
};
mockDeps = {
featureRepo: mockFeatureRepo as unknown as IFeatureRepository,
teamFeatureRepo: mockTeamFeatureRepo as unknown as ITeamFeatureRepository,
userFeatureRepo: mockUserFeatureRepo as unknown as IUserFeatureRepository,
};
service = new FeatureOptInService(mockDeps);
});
describe("listFeaturesForTeam", () => {
it("should return features with team state when no parent org", async () => {
mockFeatureRepo.findAll.mockResolvedValue([
{ slug: "test-feature-1", enabled: true },
{ slug: "test-feature-2", enabled: true },
]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({
"test-feature-1": { 1: createMockTeamFeature(1, "test-feature-1", true) },
"test-feature-2": { 1: createMockTeamFeature(1, "test-feature-2", false) },
});
const result = await service.listFeaturesForTeam({ teamId: 1 });
expect(result).toHaveLength(2);
expect(result[0]).toEqual({
featureId: "test-feature-1",
globalEnabled: true,
teamState: "enabled",
orgState: "inherit",
});
expect(result[1]).toEqual({
featureId: "test-feature-2",
globalEnabled: true,
teamState: "disabled",
orgState: "inherit",
});
// Verify that only the team ID was queried (no parent org)
expect(mockTeamFeatureRepo.findByTeamIdsAndFeatureIds).toHaveBeenCalledWith(
[1],
[
"test-feature-1",
"test-feature-2",
"org-only-feature",
"team-only-feature",
"user-only-feature",
"strict-feature",
]
);
});
it("should return features with team and org state when parent org exists", async () => {
mockFeatureRepo.findAll.mockResolvedValue([
{ slug: "test-feature-1", enabled: true },
{ slug: "test-feature-2", enabled: true },
]);
// Team ID is 1, Parent Org ID is 100
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({
"test-feature-1": {
1: createMockTeamFeature(1, "test-feature-1", true),
100: createMockTeamFeature(100, "test-feature-1", false),
},
"test-feature-2": {
100: createMockTeamFeature(100, "test-feature-2", true),
},
});
const result = await service.listFeaturesForTeam({ teamId: 1, parentOrgId: 100 });
expect(result).toHaveLength(2);
expect(result[0]).toEqual({
featureId: "test-feature-1",
globalEnabled: true,
teamState: "enabled",
orgState: "disabled",
});
expect(result[1]).toEqual({
featureId: "test-feature-2",
globalEnabled: true,
teamState: "inherit",
orgState: "enabled",
});
// Verify that both team ID and parent org ID were queried
expect(mockTeamFeatureRepo.findByTeamIdsAndFeatureIds).toHaveBeenCalledWith(
[1, 100],
[
"test-feature-1",
"test-feature-2",
"org-only-feature",
"team-only-feature",
"user-only-feature",
"strict-feature",
]
);
});
it("should return inherit for org state when parent org has no explicit state", async () => {
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: true }]);
// Team ID is 1, Parent Org ID is 100, but org has no explicit state
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({
"test-feature-1": { 1: createMockTeamFeature(1, "test-feature-1", true) },
});
const result = await service.listFeaturesForTeam({ teamId: 1, parentOrgId: 100 });
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
featureId: "test-feature-1",
globalEnabled: true,
teamState: "enabled",
orgState: "inherit",
});
});
it("should filter out globally disabled features", async () => {
mockFeatureRepo.findAll.mockResolvedValue([
{ slug: "test-feature-1", enabled: true },
{ slug: "test-feature-2", enabled: false },
]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({
"test-feature-1": { 1: createMockTeamFeature(1, "test-feature-1", true) },
"test-feature-2": { 1: createMockTeamFeature(1, "test-feature-2", true) },
});
const result = await service.listFeaturesForTeam({ teamId: 1 });
expect(result).toHaveLength(1);
expect(result[0].featureId).toBe("test-feature-1");
});
it("should return inherit for team state when team has no explicit state", async () => {
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: true }]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({
"test-feature-1": {},
});
const result = await service.listFeaturesForTeam({ teamId: 1 });
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
featureId: "test-feature-1",
globalEnabled: true,
teamState: "inherit",
orgState: "inherit",
});
});
it("should handle null parentOrgId the same as undefined", async () => {
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: true }]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({
"test-feature-1": { 1: createMockTeamFeature(1, "test-feature-1", true) },
});
const result = await service.listFeaturesForTeam({ teamId: 1, parentOrgId: null });
expect(result).toHaveLength(1);
expect(result[0].orgState).toBe("inherit");
// Verify that only the team ID was queried (no parent org)
expect(mockTeamFeatureRepo.findByTeamIdsAndFeatureIds).toHaveBeenCalledWith(
[1],
[
"test-feature-1",
"test-feature-2",
"org-only-feature",
"team-only-feature",
"user-only-feature",
"strict-feature",
]
);
});
});
describe("setUserFeatureState", () => {
it("should set user feature state when scope allows", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(true);
mockUserFeatureRepo.upsert.mockResolvedValue(createMockUserFeature(1, "test-feature-1", true));
await service.setUserFeatureState({
userId: 1,
featureId: "test-feature-1",
state: "enabled",
assignedBy: 2,
});
expect(mockUserFeatureRepo.upsert).toHaveBeenCalledWith(1, "test-feature-1", true, "user-2");
});
it("should set user feature state to inherit when scope allows", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(true);
mockUserFeatureRepo.delete.mockResolvedValue(undefined);
await service.setUserFeatureState({
userId: 1,
featureId: "test-feature-1",
state: "inherit",
});
expect(mockUserFeatureRepo.delete).toHaveBeenCalledWith(1, "test-feature-1");
});
it("should throw ErrorWithCode when feature is not allowed at user scope", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(false);
await expect(
service.setUserFeatureState({
userId: 1,
featureId: "org-only-feature",
state: "enabled",
assignedBy: 2,
})
).rejects.toThrow(ErrorWithCode);
await expect(
service.setUserFeatureState({
userId: 1,
featureId: "org-only-feature",
state: "enabled",
assignedBy: 2,
})
).rejects.toMatchObject({
code: ErrorCode.BadRequest,
message: 'Feature "org-only-feature" is not available at the user scope',
});
expect(mockUserFeatureRepo.upsert).not.toHaveBeenCalled();
});
it("should validate scope before setting inherit state", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(false);
await expect(
service.setUserFeatureState({
userId: 1,
featureId: "team-only-feature",
state: "inherit",
})
).rejects.toThrow(ErrorWithCode);
expect(mockUserFeatureRepo.delete).not.toHaveBeenCalled();
});
});
describe("setTeamFeatureState", () => {
it("should set team feature state when scope allows", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(true);
mockTeamFeatureRepo.upsert.mockResolvedValue(createMockTeamFeature(1, "test-feature-1", true));
await service.setTeamFeatureState({
teamId: 1,
featureId: "test-feature-1",
state: "enabled",
assignedBy: 2,
scope: "team",
});
expect(mockTeamFeatureRepo.upsert).toHaveBeenCalledWith(1, "test-feature-1", true, "user-2");
});
it("should set team feature state to inherit when scope allows", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(true);
mockTeamFeatureRepo.delete.mockResolvedValue(undefined);
await service.setTeamFeatureState({
teamId: 1,
featureId: "test-feature-1",
state: "inherit",
scope: "team",
});
expect(mockTeamFeatureRepo.delete).toHaveBeenCalledWith(1, "test-feature-1");
});
it("should throw ErrorWithCode when feature is not allowed at team scope", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(false);
await expect(
service.setTeamFeatureState({
teamId: 1,
featureId: "user-only-feature",
state: "enabled",
assignedBy: 2,
scope: "team",
})
).rejects.toThrow(ErrorWithCode);
await expect(
service.setTeamFeatureState({
teamId: 1,
featureId: "user-only-feature",
state: "enabled",
assignedBy: 2,
scope: "team",
})
).rejects.toMatchObject({
code: ErrorCode.BadRequest,
message: 'Feature "user-only-feature" is not available at the team scope',
});
expect(mockTeamFeatureRepo.upsert).not.toHaveBeenCalled();
});
it("should throw ErrorWithCode when feature is not allowed at org scope", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(false);
await expect(
service.setTeamFeatureState({
teamId: 100,
featureId: "team-only-feature",
state: "enabled",
assignedBy: 2,
scope: "org",
})
).rejects.toThrow(ErrorWithCode);
await expect(
service.setTeamFeatureState({
teamId: 100,
featureId: "team-only-feature",
state: "enabled",
assignedBy: 2,
scope: "org",
})
).rejects.toMatchObject({
code: ErrorCode.BadRequest,
message: 'Feature "team-only-feature" is not available at the org scope',
});
expect(mockTeamFeatureRepo.upsert).not.toHaveBeenCalled();
});
it("should default to team scope when scope is not provided", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(true);
mockTeamFeatureRepo.upsert.mockResolvedValue(createMockTeamFeature(1, "test-feature-1", true));
await service.setTeamFeatureState({
teamId: 1,
featureId: "test-feature-1",
state: "enabled",
assignedBy: 2,
});
expect(mockIsFeatureAllowedForScope).toHaveBeenCalledWith("test-feature-1", "team");
});
it("should validate scope before setting inherit state", async () => {
mockIsFeatureAllowedForScope.mockReturnValue(false);
await expect(
service.setTeamFeatureState({
teamId: 1,
featureId: "user-only-feature",
state: "inherit",
scope: "team",
})
).rejects.toThrow(ErrorWithCode);
expect(mockTeamFeatureRepo.delete).not.toHaveBeenCalled();
});
});
describe("checkFeatureOptInEligibility", () => {
beforeEach(async () => {
vi.resetAllMocks();
mockFindAllByUserId.mockReset();
mockCheckPermission = vi.fn();
mockFindOwnedTeamsByUserId = vi.fn();
mockMembershipFindUnique.mockResolvedValue(null);
mockMembershipFindMany.mockResolvedValue([]);
mockFeatureRepo = {
findAll: vi.fn(),
};
mockTeamFeatureRepo = {
findByTeamIdAndFeatureId: vi.fn(),
findByTeamIdsAndFeatureIds: vi.fn().mockResolvedValue({}),
upsert: vi.fn(),
delete: vi.fn(),
findAutoOptInByTeamId: vi.fn(),
findAutoOptInByTeamIds: vi.fn().mockResolvedValue({}),
setAutoOptIn: vi.fn(),
};
mockUserFeatureRepo = {
findByUserIdAndFeatureId: vi.fn(),
findByUserIdAndFeatureIds: vi.fn().mockResolvedValue({}),
upsert: vi.fn(),
delete: vi.fn(),
findAutoOptInByUserId: vi.fn().mockResolvedValue(false),
setAutoOptIn: vi.fn(),
};
mockDeps = {
featureRepo: mockFeatureRepo as unknown as IFeatureRepository,
teamFeatureRepo: mockTeamFeatureRepo as unknown as ITeamFeatureRepository,
userFeatureRepo: mockUserFeatureRepo as unknown as IUserFeatureRepository,
};
service = new FeatureOptInService(mockDeps);
});
it("should return invalid_feature for non-opt-in features", async () => {
const result = await service.checkFeatureOptInEligibility({
userId: 1,
featureId: "non-existent-feature",
});
expect(result).toEqual({
status: "invalid_feature",
canOptIn: false,
userRoleContext: null,
blockingReason: null,
});
});
it("should return feature_disabled when feature is globally disabled", async () => {
mockFindAllByUserId.mockResolvedValue([]);
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: false }]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({});
const result = await service.checkFeatureOptInEligibility({
userId: 1,
featureId: "test-feature-1",
});
expect(result).toEqual({
status: "feature_disabled",
canOptIn: false,
userRoleContext: null,
blockingReason: "feature_global_disabled",
});
});
it("should return already_enabled when feature is already enabled for user", async () => {
mockFindAllByUserId.mockResolvedValue([]);
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: true }]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({});
mockUserFeatureRepo.findByUserIdAndFeatureIds.mockResolvedValue({
"test-feature-1": createMockUserFeature(1, "test-feature-1", true),
});
mockFindOwnedTeamsByUserId.mockResolvedValue([]);
const result = await service.checkFeatureOptInEligibility({
userId: 1,
featureId: "test-feature-1",
});
expect(result).toEqual({
status: "already_enabled",
canOptIn: false,
userRoleContext: null,
blockingReason: null,
});
});
it("should return blocked when feature is blocked by org", async () => {
mockFindAllByUserId.mockResolvedValue([
{ teamId: 100, role: "MEMBER", team: { id: 100, isOrganization: true, parentId: null } },
]);
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: true }]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({
"test-feature-1": { 100: createMockTeamFeature(100, "test-feature-1", false) },
});
mockFindOwnedTeamsByUserId.mockResolvedValue([]);
const result = await service.checkFeatureOptInEligibility({
userId: 1,
featureId: "test-feature-1",
});
expect(result.status).toBe("blocked");
expect(result.canOptIn).toBe(false);
expect(result.blockingReason).toBe("feature_org_disabled");
expect(result.userRoleContext).toMatchObject({
orgId: 100,
adminTeamIds: [],
adminTeamNames: [],
});
});
it("should return can_opt_in when user can opt in", async () => {
mockFindAllByUserId.mockResolvedValue([
{ teamId: 1, role: "MEMBER", team: { id: 1, isOrganization: false, parentId: null } },
]);
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: true }]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({});
mockFindOwnedTeamsByUserId.mockResolvedValue([]);
const result = await service.checkFeatureOptInEligibility({
userId: 1,
featureId: "test-feature-1",
});
expect(result.status).toBe("can_opt_in");
expect(result.canOptIn).toBe(true);
expect(result.userRoleContext).toMatchObject({
orgId: null,
adminTeamIds: [],
adminTeamNames: [],
});
expect(result.blockingReason).toBeNull();
});
it("should include admin teams in user role context", async () => {
mockFindAllByUserId.mockResolvedValue([
{ teamId: 1, role: "ADMIN", team: { id: 1, isOrganization: false, parentId: null } },
{ teamId: 2, role: "MEMBER", team: { id: 2, isOrganization: false, parentId: null } },
]);
mockFeatureRepo.findAll.mockResolvedValue([{ slug: "test-feature-1", enabled: true }]);
mockTeamFeatureRepo.findByTeamIdsAndFeatureIds.mockResolvedValue({});
mockFindOwnedTeamsByUserId.mockResolvedValue([{ id: 1, name: "Team 1", isOrganization: false }]);
mockMembershipFindMany.mockResolvedValue([{ team: { id: 1, name: "Team 1", isOrganization: false } }]);
const result = await service.checkFeatureOptInEligibility({
userId: 1,
featureId: "test-feature-1",
});
expect(result.status).toBe("can_opt_in");
expect(result.userRoleContext?.adminTeamIds).toEqual([1]);
expect(result.userRoleContext?.adminTeamNames).toEqual([{ id: 1, name: "Team 1" }]);
});
});
describe("checkFeatureOptInEligibility - simulation logic", () => {
let mockMembershipRepository: { findAllByUserId: ReturnType<typeof vi.fn> };
let mockPermissionCheckService: { checkPermission: ReturnType<typeof vi.fn> };
beforeEach(() => {
mockMembershipRepository = {
findAllByUserId: vi.fn(),
};
mockPermissionCheckService = {
checkPermission: vi.fn(),
};
vi.doMock("@calcom/features/membership/repositories/MembershipRepository", () => ({
MembershipRepository: class {
findAllByUserId(...args: unknown[]): unknown {
return mockMembershipRepository.findAllByUserId(...args);
}
},
}));
vi.doMock("@calcom/features/pbac/services/permission-check.service", () => ({
PermissionCheckService: vi.fn(() => mockPermissionCheckService),
}));
});
it("should return blocked when strict policy feature would not be enabled even after user opts in", async () => {
const { computeEffectiveStateAcrossTeams } = await import("../lib/computeEffectiveState");
const result = computeEffectiveStateAcrossTeams({
globalEnabled: true,
orgState: "inherit",
teamStates: ["inherit"],
userState: "enabled",
policy: "strict",
});
expect(result.enabled).toBe(false);
expect(result.reason).toBe("feature_user_only_not_allowed");
});
it("should return enabled when permissive policy feature would be enabled after user opts in", async () => {
const { computeEffectiveStateAcrossTeams } = await import("../lib/computeEffectiveState");
const result = computeEffectiveStateAcrossTeams({
globalEnabled: true,
orgState: "inherit",
teamStates: ["inherit"],
userState: "enabled",
policy: "permissive",
});
expect(result.enabled).toBe(true);
expect(result.reason).toBe("feature_enabled");
});
it("should return enabled when strict policy feature has org enablement and user opts in", async () => {
const { computeEffectiveStateAcrossTeams } = await import("../lib/computeEffectiveState");
const result = computeEffectiveStateAcrossTeams({
globalEnabled: true,
orgState: "enabled",
teamStates: ["inherit"],
userState: "enabled",
policy: "strict",
});
expect(result.enabled).toBe(true);
expect(result.reason).toBe("feature_enabled");
});
it("should return blocked when strict policy feature has any team disabled", async () => {
const { computeEffectiveStateAcrossTeams } = await import("../lib/computeEffectiveState");
const result = computeEffectiveStateAcrossTeams({
globalEnabled: true,
orgState: "enabled",
teamStates: ["disabled", "inherit"],
userState: "enabled",
policy: "strict",
});
expect(result.enabled).toBe(false);
expect(result.reason).toBe("feature_any_team_disabled");
});
});
});