2023-01-17 18:22:31 +01:00
|
|
|
import { faker } from "@faker-js/faker"
|
|
|
|
import {
|
|
|
|
generateMakeRequest,
|
|
|
|
MakeRequestResponse,
|
|
|
|
} from "../api/routes/public/tests/utils"
|
|
|
|
|
|
|
|
import * as setup from "../api/routes/tests/utilities"
|
2023-01-18 17:46:40 +01:00
|
|
|
import { Datasource, FieldType, SourceName, Table } from "@budibase/types"
|
2023-01-18 18:07:09 +01:00
|
|
|
import _ from "lodash"
|
2023-01-17 18:22:31 +01:00
|
|
|
|
|
|
|
const config = setup.getConfig()
|
2023-01-18 14:55:24 +01:00
|
|
|
|
2023-01-19 12:00:51 +01:00
|
|
|
jest.unmock("node-fetch")
|
|
|
|
|
2023-01-18 18:07:09 +01:00
|
|
|
describe("row api - postgres", () => {
|
|
|
|
let apiKey,
|
|
|
|
makeRequest: MakeRequestResponse,
|
|
|
|
postgresDatasource: Datasource,
|
|
|
|
postgresTable: Table
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
await config.init()
|
|
|
|
apiKey = await config.generateApiKey()
|
|
|
|
postgresDatasource = await config.createDatasource({
|
|
|
|
type: "datasource",
|
|
|
|
source: SourceName.POSTGRES,
|
|
|
|
plus: true,
|
|
|
|
config: {
|
|
|
|
host: "192.168.1.98",
|
|
|
|
port: 54321,
|
|
|
|
database: "postgres",
|
|
|
|
user: "root",
|
|
|
|
password: "root",
|
|
|
|
schema: "public",
|
|
|
|
ssl: false,
|
|
|
|
rejectUnauthorized: false,
|
|
|
|
ca: false,
|
|
|
|
},
|
|
|
|
})
|
2023-01-18 17:46:40 +01:00
|
|
|
|
2023-01-18 18:07:09 +01:00
|
|
|
makeRequest = generateMakeRequest(apiKey)
|
|
|
|
|
|
|
|
postgresTable = await config.createTable({
|
|
|
|
name: faker.lorem.word(),
|
|
|
|
schema: {
|
|
|
|
name: {
|
|
|
|
name: "name",
|
|
|
|
type: FieldType.STRING,
|
|
|
|
constraints: {
|
|
|
|
presence: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
description: {
|
|
|
|
name: "description",
|
|
|
|
type: FieldType.STRING,
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
name: "value",
|
|
|
|
type: FieldType.NUMBER,
|
2023-01-18 17:46:40 +01:00
|
|
|
},
|
|
|
|
},
|
2023-01-18 18:07:09 +01:00
|
|
|
sourceId: postgresDatasource._id,
|
|
|
|
})
|
2023-01-18 17:46:40 +01:00
|
|
|
})
|
2023-01-17 18:22:31 +01:00
|
|
|
|
2023-01-18 18:07:09 +01:00
|
|
|
afterAll(async () => {
|
|
|
|
await config.end()
|
|
|
|
})
|
|
|
|
|
|
|
|
function createRandomRow() {
|
|
|
|
return {
|
|
|
|
name: faker.name.fullName(),
|
|
|
|
description: faker.lorem.paragraphs(),
|
|
|
|
value: +faker.random.numeric(),
|
|
|
|
}
|
|
|
|
}
|
2023-01-17 18:22:31 +01:00
|
|
|
|
2023-01-19 12:35:00 +01:00
|
|
|
function createRow(
|
|
|
|
row: {
|
|
|
|
name: string
|
|
|
|
description: string
|
|
|
|
value: number
|
|
|
|
},
|
|
|
|
tableId?: string
|
|
|
|
) {
|
|
|
|
return config.createRow({
|
|
|
|
tableId: tableId || postgresTable._id,
|
|
|
|
...row,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-01-19 12:06:41 +01:00
|
|
|
async function populateRows(count: number, tableId?: string) {
|
2023-01-18 18:07:09 +01:00
|
|
|
return await Promise.all(
|
|
|
|
Array(count)
|
|
|
|
.fill({})
|
|
|
|
.map(async () => {
|
|
|
|
const rowData = createRandomRow()
|
|
|
|
return {
|
|
|
|
rowData,
|
2023-01-19 12:35:00 +01:00
|
|
|
row: await createRow(rowData, tableId || postgresTable._id),
|
2023-01-18 18:07:09 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
2023-01-18 17:46:40 +01:00
|
|
|
}
|
|
|
|
|
2023-01-17 18:22:31 +01:00
|
|
|
describe("create a row", () => {
|
2023-01-18 18:07:09 +01:00
|
|
|
test("Given than no row exists, adding a new row persists it", async () => {
|
2023-01-18 17:46:40 +01:00
|
|
|
const newRow = createRandomRow()
|
2023-01-17 18:22:31 +01:00
|
|
|
|
2023-01-18 17:46:40 +01:00
|
|
|
const res = await makeRequest(
|
|
|
|
"post",
|
|
|
|
`/tables/${postgresTable._id}/rows`,
|
|
|
|
newRow
|
|
|
|
)
|
2023-01-17 18:22:31 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
2023-01-17 18:39:59 +01:00
|
|
|
|
2023-01-18 17:46:40 +01:00
|
|
|
const persistedRows = await config.getRows(postgresTable._id!)
|
2023-01-17 18:39:59 +01:00
|
|
|
expect(persistedRows).toHaveLength(1)
|
|
|
|
expect(persistedRows).toEqual([
|
|
|
|
expect.objectContaining({
|
|
|
|
...res.body.data,
|
|
|
|
...newRow,
|
|
|
|
}),
|
|
|
|
])
|
2023-01-17 18:22:31 +01:00
|
|
|
})
|
2023-01-18 13:26:26 +01:00
|
|
|
|
|
|
|
test("Given than no row exists, multiple rows can be persisted", async () => {
|
|
|
|
const numberOfRows = 10
|
2023-01-18 17:46:40 +01:00
|
|
|
const newRows = Array(numberOfRows).fill(createRandomRow())
|
2023-01-18 13:26:26 +01:00
|
|
|
|
|
|
|
for (const newRow of newRows) {
|
|
|
|
const res = await makeRequest(
|
|
|
|
"post",
|
2023-01-18 17:46:40 +01:00
|
|
|
`/tables/${postgresTable._id}/rows`,
|
2023-01-18 13:26:26 +01:00
|
|
|
newRow
|
|
|
|
)
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
}
|
|
|
|
|
2023-01-18 17:46:40 +01:00
|
|
|
const persistedRows = await config.getRows(postgresTable._id!)
|
2023-01-18 13:26:26 +01:00
|
|
|
expect(persistedRows).toHaveLength(numberOfRows)
|
2023-01-18 17:06:45 +01:00
|
|
|
expect(persistedRows).toEqual(
|
|
|
|
expect.arrayContaining(newRows.map(expect.objectContaining))
|
|
|
|
)
|
2023-01-18 13:26:26 +01:00
|
|
|
})
|
2023-01-17 18:22:31 +01:00
|
|
|
})
|
2023-01-18 17:46:40 +01:00
|
|
|
|
2023-01-18 18:11:52 +01:00
|
|
|
describe("update a row", () => {
|
|
|
|
test("Given than a row exists, updating it persists it", async () => {
|
|
|
|
let { rowData, row } = _.sample(await populateRows(10))!
|
|
|
|
|
|
|
|
const newName = faker.random.words(3)
|
|
|
|
const newValue = +faker.random.numeric()
|
|
|
|
const updateRow = {
|
|
|
|
name: newName,
|
|
|
|
value: newValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
const res = await makeRequest(
|
|
|
|
"put",
|
|
|
|
`/tables/${postgresTable._id}/rows/${row._id}`,
|
|
|
|
updateRow
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
2023-01-18 18:21:17 +01:00
|
|
|
const persistedRow = await config.getRow(postgresTable._id!, row._id!)
|
2023-01-18 18:11:52 +01:00
|
|
|
|
2023-01-18 18:21:17 +01:00
|
|
|
expect(persistedRow).toEqual(
|
2023-01-18 18:11:52 +01:00
|
|
|
expect.objectContaining({
|
|
|
|
...res.body.data,
|
|
|
|
...rowData,
|
|
|
|
...updateRow,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-01-18 18:21:17 +01:00
|
|
|
describe("delete a row", () => {
|
|
|
|
test("Given than a row exists, delete request removes it", async () => {
|
|
|
|
const numberOfInitialRows = 5
|
|
|
|
let { row } = _.sample(await populateRows(numberOfInitialRows))!
|
|
|
|
|
|
|
|
const res = await makeRequest(
|
|
|
|
"delete",
|
|
|
|
`/tables/${postgresTable._id}/rows/${row._id}`
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
const persistedRows = await config.getRows(postgresTable._id!)
|
|
|
|
expect(persistedRows).toHaveLength(numberOfInitialRows - 1)
|
|
|
|
|
|
|
|
expect(row._id).toBeDefined()
|
|
|
|
expect(persistedRows).not.toContain(
|
|
|
|
expect.objectContaining({ _id: row._id })
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-01-18 18:07:09 +01:00
|
|
|
describe("retrieve a row", () => {
|
2023-01-18 17:46:40 +01:00
|
|
|
test("Given than a table have a single row, the row can be retrieved successfully", async () => {
|
2023-01-18 18:07:09 +01:00
|
|
|
const [{ rowData, row }] = await populateRows(1)
|
2023-01-18 17:46:40 +01:00
|
|
|
|
|
|
|
const res = await makeRequest(
|
|
|
|
"get",
|
|
|
|
`/tables/${postgresTable._id}/rows/${row._id}`
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
expect(res.body.data).toEqual(expect.objectContaining(rowData))
|
|
|
|
})
|
2023-01-18 17:48:18 +01:00
|
|
|
|
|
|
|
test("Given than a table have a multiple rows, a single row can be retrieved successfully", async () => {
|
2023-01-18 18:07:09 +01:00
|
|
|
const rows = await populateRows(10)
|
|
|
|
const { rowData, row } = _.sample(rows)!
|
2023-01-18 17:48:18 +01:00
|
|
|
|
|
|
|
const res = await makeRequest(
|
|
|
|
"get",
|
|
|
|
`/tables/${postgresTable._id}/rows/${row._id}`
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
expect(res.body.data).toEqual(expect.objectContaining(rowData))
|
|
|
|
})
|
2023-01-18 17:46:40 +01:00
|
|
|
})
|
2023-01-19 12:00:51 +01:00
|
|
|
|
|
|
|
describe("search for rows", () => {
|
2023-01-19 12:44:48 +01:00
|
|
|
const search = (tableId: string | undefined, body?: object) =>
|
|
|
|
makeRequest("post", `/tables/${postgresTable._id}/rows/search`, body)
|
|
|
|
|
2023-01-19 12:20:20 +01:00
|
|
|
describe("empty search", () => {
|
|
|
|
test("Given than a table has no rows, search without query returns empty", async () => {
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id)
|
2023-01-19 12:06:41 +01:00
|
|
|
|
2023-01-19 12:20:20 +01:00
|
|
|
expect(res.status).toBe(200)
|
2023-01-19 12:06:41 +01:00
|
|
|
|
2023-01-19 12:20:20 +01:00
|
|
|
expect(res.body.data).toHaveLength(0)
|
|
|
|
})
|
2023-01-19 12:06:41 +01:00
|
|
|
|
2023-01-19 12:20:20 +01:00
|
|
|
test("Given than a table has multiple rows, search without query returns all of them", async () => {
|
|
|
|
const rowsCount = 6
|
|
|
|
const rows = await populateRows(rowsCount)
|
2023-01-19 12:00:51 +01:00
|
|
|
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id)
|
2023-01-19 12:00:51 +01:00
|
|
|
|
2023-01-19 12:20:20 +01:00
|
|
|
expect(res.status).toBe(200)
|
2023-01-19 12:00:51 +01:00
|
|
|
|
2023-01-19 12:20:20 +01:00
|
|
|
expect(res.body.data).toHaveLength(rowsCount)
|
|
|
|
expect(res.body.data).toEqual(
|
|
|
|
expect.arrayContaining(
|
|
|
|
rows.map(r => expect.objectContaining(r.rowData))
|
|
|
|
)
|
2023-01-19 12:00:51 +01:00
|
|
|
)
|
2023-01-19 12:20:20 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
test("Given than multiple tables have multiple rows, search only return the requested ones", async () => {
|
|
|
|
await populateRows(2, (await config.createTable())._id)
|
|
|
|
const rowsCount = 6
|
|
|
|
await populateRows(rowsCount)
|
|
|
|
await populateRows(2, (await config.createTable())._id)
|
|
|
|
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id)
|
2023-01-19 12:20:20 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
expect(res.body.data).toHaveLength(rowsCount)
|
|
|
|
})
|
2023-01-19 12:00:51 +01:00
|
|
|
})
|
2023-01-19 12:06:41 +01:00
|
|
|
|
2023-01-19 12:35:00 +01:00
|
|
|
test("Querying by a string field returns the rows with field containing or starting by that value", async () => {
|
2023-01-19 12:20:20 +01:00
|
|
|
const name = faker.name.fullName()
|
|
|
|
const rowsToFilter = [
|
|
|
|
...Array(2).fill({
|
|
|
|
name,
|
|
|
|
description: faker.lorem.paragraphs(),
|
|
|
|
value: +faker.random.numeric(),
|
|
|
|
}),
|
|
|
|
...Array(2).fill({
|
|
|
|
name: `${name}${faker.random.alphaNumeric(5)}`,
|
|
|
|
description: faker.lorem.paragraphs(),
|
|
|
|
value: +faker.random.numeric(),
|
|
|
|
}),
|
|
|
|
]
|
|
|
|
|
|
|
|
await populateRows(3)
|
|
|
|
for (const row of rowsToFilter) {
|
2023-01-19 12:35:00 +01:00
|
|
|
await createRow(row, postgresTable._id)
|
2023-01-19 12:20:20 +01:00
|
|
|
}
|
|
|
|
await populateRows(1)
|
2023-01-19 12:06:41 +01:00
|
|
|
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id, {
|
|
|
|
query: {
|
|
|
|
string: {
|
|
|
|
name,
|
2023-01-19 12:20:20 +01:00
|
|
|
},
|
2023-01-19 12:44:48 +01:00
|
|
|
},
|
|
|
|
})
|
2023-01-19 12:06:41 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
2023-01-19 12:20:20 +01:00
|
|
|
expect(res.body.data).toHaveLength(4)
|
|
|
|
expect(res.body.data).toEqual(
|
|
|
|
expect.arrayContaining(rowsToFilter.map(expect.objectContaining))
|
|
|
|
)
|
2023-01-19 12:06:41 +01:00
|
|
|
})
|
2023-01-19 12:35:00 +01:00
|
|
|
|
|
|
|
test("Querying respects the limit fields", async () => {
|
|
|
|
await populateRows(6)
|
|
|
|
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id, {
|
|
|
|
limit: 2,
|
|
|
|
})
|
2023-01-19 12:35:00 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
expect(res.body.data).toHaveLength(2)
|
|
|
|
})
|
|
|
|
|
|
|
|
describe("sort", () => {
|
|
|
|
beforeEach(async () => {
|
|
|
|
const defaultValue = createRandomRow()
|
|
|
|
|
|
|
|
await createRow(
|
|
|
|
{
|
|
|
|
...defaultValue,
|
|
|
|
name: "d",
|
|
|
|
value: 3,
|
|
|
|
},
|
|
|
|
postgresTable._id
|
|
|
|
)
|
|
|
|
await createRow(
|
|
|
|
{ ...defaultValue, name: "aaa", value: 40 },
|
|
|
|
postgresTable._id
|
|
|
|
)
|
|
|
|
await createRow(
|
|
|
|
{ ...defaultValue, name: "ccccc", value: -5 },
|
|
|
|
postgresTable._id
|
|
|
|
)
|
|
|
|
await createRow(
|
|
|
|
{ ...defaultValue, name: "bb", value: 0 },
|
|
|
|
postgresTable._id
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
test("Querying respects the sort order when sorting ascending by a string value", async () => {
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id, {
|
|
|
|
sort: {
|
|
|
|
order: "ascending",
|
|
|
|
column: "name",
|
|
|
|
type: "string",
|
|
|
|
},
|
|
|
|
})
|
2023-01-19 12:35:00 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
expect(res.body.data).toEqual([
|
|
|
|
expect.objectContaining({ name: "aaa" }),
|
|
|
|
expect.objectContaining({ name: "bb" }),
|
|
|
|
expect.objectContaining({ name: "ccccc" }),
|
|
|
|
expect.objectContaining({ name: "d" }),
|
|
|
|
])
|
|
|
|
})
|
|
|
|
|
|
|
|
test("Querying respects the sort order when sorting descending by a string value", async () => {
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id, {
|
|
|
|
sort: {
|
|
|
|
order: "descending",
|
|
|
|
column: "name",
|
|
|
|
type: "string",
|
|
|
|
},
|
|
|
|
})
|
2023-01-19 12:35:00 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
expect(res.body.data).toEqual([
|
|
|
|
expect.objectContaining({ name: "d" }),
|
|
|
|
expect.objectContaining({ name: "ccccc" }),
|
|
|
|
expect.objectContaining({ name: "bb" }),
|
|
|
|
expect.objectContaining({ name: "aaa" }),
|
|
|
|
])
|
|
|
|
})
|
|
|
|
|
|
|
|
test("Querying respects the sort order when sorting ascending by a numeric value", async () => {
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id, {
|
|
|
|
sort: {
|
|
|
|
order: "ascending",
|
|
|
|
column: "value",
|
|
|
|
type: "number",
|
|
|
|
},
|
|
|
|
})
|
2023-01-19 12:35:00 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
expect(res.body.data).toEqual([
|
|
|
|
expect.objectContaining({ value: -5 }),
|
|
|
|
expect.objectContaining({ value: 0 }),
|
|
|
|
expect.objectContaining({ value: 3 }),
|
|
|
|
expect.objectContaining({ value: 40 }),
|
|
|
|
])
|
|
|
|
})
|
|
|
|
|
|
|
|
test("Querying respects the sort order when sorting descending by a numeric value", async () => {
|
2023-01-19 12:44:48 +01:00
|
|
|
const res = await search(postgresTable._id, {
|
|
|
|
sort: {
|
|
|
|
order: "descending",
|
|
|
|
column: "value",
|
|
|
|
type: "number",
|
|
|
|
},
|
|
|
|
})
|
2023-01-19 12:35:00 +01:00
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
expect(res.body.data).toEqual([
|
|
|
|
expect.objectContaining({ value: 40 }),
|
|
|
|
expect.objectContaining({ value: 3 }),
|
|
|
|
expect.objectContaining({ value: 0 }),
|
|
|
|
expect.objectContaining({ value: -5 }),
|
|
|
|
])
|
|
|
|
})
|
|
|
|
})
|
2023-01-19 12:00:51 +01:00
|
|
|
})
|
2023-01-19 12:44:48 +01:00
|
|
|
|
|
|
|
describe("get all rows", () => {
|
|
|
|
const getAll = (tableId: string | undefined) =>
|
|
|
|
makeRequest("get", `/tables/${postgresTable._id}/rows`)
|
|
|
|
|
|
|
|
test("Given than a table has no rows, get returns empty", async () => {
|
|
|
|
const res = await getAll(postgresTable._id)
|
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
expect(res.body.data).toHaveLength(0)
|
|
|
|
})
|
|
|
|
|
|
|
|
test("Given than a table has multiple rows, get returns all of them", async () => {
|
|
|
|
const rowsCount = 6
|
|
|
|
const rows = await populateRows(rowsCount)
|
|
|
|
|
|
|
|
const res = await getAll(postgresTable._id)
|
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
expect(res.body.data).toHaveLength(rowsCount)
|
|
|
|
expect(res.body.data).toEqual(
|
|
|
|
expect.arrayContaining(
|
|
|
|
rows.map(r => expect.objectContaining(r.rowData))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
test("Given than multiple tables have multiple rows, get returns the requested ones", async () => {
|
|
|
|
await populateRows(2, (await config.createTable())._id)
|
|
|
|
const rowsCount = 6
|
|
|
|
await populateRows(rowsCount)
|
|
|
|
await populateRows(2, (await config.createTable())._id)
|
|
|
|
|
|
|
|
const res = await getAll(postgresTable._id)
|
|
|
|
|
|
|
|
expect(res.status).toBe(200)
|
|
|
|
|
|
|
|
expect(res.body.data).toHaveLength(rowsCount)
|
|
|
|
})
|
|
|
|
})
|
2023-01-17 18:22:31 +01:00
|
|
|
})
|