From abd732fa52537453100242530db7f200067e2fc3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 19 Aug 2022 13:54:08 +0100 Subject: [PATCH] Add tests for exporting data --- .../server/src/api/routes/tests/row.spec.js | 216 ++++++++++++------ 1 file changed, 149 insertions(+), 67 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 86e47924d8..5cd282bb34 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -3,7 +3,12 @@ const setup = require("./utilities") const { basicRow } = setup.structures const { doInAppContext } = require("@budibase/backend-core/context") const { doInTenant } = require("@budibase/backend-core/tenancy") -const { quotas, QuotaUsageType, StaticQuotaName, MonthlyQuotaName } = require("@budibase/pro") +const { + quotas, + QuotaUsageType, + StaticQuotaName, + MonthlyQuotaName, +} = require("@budibase/pro") describe("/rows", () => { let request = setup.getRequest() @@ -23,23 +28,30 @@ describe("/rows", () => { await request .get(`/api/${table._id}/rows/${id}`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(status) const getRowUsage = async () => { - return config.doInContext(null, () => quotas.getCurrentUsageValue(QuotaUsageType.STATIC, StaticQuotaName.ROWS)) + return config.doInContext(null, () => + quotas.getCurrentUsageValue(QuotaUsageType.STATIC, StaticQuotaName.ROWS) + ) } const getQueryUsage = async () => { - return config.doInContext(null, () => quotas.getCurrentUsageValue(QuotaUsageType.MONTHLY, MonthlyQuotaName.QUERIES)) + return config.doInContext(null, () => + quotas.getCurrentUsageValue( + QuotaUsageType.MONTHLY, + MonthlyQuotaName.QUERIES + ) + ) } - const assertRowUsage = async (expected) => { + const assertRowUsage = async expected => { const usage = await getRowUsage() expect(usage).toBe(expected) } - const assertQueryUsage = async (expected) => { + const assertQueryUsage = async expected => { const usage = await getQueryUsage() expect(usage).toBe(expected) } @@ -76,10 +88,12 @@ describe("/rows", () => { name: "Updated Name", }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) - expect(res.res.statusMessage).toEqual(`${table.name} updated successfully.`) + expect(res.res.statusMessage).toEqual( + `${table.name} updated successfully.` + ) expect(res.body.name).toEqual("Updated Name") // await assertRowUsage(rowUsage) // await assertQueryUsage(queryUsage + 1) @@ -92,7 +106,7 @@ describe("/rows", () => { const res = await request .get(`/api/${table._id}/rows/${existing._id}`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body).toEqual({ @@ -110,7 +124,7 @@ describe("/rows", () => { const newRow = { tableId: table._id, name: "Second Contact", - status: "new" + status: "new", } await config.createRow() await config.createRow(newRow) @@ -119,7 +133,7 @@ describe("/rows", () => { const res = await request .get(`/api/${table._id}/rows`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body.length).toBe(2) @@ -135,17 +149,36 @@ describe("/rows", () => { await request .get(`/api/${table._id}/rows/not-a-valid-id`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(404) await assertQueryUsage(queryUsage) // no change }) it("row values are coerced", async () => { - const str = {type:"string", constraints: { type: "string", presence: false }} - const attachment = {type:"attachment", constraints: { type: "array", presence: false }} - const bool = {type:"boolean", constraints: { type: "boolean", presence: false }} - const number = {type:"number", constraints: { type: "number", presence: false }} - const datetime = {type:"datetime", constraints: { type: "string", presence: false, datetime: {earliest:"", latest: ""} }} + const str = { + type: "string", + constraints: { type: "string", presence: false }, + } + const attachment = { + type: "attachment", + constraints: { type: "array", presence: false }, + } + const bool = { + type: "boolean", + constraints: { type: "boolean", presence: false }, + } + const number = { + type: "number", + constraints: { type: "number", presence: false }, + } + const datetime = { + type: "datetime", + constraints: { + type: "string", + presence: false, + datetime: { earliest: "", latest: "" }, + }, + } table = await config.createTable({ name: "TestTable2", @@ -171,9 +204,9 @@ describe("/rows", () => { boolUndefined: bool, boolString: bool, boolBool: bool, - attachmentNull : attachment, - attachmentUndefined : attachment, - attachmentEmpty : attachment, + attachmentNull: attachment, + attachmentUndefined: attachment, + attachmentEmpty: attachment, }, }) @@ -198,9 +231,9 @@ describe("/rows", () => { boolString: "true", boolBool: true, tableId: table._id, - attachmentNull : null, - attachmentUndefined : undefined, - attachmentEmpty : "", + attachmentNull: null, + attachmentUndefined: undefined, + attachmentEmpty: "", } const id = (await config.createRow(row))._id @@ -218,7 +251,9 @@ describe("/rows", () => { expect(saved.datetimeEmptyString).toBe(null) expect(saved.datetimeNull).toBe(null) expect(saved.datetimeUndefined).toBe(undefined) - expect(saved.datetimeString).toBe(new Date(row.datetimeString).toISOString()) + expect(saved.datetimeString).toBe( + new Date(row.datetimeString).toISOString() + ) expect(saved.datetimeDate).toBe(row.datetimeDate.toISOString()) expect(saved.boolNull).toBe(null) expect(saved.boolEmpty).toBe(null) @@ -247,10 +282,12 @@ describe("/rows", () => { name: "Updated Name", }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) - - expect(res.res.statusMessage).toEqual(`${table.name} updated successfully.`) + + expect(res.res.statusMessage).toEqual( + `${table.name} updated successfully.` + ) expect(res.body.name).toEqual("Updated Name") expect(res.body.description).toEqual(existing.description) @@ -292,16 +329,14 @@ describe("/rows", () => { const res = await request .delete(`/api/${table._id}/rows`) .send({ - rows: [ - createdRow - ] + rows: [createdRow], }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body[0]._id).toEqual(createdRow._id) - await assertRowUsage(rowUsage -1) - await assertQueryUsage(queryUsage +1) + await assertRowUsage(rowUsage - 1) + await assertQueryUsage(queryUsage + 1) }) }) @@ -314,9 +349,9 @@ describe("/rows", () => { .post(`/api/${table._id}/rows/validate`) .send({ name: "ivan" }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) - + expect(res.body.valid).toBe(true) expect(Object.keys(res.body.errors)).toEqual([]) await assertRowUsage(rowUsage) @@ -331,9 +366,9 @@ describe("/rows", () => { .post(`/api/${table._id}/rows/validate`) .send({ name: 1 }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) - + expect(res.body.valid).toBe(false) expect(Object.keys(res.body.errors)).toEqual(["name"]) await assertRowUsage(rowUsage) @@ -351,19 +386,16 @@ describe("/rows", () => { const res = await request .delete(`/api/${table._id}/rows`) .send({ - rows: [ - row1, - row2, - ] + rows: [row1, row2], }) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body.length).toEqual(2) await loadRow(row1._id, 404) await assertRowUsage(rowUsage - 2) - await assertQueryUsage(queryUsage +1) + await assertQueryUsage(queryUsage + 1) }) }) @@ -376,12 +408,12 @@ describe("/rows", () => { const res = await request .get(`/api/views/${table._id}`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body.length).toEqual(1) expect(res.body[0]._id).toEqual(row._id) await assertRowUsage(rowUsage) - await assertQueryUsage(queryUsage +1) + await assertQueryUsage(queryUsage + 1) }) it("should throw an error if view doesn't exist", async () => { @@ -406,7 +438,7 @@ describe("/rows", () => { const res = await request .get(`/api/views/${view.name}`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(res.body.length).toEqual(1) expect(res.body[0]._id).toEqual(row._id) @@ -418,21 +450,24 @@ describe("/rows", () => { describe("fetchEnrichedRows", () => { it("should allow enriching some linked rows", async () => { - const { table, firstRow, secondRow } = await doInTenant(setup.structures.TENANT_ID, async () => { - const table = await config.createLinkedTable() - const firstRow = await config.createRow({ - name: "Test Contact", - description: "original description", - tableId: table._id - }) - const secondRow = await config.createRow({ - name: "Test 2", - description: "og desc", - link: [{_id: firstRow._id}], - tableId: table._id, - }) - return { table, firstRow, secondRow } - }) + const { table, firstRow, secondRow } = await doInTenant( + setup.structures.TENANT_ID, + async () => { + const table = await config.createLinkedTable() + const firstRow = await config.createRow({ + name: "Test Contact", + description: "original description", + tableId: table._id, + }) + const secondRow = await config.createRow({ + name: "Test 2", + description: "og desc", + link: [{ _id: firstRow._id }], + tableId: table._id, + }) + return { table, firstRow, secondRow } + } + ) const rowUsage = await getRowUsage() const queryUsage = await getQueryUsage() @@ -440,7 +475,7 @@ describe("/rows", () => { const resBasic = await request .get(`/api/${table._id}/rows/${secondRow._id}`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(resBasic.body.link[0]._id).toBe(firstRow._id) expect(resBasic.body.link[0].primaryDisplay).toBe("Test Contact") @@ -449,14 +484,14 @@ describe("/rows", () => { const resEnriched = await request .get(`/api/${table._id}/${secondRow._id}/enrich`) .set(config.defaultHeaders()) - .expect('Content-Type', /json/) + .expect("Content-Type", /json/) .expect(200) expect(resEnriched.body.link.length).toBe(1) expect(resEnriched.body.link[0]._id).toBe(firstRow._id) expect(resEnriched.body.link[0].name).toBe("Test Contact") expect(resEnriched.body.link[0].description).toBe("original description") await assertRowUsage(rowUsage) - await assertQueryUsage(queryUsage +2) + await assertQueryUsage(queryUsage + 2) }) }) @@ -466,9 +501,11 @@ describe("/rows", () => { const row = await config.createRow({ name: "test", description: "test", - attachment: [{ - key: `${config.getAppId()}/attachments/test/thing.csv`, - }], + attachment: [ + { + key: `${config.getAppId()}/attachments/test/thing.csv`, + }, + ], tableId: table._id, }) // the environment needs configured for this @@ -482,4 +519,49 @@ describe("/rows", () => { }) }) }) + + describe("exportData", () => { + it("should allow exporting all columns", async () => { + const existing = await config.createRow() + const res = await request + .post(`/api/${table._id}/rows/exportRows?format=json`) + .set(config.defaultHeaders()) + .send({ + rows: [existing._id], + }) + .expect("Content-Type", /json/) + .expect(200) + const results = JSON.parse(res.text) + expect(results.length).toEqual(1) + const row = results[0] + + // Ensure all original columns were exported + expect(Object.keys(row).length).toBeGreaterThanOrEqual( + Object.keys(existing).length + ) + Object.keys(existing).forEach(key => { + expect(row[key]).toEqual(existing[key]) + }) + }) + + it("should allow exporting only certain columns", async () => { + const existing = await config.createRow() + const res = await request + .post(`/api/${table._id}/rows/exportRows?format=json`) + .set(config.defaultHeaders()) + .send({ + rows: [existing._id], + columns: ["_id"], + }) + .expect("Content-Type", /json/) + .expect(200) + const results = JSON.parse(res.text) + expect(results.length).toEqual(1) + const row = results[0] + + // Ensure only the _id column was exported + expect(Object.keys(row).length).toEqual(1) + expect(row._id).toEqual(existing._id) + }) + }) })