Tidy tests

This commit is contained in:
adrinr 2023-02-07 10:46:34 +00:00
parent 7474099bac
commit 79e42abc1e
2 changed files with 153 additions and 128 deletions

View File

@ -26,7 +26,7 @@ jest.unmock("pg")
describe("row api - postgres", () => { describe("row api - postgres", () => {
let makeRequest: MakeRequestResponse, let makeRequest: MakeRequestResponse,
postgresDatasource: Datasource, postgresDatasource: Datasource,
postgresTable: Table, primaryPostgresTable: Table,
auxPostgresTable: Table auxPostgresTable: Table
let host: string let host: string
@ -90,7 +90,7 @@ describe("row api - postgres", () => {
sourceId: postgresDatasource._id, sourceId: postgresDatasource._id,
}) })
postgresTable = await config.createTable({ primaryPostgresTable = await config.createTable({
name: generator.word({ length: 10 }), name: generator.word({ length: 10 }),
type: "external", type: "external",
primary: ["id"], primary: ["id"],
@ -137,7 +137,7 @@ describe("row api - postgres", () => {
await config.end() await config.end()
}) })
function makeRandomRow() { function generateRandomPrimaryRowData() {
return { return {
name: generator.name(), name: generator.name(),
description: generator.paragraph(), description: generator.paragraph(),
@ -145,31 +145,34 @@ describe("row api - postgres", () => {
} }
} }
async function createRow( async function createPrimaryRow(opts: {
row: { rowData: {
name: string name: string
description: string description: string
value: number value: number
}, }
tableId?: string,
createForeignRow?: boolean createForeignRow?: boolean
) { }) {
if (createForeignRow) { let { rowData } = opts
const foreignRow = await config.createRow({ let foreignRow: Row | undefined
if (opts?.createForeignRow) {
foreignRow = await config.createRow({
tableId: auxPostgresTable._id, tableId: auxPostgresTable._id,
title: generator.name(), title: generator.name(),
}) })
row = { rowData = {
...row, ...rowData,
[`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id, [`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id,
} }
} }
return await config.createRow({ const row = await config.createRow({
tableId: tableId || postgresTable._id, tableId: primaryPostgresTable._id,
...row, ...rowData,
}) })
return { row, foreignRow }
} }
async function createDefaultPgTable() { async function createDefaultPgTable() {
@ -190,21 +193,23 @@ describe("row api - postgres", () => {
}) })
} }
async function populateRows(count: number, tableId?: string) { async function populatePrimaryRows(count: number) {
return await Promise.all( return await Promise.all(
Array(count) Array(count)
.fill({}) .fill({})
.map(async () => { .map(async () => {
const rowData = makeRandomRow() const rowData = generateRandomPrimaryRowData()
return { return {
rowData, rowData,
row: await createRow(rowData, tableId || postgresTable._id), ...(await createPrimaryRow({
rowData,
})),
} }
}) })
) )
} }
test("validate table schema", async () => { it("validate table schema", async () => {
const res = await makeRequest( const res = await makeRequest(
"get", "get",
`/api/datasources/${postgresDatasource._id}` `/api/datasources/${postgresDatasource._id}`
@ -234,18 +239,18 @@ describe("row api - postgres", () => {
}) })
}) })
describe("create a row", () => { describe("POST /api/:tableId/rows", () => {
const createRow = (tableId: string | undefined, body: object) => const createRow = (tableId: string | undefined, body: object) =>
makeRequest("post", `/api/${tableId}/rows`, body) makeRequest("post", `/api/${tableId}/rows`, body)
test("Given than no row exists, adding a new row persists it", async () => { it("Given than no row exists, adding a new one persists it", async () => {
const newRow = makeRandomRow() const newRow = generateRandomPrimaryRowData()
const res = await createRow(postgresTable._id, newRow) const res = await createRow(primaryPostgresTable._id, newRow)
expect(res.status).toBe(200) expect(res.status).toBe(200)
const persistedRows = await config.getRows(postgresTable._id!) const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(1) expect(persistedRows).toHaveLength(1)
const expected = { const expected = {
@ -256,16 +261,16 @@ describe("row api - postgres", () => {
expect(persistedRows).toEqual([expect.objectContaining(expected)]) expect(persistedRows).toEqual([expect.objectContaining(expected)])
}) })
test("Given than no row exists, multiple rows can be persisted", async () => { it("Given than no row exists, multiple rows can be persisted", async () => {
const numberOfRows = 10 const numberOfRows = 10
const newRows = Array(numberOfRows).fill(makeRandomRow()) const newRows = Array(numberOfRows).fill(generateRandomPrimaryRowData())
for (const newRow of newRows) { for (const newRow of newRows) {
const res = await createRow(postgresTable._id, newRow) const res = await createRow(primaryPostgresTable._id, newRow)
expect(res.status).toBe(200) expect(res.status).toBe(200)
} }
const persistedRows = await config.getRows(postgresTable._id!) const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(numberOfRows) expect(persistedRows).toHaveLength(numberOfRows)
expect(persistedRows).toEqual( expect(persistedRows).toEqual(
expect.arrayContaining(newRows.map(expect.objectContaining)) expect.arrayContaining(newRows.map(expect.objectContaining))
@ -273,12 +278,12 @@ describe("row api - postgres", () => {
}) })
}) })
describe("update a row", () => { describe("PATCH /api/:tableId/rows", () => {
const updateRow = (tableId: string | undefined, body: Row) => const updateRow = (tableId: string | undefined, body: Row) =>
makeRequest("patch", `/api/${tableId}/rows`, body) makeRequest("patch", `/api/${tableId}/rows`, body)
test("Given than a row exists, updating it persists it", async () => { it("Given than a row exists, updating it persists it", async () => {
let { row } = _.sample(await populateRows(10))! let { row } = _.sample(await populatePrimaryRows(10))!
const newName = generator.name() const newName = generator.name()
const newValue = generator.age() const newValue = generator.age()
@ -288,12 +293,15 @@ describe("row api - postgres", () => {
value: newValue, value: newValue,
} }
const res = await updateRow(postgresTable._id, updatedRow) const res = await updateRow(primaryPostgresTable._id, updatedRow)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(res.body).toEqual(updatedRow) expect(res.body).toEqual(updatedRow)
const persistedRow = await config.getRow(postgresTable._id!, row.id) const persistedRow = await config.getRow(
primaryPostgresTable._id!,
row.id
)
expect(persistedRow).toEqual( expect(persistedRow).toEqual(
expect.objectContaining({ expect.objectContaining({
@ -305,21 +313,21 @@ describe("row api - postgres", () => {
}) })
}) })
describe("delete a row", () => { describe("DELETE /api/:tableId/rows", () => {
const deleteRow = ( const deleteRow = (
tableId: string | undefined, tableId: string | undefined,
body: Row | { rows: Row[] } body: Row | { rows: Row[] }
) => makeRequest("delete", `/api/${tableId}/rows`, body) ) => makeRequest("delete", `/api/${tableId}/rows`, body)
test("Given than a row exists, delete request removes it", async () => { it("Given than a row exists, delete request removes it", async () => {
const numberOfInitialRows = 5 const numberOfInitialRows = 5
let { row } = _.sample(await populateRows(numberOfInitialRows))! let { row } = _.sample(await populatePrimaryRows(numberOfInitialRows))!
const res = await deleteRow(postgresTable._id, row) const res = await deleteRow(primaryPostgresTable._id, row)
expect(res.status).toBe(200) expect(res.status).toBe(200)
const persistedRows = await config.getRows(postgresTable._id!) const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(numberOfInitialRows - 1) expect(persistedRows).toHaveLength(numberOfInitialRows - 1)
expect(row.id).toBeDefined() expect(row.id).toBeDefined()
@ -328,17 +336,18 @@ describe("row api - postgres", () => {
) )
}) })
test("Given than multiple rows exist, multiple rows can be removed", async () => { it("Given than multiple rows exist, multiple rows can be removed at once", async () => {
const numberOfInitialRows = 5 const numberOfInitialRows = 5
let rows = _.sampleSize(await populateRows(numberOfInitialRows), 3)!.map( let rows = _.sampleSize(
x => x.row await populatePrimaryRows(numberOfInitialRows),
) 3
)!.map(x => x.row)
const res = await deleteRow(postgresTable._id, { rows }) const res = await deleteRow(primaryPostgresTable._id, { rows })
expect(res.status).toBe(200) expect(res.status).toBe(200)
const persistedRows = await config.getRows(postgresTable._id!) const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(numberOfInitialRows - 3) expect(persistedRows).toHaveLength(numberOfInitialRows - 3)
for (const row of rows) { for (const row of rows) {
@ -349,33 +358,33 @@ describe("row api - postgres", () => {
}) })
}) })
describe("retrieve a row", () => { describe("GET /api/:tableId/rows/:rowId", () => {
const getRow = (tableId: string | undefined, rowId?: string | undefined) => const getRow = (tableId: string | undefined, rowId?: string | undefined) =>
makeRequest("get", `/api/${tableId}/rows/${rowId}`) makeRequest("get", `/api/${tableId}/rows/${rowId}`)
test("Given than a table have a single row, the row can be retrieved successfully", async () => { it("Given than a table have a single row, that row can be retrieved successfully", async () => {
const [{ rowData, row }] = await populateRows(1) const [{ rowData, row }] = await populatePrimaryRows(1)
const res = await getRow(postgresTable._id, row.id) const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(res.body).toEqual(expect.objectContaining(rowData)) expect(res.body).toEqual(expect.objectContaining(rowData))
}) })
test("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => { it("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => {
const rows = await populateRows(10) const rows = await populatePrimaryRows(10)
const { rowData, row } = _.sample(rows)! const { rowData, row } = _.sample(rows)!
const res = await getRow(postgresTable._id, row.id) const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(res.body).toEqual(expect.objectContaining(rowData)) expect(res.body).toEqual(expect.objectContaining(rowData))
}) })
test("given having rows with relation data, only the ids are retrieved", async () => { it("Given a rows with relation data, foreign key fields are not retrieved", async () => {
let [{ row }] = await populateRows(1) let [{ row }] = await populatePrimaryRows(1)
await config.createRow({ await config.createRow({
tableId: auxPostgresTable._id, tableId: auxPostgresTable._id,
@ -383,7 +392,7 @@ describe("row api - postgres", () => {
linkedField: row.id, linkedField: row.id,
}) })
const res = await getRow(postgresTable._id, row.id) const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
@ -396,13 +405,13 @@ describe("row api - postgres", () => {
}) })
}) })
describe("search for rows", () => { describe("POST /api/:tableId/search", () => {
const search = (tableId: string | undefined, body?: object) => const search = (tableId: string | undefined, body?: object) =>
makeRequest("post", `/api/${tableId}/search`, body) makeRequest("post", `/api/${tableId}/search`, body)
describe("empty search", () => { describe("empty search", () => {
test("Given than a table has no rows, search without query returns empty", async () => { it("Given than a table has no rows, search without query returns empty", async () => {
const res = await search(postgresTable._id) const res = await search(primaryPostgresTable._id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
@ -413,11 +422,11 @@ describe("row api - postgres", () => {
}) })
}) })
test("Given than a table has multiple rows, search without query returns all of them", async () => { it("Given than a table has multiple rows, search without query returns all of them", async () => {
const rowsCount = 6 const rowsCount = 6
const rows = await populateRows(rowsCount) const rows = await populatePrimaryRows(rowsCount)
const res = await search(postgresTable._id) const res = await search(primaryPostgresTable._id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
@ -431,13 +440,22 @@ describe("row api - postgres", () => {
expect(res.body.rows).toHaveLength(rowsCount) expect(res.body.rows).toHaveLength(rowsCount)
}) })
test("Given than multiple tables have multiple rows, search only return the requested ones", async () => { it("Given than multiple tables have multiple rows, search only return the requested ones", async () => {
await populateRows(2, (await createDefaultPgTable())._id) const createRandomTableWithRows = async () =>
const rowsCount = 6 await config.createRow({
await populateRows(rowsCount) tableId: (await createDefaultPgTable())._id,
await populateRows(2, (await createDefaultPgTable())._id) title: generator.name(),
})
const res = await search(postgresTable._id) await createRandomTableWithRows()
await createRandomTableWithRows()
const rowsCount = 6
await populatePrimaryRows(rowsCount)
await createRandomTableWithRows()
const res = await search(primaryPostgresTable._id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
@ -445,7 +463,7 @@ describe("row api - postgres", () => {
}) })
}) })
test("Querying by a string field returns the rows with field containing or starting by that value", async () => { it("Querying by a string field returns the rows with field containing or starting by that value", async () => {
const name = generator.name() const name = generator.name()
const rowsToFilter = [ const rowsToFilter = [
...Array(2).fill({ ...Array(2).fill({
@ -460,13 +478,15 @@ describe("row api - postgres", () => {
}), }),
] ]
await populateRows(3) await populatePrimaryRows(3)
for (const row of rowsToFilter) { for (const row of rowsToFilter) {
await createRow(row, postgresTable._id) await createPrimaryRow({
rowData: row,
})
} }
await populateRows(1) await populatePrimaryRows(1)
const res = await search(postgresTable._id, { const res = await search(primaryPostgresTable._id, {
query: { query: {
string: { string: {
name, name,
@ -484,10 +504,10 @@ describe("row api - postgres", () => {
expect(res.body.rows).toHaveLength(4) expect(res.body.rows).toHaveLength(4)
}) })
test("Querying respects the limit fields", async () => { it("Querying respects the limit fields", async () => {
await populateRows(6) await populatePrimaryRows(6)
const res = await search(postgresTable._id, { const res = await search(primaryPostgresTable._id, {
limit: 2, limit: 2,
}) })
@ -498,32 +518,28 @@ describe("row api - postgres", () => {
describe("sort", () => { describe("sort", () => {
beforeEach(async () => { beforeEach(async () => {
const defaultValue = makeRandomRow() const defaultValue = generateRandomPrimaryRowData()
await createRow( await createPrimaryRow({
{ rowData: {
...defaultValue, ...defaultValue,
name: "d", name: "d",
value: 3, value: 3,
}, },
postgresTable._id })
) await createPrimaryRow({
await createRow( rowData: { ...defaultValue, name: "aaa", value: 40 },
{ ...defaultValue, name: "aaa", value: 40 }, })
postgresTable._id await createPrimaryRow({
) rowData: { ...defaultValue, name: "ccccc", value: -5 },
await createRow( })
{ ...defaultValue, name: "ccccc", value: -5 }, await createPrimaryRow({
postgresTable._id rowData: { ...defaultValue, name: "bb", value: 0 },
) })
await createRow(
{ ...defaultValue, name: "bb", value: 0 },
postgresTable._id
)
}) })
test("Querying respects the sort order when sorting ascending by a string value", async () => { it("Querying respects the sort order when sorting ascending by a string value", async () => {
const res = await search(postgresTable._id, { const res = await search(primaryPostgresTable._id, {
sort: "name", sort: "name",
sortOrder: "ascending", sortOrder: "ascending",
sortType: "string", sortType: "string",
@ -538,8 +554,8 @@ describe("row api - postgres", () => {
]) ])
}) })
test("Querying respects the sort order when sorting descending by a string value", async () => { it("Querying respects the sort order when sorting descending by a string value", async () => {
const res = await search(postgresTable._id, { const res = await search(primaryPostgresTable._id, {
sort: "name", sort: "name",
sortOrder: "descending", sortOrder: "descending",
sortType: "string", sortType: "string",
@ -554,8 +570,8 @@ describe("row api - postgres", () => {
]) ])
}) })
test("Querying respects the sort order when sorting ascending by a numeric value", async () => { it("Querying respects the sort order when sorting ascending by a numeric value", async () => {
const res = await search(postgresTable._id, { const res = await search(primaryPostgresTable._id, {
sort: "value", sort: "value",
sortOrder: "ascending", sortOrder: "ascending",
sortType: "number", sortType: "number",
@ -570,8 +586,8 @@ describe("row api - postgres", () => {
]) ])
}) })
test("Querying respects the sort order when sorting descending by a numeric value", async () => { it("Querying respects the sort order when sorting descending by a numeric value", async () => {
const res = await search(postgresTable._id, { const res = await search(primaryPostgresTable._id, {
sort: "value", sort: "value",
sortOrder: "descending", sortOrder: "descending",
sortType: "number", sortType: "number",
@ -588,26 +604,21 @@ describe("row api - postgres", () => {
}) })
}) })
describe("get enriched row", () => { describe("GET /api/:tableId/:rowId/enrich", () => {
const getAll = (tableId: string | undefined, rowId: string | undefined) => const getAll = (tableId: string | undefined, rowId: string | undefined) =>
makeRequest("get", `/api/${tableId}/${rowId}/enrich`) makeRequest("get", `/api/${tableId}/${rowId}/enrich`)
test("given having rows with relation data, enrich populates the foreign field", async () => { it("Given a row with relation data, enrich populates the foreign field", async () => {
const foreignRow = await config.createRow({ const { row, foreignRow } = await createPrimaryRow({
tableId: auxPostgresTable._id, rowData: generateRandomPrimaryRowData(),
title: generator.name(), createForeignRow: true,
}) })
const rowData = { const res = await getAll(primaryPostgresTable._id, row.id)
...makeRandomRow(),
[`fk_${auxPostgresTable.name}_foreignField`]: foreignRow.id,
}
const row = await createRow(rowData)
const res = await getAll(postgresTable._id, row.id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(foreignRow).toBeDefined()
expect(res.body).toEqual({ expect(res.body).toEqual({
...row, ...row,
linkedField: [ linkedField: [
@ -619,23 +630,23 @@ describe("row api - postgres", () => {
}) })
}) })
describe("get all rows", () => { describe("GET /api/:tableId/rows", () => {
const getAll = (tableId: string | undefined) => const getAll = (tableId: string | undefined) =>
makeRequest("get", `/api/${tableId}/rows`) makeRequest("get", `/api/${tableId}/rows`)
test("Given than a table has no rows, get returns empty", async () => { it("Given than a table has no rows, get returns empty", async () => {
const res = await getAll(postgresTable._id) const res = await getAll(primaryPostgresTable._id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
expect(res.body).toHaveLength(0) expect(res.body).toHaveLength(0)
}) })
test("Given than a table has multiple rows, get returns all of them", async () => { it("Given than a table has multiple rows, get returns all of them", async () => {
const rowsCount = 6 const rowsCount = 6
const rows = await populateRows(rowsCount) const rows = await populatePrimaryRows(rowsCount)
const res = await getAll(postgresTable._id) const res = await getAll(primaryPostgresTable._id)
expect(res.status).toBe(200) expect(res.status).toBe(200)
@ -647,13 +658,19 @@ describe("row api - postgres", () => {
) )
}) })
test("Given than multiple tables have multiple rows, get returns the requested ones", async () => { it("Given than multiple tables have multiple rows, get returns the requested ones", async () => {
await populateRows(2, (await createDefaultPgTable())._id) const createRandomTableWithRows = async () =>
const rowsCount = 6 await config.createRow({
await populateRows(rowsCount, postgresTable._id) tableId: (await createDefaultPgTable())._id,
await populateRows(2, (await createDefaultPgTable())._id) title: generator.name(),
})
const res = await getAll(postgresTable._id) await createRandomTableWithRows()
const rowsCount = 6
await populatePrimaryRows(rowsCount)
await createRandomTableWithRows()
const res = await getAll(primaryPostgresTable._id)
expect(res.status).toBe(200) expect(res.status).toBe(200)

View File

@ -90,7 +90,7 @@ function parseFilters(filters: SearchFilters | undefined): SearchFilters {
function generateSelectStatement( function generateSelectStatement(
json: QueryJson, json: QueryJson,
knex: Knex, knex: Knex,
excludeJoinColumns = false opts?: { excludeJoinColumns: boolean }
): (string | Knex.Raw)[] | "*" { ): (string | Knex.Raw)[] | "*" {
const { resource, meta } = json const { resource, meta } = json
const schema = meta?.table?.schema const schema = meta?.table?.schema
@ -104,7 +104,7 @@ function generateSelectStatement(
const tableName = fieldNames[0] const tableName = fieldNames[0]
if ( if (
meta?.table?.name && meta?.table?.name &&
excludeJoinColumns && opts?.excludeJoinColumns &&
tableName !== meta.table.name tableName !== meta.table.name
) { ) {
return p return p
@ -417,7 +417,9 @@ class InternalBuilder {
} else { } else {
return query return query
.insert(parsedBody) .insert(parsedBody)
.returning(generateSelectStatement(json, knex, true)) .returning(
generateSelectStatement(json, knex, { excludeJoinColumns: true })
)
} }
} }
@ -504,7 +506,9 @@ class InternalBuilder {
} else { } else {
return query return query
.update(parsedBody) .update(parsedBody)
.returning(generateSelectStatement(json, knex, true)) .returning(
generateSelectStatement(json, knex, { excludeJoinColumns: true })
)
} }
} }
@ -519,7 +523,11 @@ class InternalBuilder {
if (opts.disableReturning) { if (opts.disableReturning) {
return query.delete() return query.delete()
} else { } else {
return query.delete().returning(generateSelectStatement(json, knex, true)) return query
.delete()
.returning(
generateSelectStatement(json, knex, { excludeJoinColumns: true })
)
} }
} }
} }