Files
upstream-rocketchat/ee/packages/license/__tests__/setLicense.spec.ts
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 2s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

207 lines
7.6 KiB
TypeScript

/**
* @jest-environment node
*/
import { LicenseImp } from '../src';
import { MockedLicenseBuilder, getReadyLicenseManager } from './MockedLicenseBuilder';
import { DuplicatedLicenseError } from '../src/errors/DuplicatedLicenseError';
import { InvalidLicenseError } from '../src/errors/InvalidLicenseError';
import { NotReadyForValidation } from '../src/errors/NotReadyForValidation';
// Same license used on ci tasks so no I didnt leak it
const VALID_LICENSE =
process.env.ENTERPRISE_LICENSE ||
'MK+bpK5NveUuNlWGaQXGoy+8b74Luet82M3ZGcBB8b5P9Y+m67NEtpW64dc1d5lEWi6d0nFjCjtCMneVD7bKxodz/Cml8URKEo5P7cQb/9wmeT0MzAhYNaRFZlIGkZ3ITF59pDV2u4HZuosEDJikVRwnaJ5ZoU/pOsHSPUPhTyGNIqLeKynODtUpfwDdIKEmHxpf2yVkKjgRiIJmbWjM6A4k+MNNYXWVXHzye7GggqWVg/ZcT7nKU1CCadpLhTJiIrgrrPzil1G5DQ4xnLs3Q2tu2dILSDiW5OYw/ywu2yCMicTjMq4MLL5SXDQJj6WoJzZ54HosbvsDzOXvsdC9gI1CjhPL2uRuvC8XLrzn3vL2UgXnifzD1VrLTtdZ+aSADveqtlzYlRWtqoUFBbNw8o+YVHdhbZGR0beMoAyRbHi5EMpxpad3L+NyztUIT/Uh/IjQ/C2SQZ6jB0GKPBOPxFLN56FNhTGrffLFR++TVoBu0Iquc7kajWkNit3bVbZvbx+oFcVW2PcjQ/+i2jpJjbgtUFUKrTKxGMAXTWoDzIQQ35zNzGAy268IM4Ymp5JmsVEnBOEUkbF9yx6fzkO6xZhpsHf0muklnW0kA+Tlore/TUrBWh1/RwWlQeZlxM5NyWoRM5onQmr/k/4BmObtL1Hpmbk8oMG29z89xtE9y/4=';
describe('License set license procedures', () => {
describe('Invalid formats', () => {
it('should have no license by default', async () => {
const license = new LicenseImp();
expect(license.hasValidLicense()).toBe(false);
expect(license.getLicense()).toBeUndefined();
});
it('should throw an error if the license applied is empty', async () => {
const license = new LicenseImp();
await expect(license.setLicense('')).rejects.toThrow(InvalidLicenseError);
});
it('should throw an error if the license applied is invalid', async () => {
const license = new LicenseImp();
await expect(license.setLicense('invalid')).rejects.toThrow(InvalidLicenseError);
});
});
describe('Invalid periods', () => {
it('should throw an error if the license is expired', async () => {
const license = await getReadyLicenseManager();
const mocked = new MockedLicenseBuilder();
const token = await mocked.withExpiredDate().sign();
await license.setLicense(token);
expect(license.hasValidLicense()).toBe(false);
});
describe('license that is not not started yet is applied', () => {
it('should throw an error if the license is not started yet', async () => {
const license = await getReadyLicenseManager();
const mocked = new MockedLicenseBuilder();
const token = await mocked.withNotStartedDate().sign();
await license.setLicense(token);
expect(license.hasValidLicense()).toBe(false);
});
it('should be allowed to set the same license again if the license is not started yet', async () => {
const license = await getReadyLicenseManager();
const mocked = new MockedLicenseBuilder();
const as = mocked.resetValidPeriods().withNotStartedDate();
const token = await as.sign();
await license.setLicense(token);
expect(license.hasValidLicense()).toBe(false);
// 5 minutes in the future
const mockedData = new Date();
mockedData.setMinutes(mockedData.getMinutes() + 5);
jest.useFakeTimers();
jest.setSystemTime(mockedData);
await license.setLicense(token);
jest.useRealTimers();
expect(license.hasValidLicense()).toBe(true);
});
});
});
it('should throw an error if the license is duplicated', async () => {
const license = await getReadyLicenseManager();
await expect(license.setLicense(VALID_LICENSE)).resolves.toBe(true);
await expect(license.setLicense(VALID_LICENSE)).rejects.toThrow(DuplicatedLicenseError);
});
it('should keep a valid license if a new invalid formatted license is applied', async () => {
const license = await getReadyLicenseManager();
await expect(license.setLicense(VALID_LICENSE)).resolves.toBe(true);
expect(license.hasValidLicense()).toBe(true);
await expect(license.setLicense('invalid')).rejects.toThrow(InvalidLicenseError);
expect(license.hasValidLicense()).toBe(true);
});
describe('Pending cases', () => {
it('should return an error if the license is not ready for validation yet - missing workspace url', async () => {
const license = new LicenseImp();
await expect(license.setLicense(VALID_LICENSE)).rejects.toThrow(NotReadyForValidation);
});
it('should return an error if the license is not ready for validation yet - missing counters', async () => {
const license = new LicenseImp();
await license.setWorkspaceUrl('http://localhost:3000');
expect(license.getWorkspaceUrl()).toBe('localhost:3000');
await expect(license.setLicense(VALID_LICENSE)).rejects.toThrow(NotReadyForValidation);
expect(license.hasValidLicense()).toBe(false);
});
it('should return a valid license if the license is ready for validation', async () => {
const license = await getReadyLicenseManager();
await expect(license.setLicense(VALID_LICENSE)).resolves.toBe(true);
expect(license.hasValidLicense()).toBe(true);
});
});
describe('License V3', () => {
it('should return a valid license if the license is ready for validation', async () => {
const license = await getReadyLicenseManager();
const token = await new MockedLicenseBuilder().sign();
await expect(license.setLicense(token)).resolves.toBe(true);
expect(license.hasValidLicense()).toBe(true);
});
it('should accept new licenses', async () => {
const license = await getReadyLicenseManager();
const mocked = new MockedLicenseBuilder();
const oldToken = await mocked.sign();
const newToken = await mocked.withGrantedModules(['livechat-enterprise', 'chat.rocket.test-addon']).sign();
await expect(license.setLicense(oldToken)).resolves.toBe(true);
expect(license.hasValidLicense()).toBe(true);
expect(license.hasModule('livechat-enterprise')).toBe(false);
expect(license.hasModule('chat.rocket.test-addon')).toBe(false);
await expect(license.setLicense(newToken)).resolves.toBe(true);
expect(license.hasValidLicense()).toBe(true);
expect(license.hasModule('livechat-enterprise')).toBe(true);
expect(license.hasModule('chat.rocket.test-addon')).toBe(true);
});
it('should call a validated event after set a valid license', async () => {
const license = await getReadyLicenseManager();
const validateCallback = jest.fn();
license.onValidateLicense(validateCallback);
await expect(license.setLicense(VALID_LICENSE)).resolves.toBe(true);
expect(license.hasValidLicense()).toBe(true);
expect(validateCallback).toHaveBeenCalledTimes(1);
});
describe('License limits', () => {
describe('invalidate license', () => {
it('should trigger an invalidation event when a license with invalid limits is set after a valid one', async () => {
const invalidationCallback = jest.fn();
const licenseManager = await getReadyLicenseManager();
const mocked = new MockedLicenseBuilder();
const oldToken = await mocked
.withLimits('activeUsers', [
{
max: 10,
behavior: 'invalidate_license',
},
])
.sign();
const newToken = await mocked
.withLimits('activeUsers', [
{
max: 1,
behavior: 'invalidate_license',
},
])
.sign();
licenseManager.onInvalidateLicense(invalidationCallback);
licenseManager.setLicenseLimitCounter('activeUsers', () => 5);
await expect(licenseManager.setLicense(oldToken)).resolves.toBe(true);
expect(licenseManager.hasValidLicense()).toBe(true);
await expect(licenseManager.setLicense(newToken)).resolves.toBe(true);
expect(licenseManager.hasValidLicense()).toBe(false);
expect(invalidationCallback).toHaveBeenCalledTimes(1);
});
});
});
});
});