2023-02-20 02:51:40 +00:00
|
|
|
import {UserAPI} from 'app/common/UserAPI';
|
|
|
|
import axios from 'axios';
|
|
|
|
import {assert} from 'chai';
|
|
|
|
import * as fse from 'fs-extra';
|
|
|
|
import {TestServer} from 'test/gen-server/apiUtils';
|
|
|
|
import {configForUser} from 'test/gen-server/testUtils';
|
|
|
|
import {createTmpDir} from 'test/server/docTools';
|
|
|
|
import {openClient} from 'test/server/gristClient';
|
|
|
|
import * as testUtils from 'test/server/testUtils';
|
|
|
|
|
|
|
|
const chimpy = configForUser('Chimpy');
|
2024-04-18 12:13:16 +00:00
|
|
|
const kiwi = configForUser('Kiwi');
|
2023-02-20 02:51:40 +00:00
|
|
|
|
|
|
|
describe('DocApi2', function() {
|
|
|
|
this.timeout(40000);
|
|
|
|
let server: TestServer;
|
2024-04-18 12:13:16 +00:00
|
|
|
let homeUrl: string;
|
2023-02-20 02:51:40 +00:00
|
|
|
let owner: UserAPI;
|
|
|
|
let wsId: number;
|
|
|
|
testUtils.setTmpLogLevel('error');
|
2023-11-06 08:24:59 +00:00
|
|
|
let oldEnv: testUtils.EnvironmentSnapshot;
|
2023-02-20 02:51:40 +00:00
|
|
|
|
|
|
|
before(async function() {
|
2023-11-06 08:24:59 +00:00
|
|
|
oldEnv = new testUtils.EnvironmentSnapshot();
|
2023-02-20 02:51:40 +00:00
|
|
|
const tmpDir = await createTmpDir();
|
|
|
|
process.env.GRIST_DATA_DIR = tmpDir;
|
|
|
|
process.env.STRIPE_ENDPOINT_SECRET = 'TEST_WITHOUT_ENDPOINT_SECRET';
|
|
|
|
// Use the TEST_REDIS_URL as the global redis url, if supplied.
|
|
|
|
if (process.env.TEST_REDIS_URL && !process.env.REDIS_URL) {
|
|
|
|
process.env.REDIS_URL = process.env.TEST_REDIS_URL;
|
|
|
|
}
|
|
|
|
|
|
|
|
server = new TestServer(this);
|
2024-04-18 12:13:16 +00:00
|
|
|
homeUrl = await server.start(['home', 'docs']);
|
2023-02-20 02:51:40 +00:00
|
|
|
const api = await server.createHomeApi('chimpy', 'docs', true);
|
|
|
|
await api.newOrg({name: 'testy', domain: 'testy'});
|
|
|
|
owner = await server.createHomeApi('chimpy', 'testy', true);
|
|
|
|
wsId = await owner.newWorkspace({name: 'ws'}, 'current');
|
|
|
|
});
|
|
|
|
|
|
|
|
after(async function() {
|
|
|
|
const api = await server.createHomeApi('chimpy', 'docs');
|
|
|
|
await api.deleteOrg('testy');
|
|
|
|
await server.stop();
|
|
|
|
oldEnv.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('DELETE /docs/{did}', async () => {
|
|
|
|
it('permanently deletes a document and all of its forks', async function() {
|
|
|
|
// Create a new document and fork it twice.
|
|
|
|
const docId = await owner.newDoc({name: 'doc'}, wsId);
|
|
|
|
const session = await owner.getSessionActive();
|
|
|
|
const client = await openClient(server.server, session.user.email, session.org?.domain || 'docs');
|
|
|
|
await client.openDocOnConnect(docId);
|
|
|
|
const forkDocResponse1 = await client.send('fork', 0);
|
|
|
|
const forkDocResponse2 = await client.send('fork', 0);
|
|
|
|
|
|
|
|
// Check that files were created for the trunk and forks.
|
|
|
|
const docPath = server.server.getStorageManager().getPath(docId);
|
|
|
|
const forkPath1 = server.server.getStorageManager().getPath(forkDocResponse1.data.docId);
|
|
|
|
const forkPath2 = server.server.getStorageManager().getPath(forkDocResponse2.data.docId);
|
|
|
|
assert.equal(await fse.pathExists(docPath), true);
|
|
|
|
assert.equal(await fse.pathExists(forkPath1), true);
|
|
|
|
assert.equal(await fse.pathExists(forkPath2), true);
|
|
|
|
|
|
|
|
// Delete the trunk via API.
|
2024-04-18 12:13:16 +00:00
|
|
|
const deleteDocResponse = await axios.delete(`${homeUrl}/api/docs/${docId}`, chimpy);
|
2023-02-20 02:51:40 +00:00
|
|
|
assert.equal(deleteDocResponse.status, 200);
|
|
|
|
|
|
|
|
// Check that files for the trunk and forks were deleted.
|
|
|
|
assert.equal(await fse.pathExists(docPath), false);
|
|
|
|
assert.equal(await fse.pathExists(forkPath1), false);
|
|
|
|
assert.equal(await fse.pathExists(forkPath2), false);
|
|
|
|
});
|
|
|
|
});
|
2024-04-18 12:13:16 +00:00
|
|
|
|
|
|
|
describe('/docs/{did}/timing', async () => {
|
|
|
|
let docId: string;
|
|
|
|
before(async function() {
|
|
|
|
docId = await owner.newDoc({name: 'doc2'}, wsId);
|
|
|
|
});
|
|
|
|
|
|
|
|
after(async function() {
|
|
|
|
await owner.deleteDoc(docId);
|
|
|
|
});
|
|
|
|
|
|
|
|
// There are two endpoints here /timing/start and /timing/stop.
|
|
|
|
// Here we just test that it is operational, available only for owners
|
|
|
|
// and that it returns sane results. Exact tests are done in python.
|
|
|
|
|
|
|
|
// Smoke test.
|
|
|
|
it('POST /docs/{did}/timing smoke tests', async function() {
|
|
|
|
// We are disabled.
|
|
|
|
let resp = await axios.get(`${homeUrl}/api/docs/${docId}/timing`, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
assert.deepEqual(resp.data, {status: 'disabled'});
|
|
|
|
|
|
|
|
// Start it.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/start`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
// Stop it.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
assert.deepEqual(resp.data, []);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('POST /docs/{did}/timing/start', async function() {
|
|
|
|
// Start timing as non owner, should fail.
|
|
|
|
let resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/start`, {}, kiwi);
|
|
|
|
assert.equal(resp.status, 403);
|
|
|
|
|
|
|
|
// Query status as non owner, should fail.
|
|
|
|
resp = await axios.get(`${homeUrl}/api/docs/${docId}/timing`, kiwi);
|
|
|
|
assert.equal(resp.status, 403);
|
|
|
|
|
|
|
|
// Check as owner.
|
|
|
|
resp = await axios.get(`${homeUrl}/api/docs/${docId}/timing`, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
assert.deepEqual(resp.data, {status: 'disabled'});
|
|
|
|
|
|
|
|
// Start timing as owner.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/start`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
// Check we are started.
|
|
|
|
resp = await axios.get(`${homeUrl}/api/docs/${docId}/timing`, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
assert.deepEqual(resp.data, {status: 'active', timing: []});
|
|
|
|
|
|
|
|
// Starting timing again works as expected, returns 400 as this is already.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/start`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 400);
|
|
|
|
|
|
|
|
// As non owner
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, kiwi);
|
|
|
|
assert.equal(resp.status, 403);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('POST /docs/{did}/timing/stop', async function() {
|
|
|
|
// Timings are turned on, so we can stop them.
|
|
|
|
// First as non owner, we should fail.
|
|
|
|
let resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, kiwi);
|
|
|
|
assert.equal(resp.status, 403);
|
|
|
|
|
|
|
|
// Next as owner.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
// Now do it once again, we should got 400, as we are not timing.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 400);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('GET /docs/{did}/timing', async function() {
|
|
|
|
// Now we can check the results. Start timing and check that we got [] in response.
|
|
|
|
let resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/start`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
assert.deepEqual(resp.data, []);
|
|
|
|
|
|
|
|
// Now create a table with a formula column and make sure we see it in the results.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/apply`, [
|
|
|
|
['AddTable', 'Timings', [
|
|
|
|
{id: 'A', formula: '$id' }
|
|
|
|
]],
|
|
|
|
], chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
// Now start it again,
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/start`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
// Make sure we see that it is active and we have some intermediate results
|
|
|
|
resp = await axios.get(`${homeUrl}/api/docs/${docId}/timing`, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
assert.deepEqual(resp.data, {status: 'active', timing: []});
|
|
|
|
|
|
|
|
// And trigger some formula calculations.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/apply`, [
|
|
|
|
['BulkAddRecord', 'Timings', [null, null], {}],
|
|
|
|
], chimpy);
|
|
|
|
assert.equal(resp.status, 200, JSON.stringify(resp.data));
|
|
|
|
|
|
|
|
// Make sure we can't stop it as non owner.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, kiwi);
|
|
|
|
assert.equal(resp.status, 403);
|
|
|
|
|
|
|
|
// Now stop it as owner and make sure the result is sane.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/stop`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200, JSON.stringify(resp.data));
|
|
|
|
const data = resp.data as Array<{
|
|
|
|
tableId: string;
|
|
|
|
colId: string;
|
|
|
|
sum: number;
|
|
|
|
count: number;
|
|
|
|
average: number;
|
|
|
|
max: number;
|
|
|
|
markers?: Array<{
|
|
|
|
name: string;
|
|
|
|
sum: number;
|
|
|
|
count: number;
|
|
|
|
average: number;
|
|
|
|
max: number;
|
|
|
|
}>
|
|
|
|
}>;
|
|
|
|
|
|
|
|
assert.isAbove(data.length, 0);
|
|
|
|
assert.equal(data[0].tableId, 'Timings');
|
|
|
|
assert.isTrue(typeof data[0].sum === 'number');
|
|
|
|
assert.isTrue(typeof data[0].count === 'number');
|
|
|
|
assert.isTrue(typeof data[0].average === 'number');
|
|
|
|
assert.isTrue(typeof data[0].max === 'number');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('POST /docs/{did}/timing/start remembers state after reload', async function() {
|
|
|
|
// Make sure we are off.
|
|
|
|
let resp = await axios.get(`${homeUrl}/api/docs/${docId}/timing`, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
assert.deepEqual(resp.data, {status: 'disabled'});
|
|
|
|
|
|
|
|
// Now start it.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/timing/start`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
// Now reload document.
|
|
|
|
resp = await axios.post(`${homeUrl}/api/docs/${docId}/force-reload`, {}, chimpy);
|
|
|
|
assert.equal(resp.status, 200);
|
|
|
|
|
|
|
|
// And check that we are still on.
|
|
|
|
resp = await axios.get(`${homeUrl}/api/docs/${docId}/timing`, chimpy);
|
|
|
|
assert.equal(resp.status, 200, JSON.stringify(resp.data));
|
2024-05-21 16:27:06 +00:00
|
|
|
assert.equal(resp.data.status, 'active');
|
|
|
|
assert.isNotEmpty(resp.data.timing);
|
2024-04-18 12:13:16 +00:00
|
|
|
});
|
|
|
|
});
|
2023-02-20 02:51:40 +00:00
|
|
|
});
|