gristlabs_grist-core/test/nbrowser/WebhookOverflow.ts
Jarosław Sadziński 14718120bd (core) Allowing installation admins to manage billing.
Summary:
Permissions for admin billing endpoints were changed

- Support user can't use admin subscription endpoints
- Installation admin (as support user) can see billing details on any site
- Installation admin (unlike support user) can replace subscription (or attach payment) on any site, regardless permissions

Installation admin is any user that belongs to a special `admin` org. If `admin` org is not defined, it defaults to
support user. In that case, with this diff, the support user receives admin's permissions, and now can replace subscription on
any site (without being billing manager).

Test Plan: Added new test

Reviewers: dsagal, paulfitz

Reviewed By: dsagal, paulfitz

Subscribers: dsagal

Differential Revision: https://phab.getgrist.com/D4338
2024-09-11 23:06:47 +02:00

138 lines
4.9 KiB
TypeScript

import {DocCreationInfo} from 'app/common/DocListAPI';
import {DocAPI} from 'app/common/UserAPI';
import {assert, driver, Key} from 'mocha-webdriver';
import * as gu from 'test/nbrowser/gristUtils';
import {server, setupTestSuite} from 'test/nbrowser/testUtils';
import {EnvironmentSnapshot} from 'test/server/testUtils';
import {WebhookFields} from "../../app/common/Triggers";
describe('WebhookOverflow', function () {
this.timeout(30000);
const cleanup = setupTestSuite();
let session: gu.Session;
let oldEnv: EnvironmentSnapshot;
let doc: DocCreationInfo;
let docApi: DocAPI;
gu.bigScreen();
before(async function () {
oldEnv = new EnvironmentSnapshot();
process.env.ALLOWED_WEBHOOK_DOMAINS = '*';
process.env.GRIST_MAX_QUEUE_SIZE = '4';
await server.restart();
session = await gu.session().teamSite.login();
const api = session.createHomeApi();
doc = await session.tempDoc(cleanup, 'Hello.grist');
docApi = api.getDocAPI(doc.id);
await api.applyUserActions(doc.id, [
['AddTable', 'Table2', [{id: 'A'}, {id: 'B'}, {id: 'C'}, {id: 'D'}, {id: 'E'}]],
['AddRecord', 'Table2', null, {}],
]);
const webhookDetails: WebhookFields = {
url: 'https://localhost/WrongWebhook',
eventTypes: ["update"],
enabled: true,
name: 'test webhook',
tableId: 'Table2',
watchedColIds: []
};
await docApi.addWebhook(webhookDetails);
await docApi.addWebhook(webhookDetails);
});
after(async function () {
oldEnv.restore();
await server.restart();
});
async function enterCellWithoutWaitingOnServer(...keys: string[]) {
const lastKey = keys[keys.length - 1];
if (![Key.ENTER, Key.TAB, Key.DELETE].includes(lastKey)) {
keys.push(Key.ENTER);
}
await driver.sendKeys(...keys);
}
async function getNumWaiting() {
const cells = await gu.getVisibleDetailCells({col: 'Status', rowNums: [1, 2]});
return cells.map((cell) => {
const status = JSON.parse(cell.replace(/\n/g, ''));
return status.numWaiting;
});
}
async function overflowWebhook() {
await gu.openPage('Table2');
await gu.getCell('A', 1).click();
await gu.enterCell(new Date().toString());
await gu.getCell('B', 1).click();
await enterCellWithoutWaitingOnServer(new Date().toString());
await gu.waitToPass(async () => {
const toast = await gu.getToasts();
assert.include(toast, 'New changes are temporarily suspended. Webhooks queue overflowed.' +
' Please check webhooks settings, remove invalid webhooks, and clean the queue.\ngo to webhook settings');
}, 4000);
}
async function overflowResolved() {
await gu.waitForServer();
await gu.waitToPass(async () => {
const toast = await gu.getToasts();
assert.notInclude(toast, 'New changes are temporarily suspended. Webhooks queue overflowed.' +
' Please check webhooks settings, remove invalid webhooks, and clean the queue.\ngo to webhook settings');
}, 12500);
}
it('should show a message when overflowed', async function () {
await overflowWebhook();
});
it('message should disappear after clearing queue', async function () {
await openWebhookPageWithoutWaitForServer();
assert.deepEqual(await getNumWaiting(), [2, 2]);
await driver.findContent('button', /Clear Queue/).click();
await overflowResolved();
assert.deepEqual(await getNumWaiting(), [0, 0]);
});
it('should clear a single webhook queue when that webhook is disabled', async function () {
await overflowWebhook();
await openWebhookPageWithoutWaitForServer();
await gu.waitToPass(async () => {
assert.deepEqual(await getNumWaiting(), [2, 2]);
}, 4000);
await gu.getDetailCell({col: 'Enabled', rowNum: 1}).click();
await overflowResolved();
assert.deepEqual(await getNumWaiting(), [0, 2]);
});
});
async function openWebhookPageWithoutWaitForServer() {
await openDocumentSettings();
const button = await driver.findContentWait('a', /Manage Webhooks/i, 3000);
await gu.scrollIntoView(button).click();
await waitForWebhookPage();
}
async function waitForWebhookPage() {
await driver.findContentWait('button', /Clear Queue/, 3000);
// No section, so no easy utility for setting focus. Click on a random cell.
await gu.waitToPass(async () => {
await gu.getDetailCell({col: 'Webhook Id', rowNum: 1}).click();
});
}
export async function openAccountMenu() {
await driver.findWait('.test-dm-account', 1000).click();
// Since the AccountWidget loads orgs and the user data asynchronously, the menu
// can expand itself causing the click to land on a wrong button.
await driver.findWait('.test-site-switcher-org', 1000);
await driver.sleep(250); // There's still some jitter (scroll-bar? other user accounts?)
}
export async function openDocumentSettings() {
await openAccountMenu();
await driver.findContent('.grist-floating-menu a', 'Document Settings').click();
await gu.waitForUrl(/settings/, 5000);
}