Reduce license plugin api (#53489)

* inOneOf --> hasAtLeast. to follow to licensing hierarchical model

* adopt licensing tests

* add license mock and use it in the tests

* adopt security plugin to hasAtLeast and licensing mocks

* adopt uptime to hasAtLeast

* update readme

* add test for unknown license

* fix import in js test

* fix security plugin merge conflict

* Update x-pack/plugins/security/common/licensing/license_service.ts

Co-Authored-By: Larry Gregory <lgregorydev@gmail.com>

* Update x-pack/plugins/licensing/common/types.ts

Co-Authored-By: Josh Dover <me@joshdover.com>

* simplify tests

* remove unused import
This commit is contained in:
Mikhail Shustov 2019-12-19 09:35:09 +01:00 committed by GitHub
parent 745188b378
commit ba7589d603
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 148 additions and 173 deletions

View file

@ -8,7 +8,7 @@ import { ILicense } from '../../../../../../../plugins/licensing/server';
import { licenseCheck } from '../license';
describe('license check', () => {
let mockLicense: Pick<ILicense, 'isActive' | 'isOneOf'>;
let mockLicense: Pick<ILicense, 'isActive' | 'hasAtLeast'>;
it('throws for null license', () => {
expect(licenseCheck(undefined)).toMatchSnapshot();
@ -16,7 +16,7 @@ describe('license check', () => {
it('throws for unsupported license type', () => {
mockLicense = {
isOneOf: jest.fn().mockReturnValue(false),
hasAtLeast: jest.fn().mockReturnValue(false),
isActive: false,
};
expect(licenseCheck(mockLicense)).toMatchSnapshot();
@ -24,7 +24,7 @@ describe('license check', () => {
it('throws for inactive license', () => {
mockLicense = {
isOneOf: jest.fn().mockReturnValue(true),
hasAtLeast: jest.fn().mockReturnValue(true),
isActive: false,
};
expect(licenseCheck(mockLicense)).toMatchSnapshot();
@ -32,7 +32,7 @@ describe('license check', () => {
it('returns result for a valid license', () => {
mockLicense = {
isOneOf: jest.fn().mockReturnValue(true),
hasAtLeast: jest.fn().mockReturnValue(true),
isActive: true,
};
expect(licenseCheck(mockLicense)).toMatchSnapshot();

View file

@ -11,7 +11,7 @@ export interface UMLicenseStatusResponse {
message: string;
}
export type UMLicenseCheck = (
license?: Pick<ILicense, 'isActive' | 'isOneOf'>
license?: Pick<ILicense, 'isActive' | 'hasAtLeast'>
) => UMLicenseStatusResponse;
export const licenseCheck: UMLicenseCheck = license => {
@ -21,7 +21,7 @@ export const licenseCheck: UMLicenseCheck = license => {
statusCode: 400,
};
}
if (!license.isOneOf(['basic', 'standard', 'gold', 'platinum', 'enterprise', 'trial'])) {
if (!license.hasAtLeast('basic')) {
return {
message: 'License not supported',
statusCode: 401,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { licensingMock } from '../../../../../plugins/licensing/server/licensing.mock';
import { licensingMock } from '../../../../../plugins/licensing/server/mocks';
import { XPackInfoLicense } from './xpack_info_license';
function getXPackInfoLicense(getRawLicense) {

View file

@ -56,7 +56,7 @@ chrome.navLinks.update('myPlugin', {
"requiredPlugins": ["licensing"],
// my_plugin/server/plugin.ts
import { LicensingPluginSetup, LICENSE_CHECK_STATE } from '../licensing'
import { LicensingPluginSetup, LICENSE_CHECK_STATE } from '../licensing/server'
interface SetupDeps {
licensing: LicensingPluginSetup;
@ -77,7 +77,8 @@ class MyPlugin {
}
}
// my_plugin/client/plugin.ts
// my_plugin/public/plugin.ts
import { LicensingPluginSetup, LICENSE_CHECK_STATE } from '../licensing/public'
class MyPlugin {
setup(core: CoreSetup, deps: SetupDeps) {
deps.licensing.license$.subscribe(license => {

View file

@ -9,10 +9,10 @@ import { LICENSE_CHECK_STATE } from './types';
import { licenseMock } from './licensing.mock';
describe('License', () => {
const basicLicense = licenseMock.create();
const basicExpiredLicense = licenseMock.create({ license: { status: 'expired' } });
const goldLicense = licenseMock.create({ license: { type: 'gold' } });
const enterpriseLicense = licenseMock.create({ license: { type: 'enterprise' } });
const basicLicense = licenseMock.createLicense();
const basicExpiredLicense = licenseMock.createLicense({ license: { status: 'expired' } });
const goldLicense = licenseMock.createLicense({ license: { type: 'gold' } });
const enterpriseLicense = licenseMock.createLicense({ license: { type: 'enterprise' } });
const errorMessage = 'unavailable';
const errorLicense = new License({ error: errorMessage, signature: '' });
@ -50,34 +50,23 @@ describe('License', () => {
expect(unavailableLicense.isActive).toBe(false);
});
it('isBasic', () => {
expect(basicLicense.isBasic).toBe(true);
expect(goldLicense.isBasic).toBe(false);
expect(enterpriseLicense.isBasic).toBe(false);
expect(errorLicense.isBasic).toBe(false);
expect(unavailableLicense.isBasic).toBe(false);
});
it('hasAtLeast', () => {
expect(basicLicense.hasAtLeast('platinum')).toBe(false);
expect(basicLicense.hasAtLeast('gold')).toBe(false);
expect(basicLicense.hasAtLeast('basic')).toBe(true);
it('isNotBasic', () => {
expect(basicLicense.isNotBasic).toBe(false);
expect(goldLicense.isNotBasic).toBe(true);
expect(enterpriseLicense.isNotBasic).toBe(true);
expect(errorLicense.isNotBasic).toBe(false);
expect(unavailableLicense.isNotBasic).toBe(false);
});
expect(errorLicense.hasAtLeast('basic')).toBe(false);
it('isOneOf', () => {
expect(basicLicense.isOneOf('platinum')).toBe(false);
expect(basicLicense.isOneOf(['platinum'])).toBe(false);
expect(basicLicense.isOneOf(['gold', 'platinum'])).toBe(false);
expect(basicLicense.isOneOf(['platinum', 'gold'])).toBe(false);
expect(basicLicense.isOneOf(['basic', 'gold'])).toBe(true);
expect(basicLicense.isOneOf(['basic'])).toBe(true);
expect(basicLicense.isOneOf('basic')).toBe(true);
expect(unavailableLicense.hasAtLeast('basic')).toBe(false);
expect(errorLicense.isOneOf(['basic', 'gold', 'platinum'])).toBe(false);
expect(goldLicense.hasAtLeast('basic')).toBe(true);
expect(goldLicense.hasAtLeast('gold')).toBe(true);
expect(goldLicense.hasAtLeast('platinum')).toBe(false);
expect(unavailableLicense.isOneOf(['basic', 'gold', 'platinum'])).toBe(false);
expect(enterpriseLicense.hasAtLeast('basic')).toBe(true);
expect(enterpriseLicense.hasAtLeast('platinum')).toBe(true);
expect(enterpriseLicense.hasAtLeast('enterprise')).toBe(true);
expect(enterpriseLicense.hasAtLeast('trial')).toBe(false);
});
it('getUnavailableReason', () => {
@ -115,9 +104,13 @@ describe('License', () => {
});
it('throws in case of unknown license type', () => {
expect(
() => basicLicense.check('ccr', 'any' as any).state
).toThrowErrorMatchingInlineSnapshot(`"\\"any\\" is not a valid license type"`);
expect(() => basicLicense.check('ccr', 'any' as any)).toThrowErrorMatchingInlineSnapshot(
`"\\"any\\" is not a valid license type"`
);
expect(() => basicLicense.hasAtLeast('any' as any)).toThrowErrorMatchingInlineSnapshot(
`"\\"any\\" is not a valid license type"`
);
});
});
});

View file

@ -26,8 +26,6 @@ export class License implements ILicense {
public readonly error?: string;
public readonly isActive: boolean;
public readonly isAvailable: boolean;
public readonly isBasic: boolean;
public readonly isNotBasic: boolean;
public readonly uid?: string;
public readonly status?: LicenseStatus;
@ -70,8 +68,6 @@ export class License implements ILicense {
}
this.isActive = this.status === 'active';
this.isBasic = this.isActive && this.type === 'basic';
this.isNotBasic = this.isActive && this.type !== 'basic';
}
toJSON() {
@ -89,23 +85,20 @@ export class License implements ILicense {
}
}
isOneOf(candidateLicenses: LicenseType | LicenseType[]) {
if (!this.type) {
hasAtLeast(minimumLicenseRequired: LicenseType) {
const type = this.type;
if (!type) {
return false;
}
if (!Array.isArray(candidateLicenses)) {
candidateLicenses = [candidateLicenses];
}
return candidateLicenses.includes(this.type);
}
check(pluginName: string, minimumLicenseRequired: LicenseType) {
if (!(minimumLicenseRequired in LICENSE_TYPE)) {
throw new Error(`"${minimumLicenseRequired}" is not a valid license type`);
}
return LICENSE_TYPE[minimumLicenseRequired] <= LICENSE_TYPE[type];
}
check(pluginName: string, minimumLicenseRequired: LicenseType) {
if (!this.isAvailable) {
return {
state: LICENSE_CHECK_STATE.Unavailable,
@ -117,26 +110,24 @@ export class License implements ILicense {
};
}
const type = this.type!;
if (!this.isActive) {
return {
state: LICENSE_CHECK_STATE.Expired,
message: i18n.translate('xpack.licensing.check.errorExpiredMessage', {
defaultMessage:
'You cannot use {pluginName} because your {licenseType} license has expired.',
values: { licenseType: type, pluginName },
values: { licenseType: this.type!, pluginName },
}),
};
}
if (LICENSE_TYPE[type] < LICENSE_TYPE[minimumLicenseRequired]) {
if (!this.hasAtLeast(minimumLicenseRequired)) {
return {
state: LICENSE_CHECK_STATE.Invalid,
message: i18n.translate('xpack.licensing.check.errorUnsupportedMessage', {
defaultMessage:
'Your {licenseType} license does not support {pluginName}. Please upgrade your license.',
values: { licenseType: type, pluginName },
values: { licenseType: this.type!, pluginName },
}),
};
}

View file

@ -7,7 +7,7 @@
import { Subject } from 'rxjs';
import { take, toArray } from 'rxjs/operators';
import { ILicense, LicenseType } from './types';
import { ILicense } from './types';
import { createLicenseUpdate } from './license_update';
import { licenseMock } from './licensing.mock';
@ -15,14 +15,11 @@ const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const stop$ = new Subject();
describe('licensing update', () => {
it('loads updates when triggered', async () => {
const types: LicenseType[] = ['basic', 'gold'];
const trigger$ = new Subject();
const fetcher = jest
.fn()
.mockImplementation(() =>
Promise.resolve(licenseMock.create({ license: { type: types.shift() } }))
);
.mockResolvedValueOnce(licenseMock.createLicense({ license: { type: 'basic' } }))
.mockResolvedValueOnce(licenseMock.createLicense({ license: { type: 'gold' } }));
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
@ -38,8 +35,8 @@ describe('licensing update', () => {
});
it('starts with initial value if presents', async () => {
const initialLicense = licenseMock.create({ license: { type: 'platinum' } });
const fetchedLicense = licenseMock.create({ license: { type: 'gold' } });
const initialLicense = licenseMock.createLicense({ license: { type: 'platinum' } });
const fetchedLicense = licenseMock.createLicense({ license: { type: 'gold' } });
const trigger$ = new Subject();
const fetcher = jest.fn().mockResolvedValue(fetchedLicense);
@ -55,14 +52,11 @@ describe('licensing update', () => {
it('does not emit if license has not changed', async () => {
const trigger$ = new Subject();
let i = 0;
const fetcher = jest
.fn()
.mockImplementation(() =>
Promise.resolve(
++i < 3 ? licenseMock.create() : licenseMock.create({ license: { type: 'gold' } })
)
);
.mockResolvedValueOnce(licenseMock.createLicense())
.mockResolvedValueOnce(licenseMock.createLicense())
.mockResolvedValueOnce(licenseMock.createLicense({ license: { type: 'gold' } }));
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
trigger$.next();
@ -83,7 +77,7 @@ describe('licensing update', () => {
it('new subscriptions does not force re-fetch', async () => {
const trigger$ = new Subject();
const fetcher = jest.fn().mockResolvedValue(licenseMock.create());
const fetcher = jest.fn().mockResolvedValue(licenseMock.createLicense());
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
@ -103,9 +97,9 @@ describe('licensing update', () => {
new Promise(resolve => {
if (firstCall) {
firstCall = false;
setTimeout(() => resolve(licenseMock.create()), delayMs);
setTimeout(() => resolve(licenseMock.createLicense()), delayMs);
} else {
resolve(licenseMock.create({ license: { type: 'gold' } }));
resolve(licenseMock.createLicense({ license: { type: 'gold' } }));
}
})
);
@ -126,7 +120,7 @@ describe('licensing update', () => {
it('completes license$ stream when stop$ is triggered', () => {
const trigger$ = new Subject();
const fetcher = jest.fn().mockResolvedValue(licenseMock.create());
const fetcher = jest.fn().mockResolvedValue(licenseMock.createLicense());
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
let completed = false;
@ -138,7 +132,7 @@ describe('licensing update', () => {
it('stops fetching when stop$ is triggered', () => {
const trigger$ = new Subject();
const fetcher = jest.fn().mockResolvedValue(licenseMock.create());
const fetcher = jest.fn().mockResolvedValue(licenseMock.createLicense());
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
const values: ILicense[] = [];
@ -152,8 +146,8 @@ describe('licensing update', () => {
it('refreshManually guarantees license fetching', async () => {
const trigger$ = new Subject();
const firstLicense = licenseMock.create({ license: { uid: 'first', type: 'basic' } });
const secondLicense = licenseMock.create({ license: { uid: 'second', type: 'gold' } });
const firstLicense = licenseMock.createLicense({ license: { uid: 'first', type: 'basic' } });
const secondLicense = licenseMock.createLicense({ license: { uid: 'second', type: 'gold' } });
const fetcher = jest
.fn()

View file

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { PublicLicense, PublicFeatures } from './types';
import { ILicense, PublicLicense, PublicFeatures, LICENSE_CHECK_STATE } from './types';
import { License } from './license';
function createLicense({
@ -40,6 +40,22 @@ function createLicense({
});
}
export const licenseMock = {
create: createLicense,
const createLicenseMock = () => {
const mock: jest.Mocked<ILicense> = {
isActive: true,
isAvailable: true,
signature: '',
toJSON: jest.fn(),
getUnavailableReason: jest.fn(),
getFeature: jest.fn(),
check: jest.fn(),
hasAtLeast: jest.fn(),
};
mock.check.mockReturnValue({ state: LICENSE_CHECK_STATE.Valid });
mock.hasAtLeast.mockReturnValue(true);
return mock;
};
export const licenseMock = {
createLicense,
createLicenseMock,
};

View file

@ -140,16 +140,6 @@ export interface ILicense {
*/
isAvailable: boolean;
/**
* Determine if the type of the license is basic, and also active.
*/
isBasic: boolean;
/**
* Determine if the type of the license is not basic, and also active.
*/
isNotBasic: boolean;
/**
* Returns
*/
@ -166,10 +156,10 @@ export interface ILicense {
getUnavailableReason: () => string | undefined;
/**
* Determine if the provided license types match against the license type.
* @param candidateLicenses license types to intersect against the license.
* Determine if license type >= minimal required license type.
* @param minimumLicenseRequired the minimum valid license required for the given feature
*/
isOneOf(candidateLicenses: LicenseType | LicenseType[]): boolean;
hasAtLeast(minimumLicenseRequired: LicenseType): boolean;
/**
* For a given plugin and license type, receive information about the status of the license.

View file

@ -8,7 +8,7 @@ import { LicensingPluginSetup } from './types';
import { licenseMock } from '../common/licensing.mock';
const createSetupMock = () => {
const license = licenseMock.create();
const license = licenseMock.createLicense();
const mock: jest.Mocked<LicensingPluginSetup> = {
license$: new BehaviorSubject(license),
refresh: jest.fn(),
@ -20,5 +20,5 @@ const createSetupMock = () => {
export const licensingMock = {
createSetup: createSetupMock,
createLicense: licenseMock.create,
...licenseMock,
};

View file

@ -30,8 +30,12 @@ describe('licensing plugin', () => {
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
const coreSetup = coreMock.createSetup();
const firstLicense = licenseMock.create({ license: { uid: 'first', type: 'basic' } });
const secondLicense = licenseMock.create({ license: { uid: 'second', type: 'gold' } });
const firstLicense = licenseMock.createLicense({
license: { uid: 'first', type: 'basic' },
});
const secondLicense = licenseMock.createLicense({
license: { uid: 'second', type: 'gold' },
});
coreSetup.http.get.mockResolvedValueOnce(firstLicense).mockResolvedValueOnce(secondLicense);
const { license$, refresh } = await plugin.setup(coreSetup);
@ -53,7 +57,7 @@ describe('licensing plugin', () => {
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
const coreSetup = coreMock.createSetup();
const fetchedLicense = licenseMock.create();
const fetchedLicense = licenseMock.createLicense();
coreSetup.http.get.mockResolvedValue(fetchedLicense);
const { refresh } = await plugin.setup(coreSetup);
@ -71,7 +75,7 @@ describe('licensing plugin', () => {
describe('#license$', () => {
it('starts with license saved in sessionStorage if available', async () => {
const sessionStorage = coreMock.createStorage();
const savedLicense = licenseMock.create({ license: { uid: 'saved' } });
const savedLicense = licenseMock.createLicense({ license: { uid: 'saved' } });
sessionStorage.getItem.mockReturnValue(JSON.stringify(savedLicense));
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
@ -90,12 +94,12 @@ describe('licensing plugin', () => {
const types: LicenseType[] = ['gold', 'platinum'];
const sessionStorage = coreMock.createStorage();
sessionStorage.getItem.mockReturnValue(JSON.stringify(licenseMock.create()));
sessionStorage.getItem.mockReturnValue(JSON.stringify(licenseMock.createLicense()));
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
const coreSetup = coreMock.createSetup();
coreSetup.http.get.mockImplementation(() =>
Promise.resolve(licenseMock.create({ license: { type: types.shift() } }))
Promise.resolve(licenseMock.createLicense({ license: { type: types.shift() } }))
);
const { license$, refresh } = await plugin.setup(coreSetup);
@ -123,7 +127,7 @@ describe('licensing plugin', () => {
const coreSetup = coreMock.createSetup();
const fetchedLicense = licenseMock.create({ license: { uid: 'fresh' } });
const fetchedLicense = licenseMock.createLicense({ license: { uid: 'fresh' } });
coreSetup.http.get.mockResolvedValue(fetchedLicense);
const { license$, refresh } = await plugin.setup(coreSetup);
@ -196,7 +200,7 @@ describe('licensing plugin', () => {
const coreSetup = coreMock.createSetup();
coreSetup.http.get.mockResolvedValue(licenseMock.create({ signature: 'signature-1' }));
coreSetup.http.get.mockResolvedValue(licenseMock.createLicense({ signature: 'signature-1' }));
let registeredInterceptor: HttpInterceptor;
coreSetup.http.intercept.mockImplementation((interceptor: HttpInterceptor) => {
@ -321,7 +325,7 @@ describe('licensing plugin', () => {
const coreSetup = coreMock.createSetup();
coreSetup.http.get.mockResolvedValueOnce(
licenseMock.create({ license: { status: 'active', type: 'gold' } })
licenseMock.createLicense({ license: { status: 'active', type: 'gold' } })
);
const { refresh } = await plugin.setup(coreSetup);
@ -338,8 +342,12 @@ describe('licensing plugin', () => {
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
const coreSetup = coreMock.createSetup();
const activeLicense = licenseMock.create({ license: { status: 'active', type: 'gold' } });
const expiredLicense = licenseMock.create({ license: { status: 'expired', type: 'gold' } });
const activeLicense = licenseMock.createLicense({
license: { status: 'active', type: 'gold' },
});
const expiredLicense = licenseMock.createLicense({
license: { status: 'expired', type: 'gold' },
});
coreSetup.http.get
.mockResolvedValueOnce(activeLicense)
.mockResolvedValueOnce(expiredLicense)

View file

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { BehaviorSubject } from 'rxjs';
import { LicensingPluginSetup } from './types';
import { licenseMock } from '../common/licensing.mock';
const createSetupMock = () => {
const license = licenseMock.create();
const mock: jest.Mocked<LicensingPluginSetup> = {
license$: new BehaviorSubject(license),
refresh: jest.fn(),
createLicensePoller: jest.fn(),
};
mock.refresh.mockResolvedValue(license);
mock.createLicensePoller.mockReturnValue({
license$: mock.license$,
refresh: mock.refresh,
});
return mock;
};
export const licensingMock = {
createSetup: createSetupMock,
createLicense: licenseMock.create,
};

View file

@ -11,8 +11,8 @@ import { createRouteHandlerContext } from './licensing_route_handler_context';
describe('createRouteHandlerContext', () => {
it('returns a function providing the last license value', async () => {
const firstLicense = licenseMock.create();
const secondLicense = licenseMock.create();
const firstLicense = licenseMock.createLicense();
const secondLicense = licenseMock.createLicense();
const license$ = new BehaviorSubject(firstLicense);
const routeHandler = createRouteHandlerContext(license$);

View file

@ -3,5 +3,27 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { BehaviorSubject } from 'rxjs';
import { LicensingPluginSetup } from './types';
import { licenseMock } from '../common/licensing.mock';
export * from './licensing.mock';
const createSetupMock = () => {
const license = licenseMock.createLicense();
const mock: jest.Mocked<LicensingPluginSetup> = {
license$: new BehaviorSubject(license),
refresh: jest.fn(),
createLicensePoller: jest.fn(),
};
mock.refresh.mockResolvedValue(license);
mock.createLicensePoller.mockReturnValue({
license$: mock.license$,
refresh: mock.refresh,
});
return mock;
};
export const licensingMock = {
createSetup: createSetupMock,
...licenseMock,
};

View file

@ -11,7 +11,7 @@ import { licenseMock } from '../common/licensing.mock';
describe('createOnPreResponseHandler', () => {
it('sets license.signature header immediately for non-error responses', async () => {
const refresh = jest.fn();
const license$ = new BehaviorSubject(licenseMock.create({ signature: 'foo' }));
const license$ = new BehaviorSubject(licenseMock.createLicense({ signature: 'foo' }));
const toolkit = httpServiceMock.createOnPreResponseToolkit();
const interceptor = createOnPreResponseHandler(refresh, license$);
@ -26,8 +26,8 @@ describe('createOnPreResponseHandler', () => {
});
});
it('sets license.signature header after refresh for non-error responses', async () => {
const updatedLicense = licenseMock.create({ signature: 'bar' });
const license$ = new BehaviorSubject(licenseMock.create({ signature: 'foo' }));
const updatedLicense = licenseMock.createLicense({ signature: 'bar' });
const license$ = new BehaviorSubject(licenseMock.createLicense({ signature: 'foo' }));
const refresh = jest.fn().mockImplementation(
() =>
new Promise(resolve => {

View file

@ -5,21 +5,13 @@
*/
import { of, BehaviorSubject } from 'rxjs';
import { ILicense } from '../../../licensing/public';
import { licensingMock } from '../../../licensing/public/mocks';
import { SecurityLicenseService } from './license_service';
function getMockRawLicense({ isAvailable = false } = {}) {
return ({
isAvailable,
isOneOf: jest.fn(),
getFeature: jest.fn(),
} as unknown) as jest.Mocked<ILicense>;
}
describe('license features', function() {
it('should display error when ES is unavailable', () => {
const serviceSetup = new SecurityLicenseService().setup({
license$: of((undefined as unknown) as ILicense),
license$: of(undefined as any),
});
expect(serviceSetup.license.getFeatures()).toEqual({
showLogin: true,
@ -33,8 +25,10 @@ describe('license features', function() {
});
it('should display error when X-Pack is unavailable', () => {
const rawLicenseMock = licensingMock.createLicenseMock();
rawLicenseMock.isAvailable = false;
const serviceSetup = new SecurityLicenseService().setup({
license$: of(getMockRawLicense({ isAvailable: false })),
license$: of(rawLicenseMock),
});
expect(serviceSetup.license.getFeatures()).toEqual({
showLogin: true,
@ -48,7 +42,9 @@ describe('license features', function() {
});
it('should notify consumers of licensed feature changes', () => {
const rawLicense$ = new BehaviorSubject(getMockRawLicense({ isAvailable: false }));
const rawLicenseMock = licensingMock.createLicenseMock();
rawLicenseMock.isAvailable = false;
const rawLicense$ = new BehaviorSubject(rawLicenseMock);
const serviceSetup = new SecurityLicenseService().setup({
license$: rawLicense$,
});
@ -71,7 +67,7 @@ describe('license features', function() {
]
`);
rawLicense$.next(getMockRawLicense({ isAvailable: true }));
rawLicense$.next(licensingMock.createLicenseMock());
expect(subscriptionHandler).toHaveBeenCalledTimes(2);
expect(subscriptionHandler.mock.calls[1]).toMatchInlineSnapshot(`
Array [
@ -92,10 +88,8 @@ describe('license features', function() {
});
it('should show login page and other security elements, allow RBAC but forbid document level security if license is not platinum or trial.', () => {
const mockRawLicense = getMockRawLicense({ isAvailable: true });
mockRawLicense.isOneOf.mockImplementation(licenses =>
Array.isArray(licenses) ? licenses.includes('basic') : licenses === 'basic'
);
const mockRawLicense = licensingMock.createLicenseMock();
mockRawLicense.hasAtLeast.mockReturnValue(false);
mockRawLicense.getFeature.mockReturnValue({ isEnabled: true, isAvailable: true });
const serviceSetup = new SecurityLicenseService().setup({
@ -114,8 +108,8 @@ describe('license features', function() {
});
it('should not show login page or other security elements if security is disabled in Elasticsearch.', () => {
const mockRawLicense = getMockRawLicense({ isAvailable: true });
mockRawLicense.isOneOf.mockReturnValue(false);
const mockRawLicense = licensingMock.createLicenseMock();
mockRawLicense.hasAtLeast.mockReturnValue(false);
mockRawLicense.getFeature.mockReturnValue({ isEnabled: false, isAvailable: true });
const serviceSetup = new SecurityLicenseService().setup({
@ -133,14 +127,9 @@ describe('license features', function() {
});
it('should allow to login, allow RBAC and document level security if license >= platinum', () => {
const mockRawLicense = getMockRawLicense({ isAvailable: true });
mockRawLicense.isOneOf.mockImplementation(licenses => {
const licenseArray = [licenses].flat();
return (
licenseArray.includes('trial') ||
licenseArray.includes('platinum') ||
licenseArray.includes('enterprise')
);
const mockRawLicense = licensingMock.createLicenseMock();
mockRawLicense.hasAtLeast.mockImplementation(license => {
return license === 'trial' || license === 'platinum' || license === 'enterprise';
});
mockRawLicense.getFeature.mockReturnValue({ isEnabled: true, isAvailable: true });

View file

@ -92,7 +92,7 @@ export class SecurityLicenseService {
};
}
const isLicensePlatinumOrBetter = rawLicense.isOneOf(['enterprise', 'platinum', 'trial']);
const isLicensePlatinumOrBetter = rawLicense.hasAtLeast('platinum');
return {
showLogin: true,
allowLogin: true,

View file

@ -21,7 +21,7 @@ const validLicense = {
isEnabled: true,
};
},
isOneOf: (...candidates) => true,
hasAtLeast: (...candidates) => true,
} as ILicense;
describe('SecurityNavControlService', () => {