oncall-engine/grafana-plugin/src/state/rootBaseStore/rootBaseStore.test.ts
Michael Derynck 0d22ae7e53
Change functions for access control (#3184)
# What this PR does
Update calls used for access control to match changes in Grafana:
https://github.com/grafana/grafana/pull/76187
https://github.com/grafana/grafana/pull/76237

## Which issue(s) this PR fixes

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
2023-10-23 15:58:45 -06:00

362 lines
13 KiB
TypeScript

import { OrgRole } from '@grafana/data';
import { contextSrv } from 'grafana/app/core/core';
import { OnCallAppPluginMeta } from 'types';
import PluginState from 'state/plugin';
import { isUserActionAllowed as isUserActionAllowedOriginal } from 'utils/authorization';
import { RootBaseStore } from './';
jest.mock('state/plugin');
jest.mock('utils/authorization');
jest.mock('grafana/app/core/core', () => ({
contextSrv: {
user: {
orgRole: null,
},
},
}));
const isUserActionAllowed = isUserActionAllowedOriginal as jest.Mock<ReturnType<typeof isUserActionAllowedOriginal>>;
const generatePluginData = (
onCallApiUrl: OnCallAppPluginMeta['jsonData']['onCallApiUrl'] = null
): OnCallAppPluginMeta =>
({
jsonData: onCallApiUrl === null ? null : { onCallApiUrl },
} as OnCallAppPluginMeta);
describe('rootBaseStore', () => {
afterEach(() => {
jest.resetAllMocks();
});
test("onCallApiUrl is not set in the plugin's meta jsonData", async () => {
// mocks/setup
const rootBaseStore = new RootBaseStore();
// test
await rootBaseStore.setupPlugin(generatePluginData());
// assertions
expect(rootBaseStore.initializationError).toEqual('🚫 Plugin has not been initialized');
});
test('when there is an issue checking the plugin connection, the error is properly handled', async () => {
// mocks/setup
const errorMsg = 'ohhh noooo error';
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce(errorMsg);
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(rootBaseStore.initializationError).toEqual(errorMsg);
});
test('currently undergoing maintenance', async () => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
const maintenanceMessage = 'mncvnmvcmnvkjdjkd';
PluginState.updatePluginStatus = jest
.fn()
.mockResolvedValueOnce({ currently_undergoing_maintenance_message: maintenanceMessage });
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(rootBaseStore.initializationError).toEqual(`🚧 ${maintenanceMessage} 🚧`);
expect(rootBaseStore.currentlyUndergoingMaintenance).toBe(true);
});
test('anonymous user', async () => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
is_user_anonymous: true,
is_installed: true,
token_ok: true,
allow_signup: true,
version: 'asdfasdf',
license: 'asdfasdf',
});
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(rootBaseStore.initializationError).toEqual(
'😞 Grafana OnCall is available for authorized users only, please sign in to proceed.'
);
});
test('the plugin is not installed, and allow_signup is false', async () => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
is_user_anonymous: false,
is_installed: false,
token_ok: true,
allow_signup: false,
version: 'asdfasdf',
license: 'asdfasdf',
});
PluginState.installPlugin = jest.fn().mockResolvedValueOnce(null);
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(PluginState.installPlugin).toHaveBeenCalledTimes(0);
expect(rootBaseStore.initializationError).toEqual(
'🚫 OnCall has temporarily disabled signup of new users. Please try again later.'
);
});
test('plugin is not installed, user is not an Admin', async () => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
contextSrv.user.orgRole = OrgRole.Viewer;
contextSrv.licensedAccessControlEnabled = jest.fn().mockReturnValue(false);
contextSrv.hasPermission = jest.fn().mockReturnValue(false);
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
is_user_anonymous: false,
is_installed: false,
token_ok: true,
allow_signup: true,
version: 'asdfasdf',
license: 'asdfasdf',
});
isUserActionAllowed.mockReturnValueOnce(false);
PluginState.installPlugin = jest.fn().mockResolvedValueOnce(null);
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(PluginState.installPlugin).toHaveBeenCalledTimes(0);
expect(rootBaseStore.initializationError).toEqual(
'🚫 User with Admin permissions in your organization must sign on and setup OnCall before it can be used'
);
});
test.each([
{ is_installed: false, token_ok: true },
{ is_installed: true, token_ok: false },
])('signup is allowed, user is an admin, plugin installation is triggered', async (scenario) => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
const mockedLoadCurrentUser = jest.fn();
contextSrv.user.orgRole = OrgRole.Admin;
contextSrv.licensedAccessControlEnabled = jest.fn().mockResolvedValueOnce(false);
contextSrv.hasPermission = jest.fn().mockReturnValue(true);
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
...scenario,
is_user_anonymous: false,
allow_signup: true,
version: 'asdfasdf',
license: 'asdfasdf',
});
isUserActionAllowed.mockReturnValueOnce(true);
PluginState.installPlugin = jest.fn().mockResolvedValueOnce(null);
rootBaseStore.userStore.loadCurrentUser = mockedLoadCurrentUser;
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(PluginState.installPlugin).toHaveBeenCalledTimes(1);
expect(PluginState.installPlugin).toHaveBeenCalledWith();
});
test.each([
{ role: OrgRole.Admin, missing_permissions: [], expected_result: true },
{ role: OrgRole.Viewer, missing_permissions: [], expected_result: true },
{
role: OrgRole.Admin,
missing_permissions: ['plugins:write', 'org.users:read', 'teams:read', 'apikeys:create', 'apikeys:delete'],
expected_result: false,
},
{
role: OrgRole.Viewer,
missing_permissions: ['plugins:write', 'org.users:read', 'teams:read', 'apikeys:create', 'apikeys:delete'],
expected_result: false,
},
])('signup is allowed, licensedAccessControlEnabled, various roles and permissions', async (scenario) => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
const mockedLoadCurrentUser = jest.fn();
contextSrv.user.orgRole = scenario.role;
contextSrv.licensedAccessControlEnabled = jest.fn().mockReturnValue(true);
rootBaseStore.checkMissingSetupPermissions = jest.fn().mockImplementation(() => scenario.missing_permissions);
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
...scenario,
is_user_anonymous: false,
allow_signup: true,
version: 'asdfasdf',
license: 'asdfasdf',
});
isUserActionAllowed.mockReturnValueOnce(true);
PluginState.installPlugin = jest.fn().mockResolvedValueOnce(null);
rootBaseStore.userStore.loadCurrentUser = mockedLoadCurrentUser;
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
if (scenario.expected_result) {
expect(PluginState.installPlugin).toHaveBeenCalledTimes(1);
expect(PluginState.installPlugin).toHaveBeenCalledWith();
} else {
expect(PluginState.installPlugin).toHaveBeenCalledTimes(0);
expect(rootBaseStore.initializationError).toEqual(
'🚫 User is missing permission(s) ' +
scenario.missing_permissions.join(', ') +
' to setup OnCall before it can be used'
);
}
});
test('plugin is not installed, signup is allowed, the user is an admin, and plugin installation throws an error', async () => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
const installPluginError = new Error('asdasdfasdfasf');
const humanReadableErrorMsg = 'asdfasldkfjaksdjflk';
contextSrv.user.orgRole = OrgRole.Admin;
contextSrv.licensedAccessControlEnabled = jest.fn().mockReturnValue(false);
contextSrv.hasPermission = jest.fn().mockReturnValue(true);
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
is_user_anonymous: false,
is_installed: false,
token_ok: true,
allow_signup: true,
version: 'asdfasdf',
license: 'asdfasdf',
});
isUserActionAllowed.mockReturnValueOnce(true);
PluginState.installPlugin = jest.fn().mockRejectedValueOnce(installPluginError);
PluginState.getHumanReadableErrorFromOnCallError = jest.fn().mockReturnValueOnce(humanReadableErrorMsg);
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(PluginState.installPlugin).toHaveBeenCalledTimes(1);
expect(PluginState.installPlugin).toHaveBeenCalledWith();
expect(PluginState.getHumanReadableErrorFromOnCallError).toHaveBeenCalledTimes(1);
expect(PluginState.getHumanReadableErrorFromOnCallError).toHaveBeenCalledWith(
installPluginError,
onCallApiUrl,
'install'
);
expect(rootBaseStore.initializationError).toEqual(humanReadableErrorMsg);
});
test('when the plugin is installed, a data sync is triggered', async () => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
const mockedLoadCurrentUser = jest.fn();
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
is_user_anonymous: false,
is_installed: true,
token_ok: true,
allow_signup: true,
version: 'asdfasdf',
license: 'asdfasdf',
});
rootBaseStore.userStore.loadCurrentUser = mockedLoadCurrentUser;
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(mockedLoadCurrentUser).toHaveBeenCalledTimes(1);
expect(mockedLoadCurrentUser).toHaveBeenCalledWith();
expect(rootBaseStore.initializationError).toBeNull();
});
test('when the plugin is installed, and the data sync returns an error, it is properly handled', async () => {
// mocks/setup
const onCallApiUrl = 'http://asdfasdf.com';
const rootBaseStore = new RootBaseStore();
const mockedLoadCurrentUser = jest.fn();
const updatePluginStatusError = 'asdasdfasdfasf';
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce({
is_user_anonymous: false,
is_installed: true,
token_ok: true,
allow_signup: true,
version: 'asdfasdf',
license: 'asdfasdf',
});
PluginState.updatePluginStatus = jest.fn().mockResolvedValueOnce(updatePluginStatusError);
rootBaseStore.userStore.loadCurrentUser = mockedLoadCurrentUser;
// test
await rootBaseStore.setupPlugin(generatePluginData(onCallApiUrl));
// assertions
expect(PluginState.updatePluginStatus).toHaveBeenCalledTimes(1);
expect(PluginState.updatePluginStatus).toHaveBeenCalledWith(onCallApiUrl);
expect(rootBaseStore.initializationError).toEqual(updatePluginStatusError);
});
});