ToolJet/server/test/ee/organization-env/registry/git-env.spec.ts
Shantanu Mane d91137edb3
feat: Workspace Env Config (#15593)
* feat: add environment configuration support for Git providers and enhance git sync functionality

* feat: update Git environment constants and integrate OrganizationGitSyncRepository

* feat: add testProviderConnection method to gitSyncService for testing Git provider connections

* chore: remove scanning on module init

* feat: integrate OrganizationEnvRegistryService and reload envConfig on workspace create and slug update

* feat: eload env config on onboarding

* feat: add testProviderConnection method and update LicenseBase for envGitMapping

* fix: handle downgrade path for env registry

* fix flaky case in granular access

* uncommented changes

* feat: update .gitignore to include additional environment files

* feat: refactor organization environment handling and remove deprecated service

* feat: implement Git environment registry service and refactor organization environment handling

* feat: rename envGitMapping to workspaceEnv across licensing module

* chore: remove unnecessary try-catch

* refactor(org-env): update interfaces — remove callback contract, add ensureResolved

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: remove reload-on-org-event, swap bootstrap init order

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(org-env): add getResolvedOrganizationIds() to interface, CE stub, and test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: improve assertions in getResolvedOrganizationIds() test

* feat: refactor GitEnvRegistryService import paths and add new service implementation

* feat: add applyLicenseToResolvedOrgs method to IGitEnvRegistryService and GitEnvRegistryService

* refactor: replace GitEnvRegistryService with GitSyncEnvUtilService and update related references

* feat: add use_env_config column to organization_git_sync and remove env_git_provider column

* refactor: remove EncryptionModule import and update OrganizationEnvModule imports

* feat: introduce OrganizationEnvUtilService and update OrganizationEnvModule to utilize it

---------

Co-authored-by: Yukti Goyal <yuktigoyal02@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:14:46 +05:30

234 lines
8.1 KiB
TypeScript

jest.mock('@modules/organizations/repository', () => ({
OrganizationRepository: jest.fn(),
}));
import { GitSyncEnvUtilService } from '@ee/organization-env/services/gitsync.util.service';
import { GIT_ENV_KEYS } from '@modules/organization-env/constants';
import { GITConnectionType } from 'src/entities/organization_git_sync.entity';
const ORG_ID = '11111111-1111-1111-1111-111111111111';
function makeOrgEnvService(overrides: Partial<Record<string, jest.Mock>> = {}) {
return {
getResolvedOrganizationIds: jest.fn().mockReturnValue([]),
has: jest.fn().mockReturnValue(false),
hasAll: jest.fn().mockReturnValue(false),
get: jest.fn().mockResolvedValue(undefined),
ensureResolved: jest.fn().mockResolvedValue(undefined),
...overrides,
};
}
function makeService(orgEnvService: ReturnType<typeof makeOrgEnvService>) {
const orgGitSyncRepo = {
find: jest.fn().mockResolvedValue([]),
findOrgGitByOrganizationId: jest.fn().mockResolvedValue(null),
update: jest.fn().mockResolvedValue(undefined),
create: jest.fn().mockReturnValue({}),
save: jest.fn().mockResolvedValue(undefined),
};
const logger = {
log: jest.fn(), warn: jest.fn(), error: jest.fn(), debug: jest.fn(),
};
const licenseTerms = {
getLicenseTerms: jest.fn().mockResolvedValue(true),
};
return new GitSyncEnvUtilService(
orgEnvService as any,
orgGitSyncRepo as any,
logger as any,
licenseTerms as any,
);
}
function makeServiceWithMocks(
orgEnvService: ReturnType<typeof makeOrgEnvService>,
licenseResult = true,
) {
const orgGitSyncRepo = {
find: jest.fn().mockResolvedValue([]),
findOrgGitByOrganizationId: jest.fn().mockResolvedValue(null),
update: jest.fn().mockResolvedValue(undefined),
create: jest.fn().mockReturnValue({}),
save: jest.fn().mockResolvedValue(undefined),
};
const logger = {
log: jest.fn(), warn: jest.fn(), error: jest.fn(), debug: jest.fn(),
};
const licenseTerms = {
getLicenseTerms: jest.fn().mockResolvedValue(licenseResult),
};
const service = new GitSyncEnvUtilService(
orgEnvService as any,
orgGitSyncRepo as any,
logger as any,
licenseTerms as any,
);
return { service, orgGitSyncRepo, licenseTerms };
}
describe('GitSyncEnvUtilService', () => {
describe('initialize() — proactive provider state from env store', () => {
it('sets HTTPS provider isEnabled=true when at least one HTTPS key is mapped', async () => {
const orgEnvService = makeOrgEnvService({
getResolvedOrganizationIds: jest.fn().mockReturnValue([ORG_ID]),
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.HTTPS.URL,
),
});
const service = makeService(orgEnvService);
await service.initialize();
expect(service.getProviderState(ORG_ID, GITConnectionType.GITHUB_HTTPS)).toEqual({
isEnabled: true,
isFinalized: false,
});
});
it('sets SSH provider isEnabled=true independently when SSH key is mapped', async () => {
const orgEnvService = makeOrgEnvService({
getResolvedOrganizationIds: jest.fn().mockReturnValue([ORG_ID]),
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.SSH.GIT_URL,
),
});
const service = makeService(orgEnvService);
await service.initialize();
expect(service.getProviderState(ORG_ID, GITConnectionType.GITHUB_SSH)).toEqual({
isEnabled: true,
isFinalized: false,
});
expect(service.getProviderState(ORG_ID, GITConnectionType.GITHUB_HTTPS)).toEqual({
isEnabled: false,
isFinalized: false,
});
});
it('sets GitLab provider isEnabled=true when GitLab key is mapped', async () => {
const orgEnvService = makeOrgEnvService({
getResolvedOrganizationIds: jest.fn().mockReturnValue([ORG_ID]),
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.GITLAB.URL,
),
});
const service = makeService(orgEnvService);
await service.initialize();
expect(service.getProviderState(ORG_ID, GITConnectionType.GITLAB)).toEqual({
isEnabled: true,
isFinalized: false,
});
});
it('leaves provider disabled when no keys are mapped for that provider', async () => {
const orgEnvService = makeOrgEnvService({
getResolvedOrganizationIds: jest.fn().mockReturnValue([ORG_ID]),
has: jest.fn().mockReturnValue(false),
});
const service = makeService(orgEnvService);
await service.initialize();
expect(service.getProviderState(ORG_ID, GITConnectionType.GITHUB_HTTPS)).toEqual({
isEnabled: false,
isFinalized: false,
});
});
it('does not call has() when no resolved orgs exist', async () => {
const orgEnvService = makeOrgEnvService({
getResolvedOrganizationIds: jest.fn().mockReturnValue([]),
has: jest.fn().mockReturnValue(true),
});
const service = makeService(orgEnvService);
await service.initialize();
expect(orgEnvService.has).not.toHaveBeenCalled();
});
it('sets provider isEnabled=false when license is not valid', async () => {
const orgEnvService = makeOrgEnvService({
getResolvedOrganizationIds: jest.fn().mockReturnValue([ORG_ID]),
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.HTTPS.URL,
),
});
const { service } = makeServiceWithMocks(orgEnvService, false);
await service.initialize();
expect(service.getProviderState(ORG_ID, GITConnectionType.GITHUB_HTTPS)).toEqual({
isEnabled: false,
isFinalized: false,
});
});
it('does not call hydrateUseEnvConfig when license is not valid', async () => {
const orgEnvService = makeOrgEnvService({
getResolvedOrganizationIds: jest.fn().mockReturnValue([ORG_ID]),
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.HTTPS.URL,
),
});
const { service, orgGitSyncRepo } = makeServiceWithMocks(orgEnvService, false);
await service.initialize();
// hydrateUseEnvConfig calls save (create path) or update — neither should be called
expect(orgGitSyncRepo.save).not.toHaveBeenCalled();
expect(orgGitSyncRepo.update).not.toHaveBeenCalled();
});
});
describe('ensureResolved() — license-aware state + dedup', () => {
it('sets provider isEnabled=false when license is not valid', async () => {
const orgEnvService = makeOrgEnvService({
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.HTTPS.URL,
),
});
const { service } = makeServiceWithMocks(orgEnvService, false);
await service.ensureResolved(ORG_ID);
expect(service.getProviderState(ORG_ID, GITConnectionType.GITHUB_HTTPS)).toEqual({
isEnabled: false,
isFinalized: false,
});
});
it('does not call hydrateUseEnvConfig when license is not valid', async () => {
const orgEnvService = makeOrgEnvService({
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.HTTPS.URL,
),
});
const { service, orgGitSyncRepo } = makeServiceWithMocks(orgEnvService, false);
await service.ensureResolved(ORG_ID);
expect(orgGitSyncRepo.save).not.toHaveBeenCalled();
expect(orgGitSyncRepo.update).not.toHaveBeenCalled();
});
it('runs license check only once across multiple ensureResolved calls (dedup)', async () => {
const orgEnvService = makeOrgEnvService({
has: jest.fn().mockImplementation((_id: string, key: string) =>
key === GIT_ENV_KEYS.HTTPS.URL,
),
});
const { service, licenseTerms } = makeServiceWithMocks(orgEnvService, false);
await service.ensureResolved(ORG_ID);
await service.ensureResolved(ORG_ID);
// getLicenseTerms called twice per check (WORKSPACE_ENV + VALID), but only on first call
expect(licenseTerms.getLicenseTerms).toHaveBeenCalledTimes(2);
});
});
});