1
0
Fork 0
mirror of synced 2024-09-21 20:01:32 +12:00
budibase/packages/server/src/integration-test/postgres.spec.ts

1043 lines
30 KiB
TypeScript
Raw Normal View History

import fetch from "node-fetch"
// @ts-ignore
fetch.mockSearch()
2023-01-18 06:22:31 +13:00
import {
generateMakeRequest,
MakeRequestResponse,
} from "../api/routes/public/tests/utils"
import * as setup from "../api/routes/tests/utilities"
2023-01-20 09:09:39 +13:00
import {
Datasource,
FieldType,
RelationshipType,
2023-01-20 09:09:39 +13:00
Row,
Table,
} from "@budibase/types"
2023-01-19 06:07:09 +13:00
import _ from "lodash"
import { generator } from "@budibase/backend-core/tests"
2023-02-02 23:43:18 +13:00
import { utils } from "@budibase/backend-core"
2023-09-05 20:47:59 +12:00
import { PostgresProvider } from "../integrations/tests/utils"
2023-01-18 06:22:31 +13:00
2023-01-21 00:48:11 +13:00
const config = setup.getConfig()!
2023-01-19 02:55:24 +13:00
2023-02-07 09:12:08 +13:00
jest.setTimeout(30000)
2023-02-07 03:54:49 +13:00
jest.unmock("pg")
jest.mock("../websockets")
2023-01-20 00:00:51 +13:00
2023-09-05 21:11:47 +12:00
const provider = new PostgresProvider()
describe("postgres integrations", () => {
2023-02-07 03:54:49 +13:00
let makeRequest: MakeRequestResponse,
2023-01-19 06:07:09 +13:00
postgresDatasource: Datasource,
2023-02-07 23:46:34 +13:00
primaryPostgresTable: Table,
2023-02-24 22:47:32 +13:00
oneToManyRelationshipInfo: ForeignTableInfo,
manyToOneRelationshipInfo: ForeignTableInfo,
manyToManyRelationshipInfo: ForeignTableInfo
2023-01-19 06:07:09 +13:00
2023-02-07 03:54:49 +13:00
beforeAll(async () => {
2023-01-19 06:07:09 +13:00
await config.init()
2023-02-07 03:54:49 +13:00
const apiKey = await config.generateApiKey()
makeRequest = generateMakeRequest(apiKey, true)
})
2023-05-19 23:19:55 +12:00
beforeEach(async () => {
2023-09-05 20:47:59 +12:00
postgresDatasource = await config.api.datasource.create(
2023-09-05 21:11:47 +12:00
await provider.getDsConfig()
2023-09-05 20:47:59 +12:00
)
2023-01-19 05:46:40 +13:00
2023-02-23 10:40:50 +13:00
async function createAuxTable(prefix: string) {
return await config.createTable({
name: `${prefix}_${generator.word({ length: 6 })}`,
type: "external",
primary: ["id"],
2023-02-23 22:39:16 +13:00
primaryDisplay: "title",
2023-02-23 10:40:50 +13:00
schema: {
id: {
name: "id",
type: FieldType.AUTO,
2023-02-23 22:39:16 +13:00
autocolumn: true,
2023-02-07 03:54:49 +13:00
},
2023-02-23 10:40:50 +13:00
title: {
name: "title",
type: FieldType.STRING,
2023-02-07 03:54:49 +13:00
},
},
2023-02-23 10:40:50 +13:00
sourceId: postgresDatasource._id,
})
}
2023-02-24 22:47:32 +13:00
oneToManyRelationshipInfo = {
2023-02-23 10:40:50 +13:00
table: await createAuxTable("o2m"),
fieldName: "oneToManyRelation",
relationshipType: RelationshipType.ONE_TO_MANY,
2023-02-23 10:40:50 +13:00
}
2023-02-24 22:47:32 +13:00
manyToOneRelationshipInfo = {
2023-02-23 10:40:50 +13:00
table: await createAuxTable("m2o"),
fieldName: "manyToOneRelation",
relationshipType: RelationshipType.MANY_TO_ONE,
2023-02-23 10:40:50 +13:00
}
2023-02-24 22:47:32 +13:00
manyToManyRelationshipInfo = {
2023-02-23 10:40:50 +13:00
table: await createAuxTable("m2m"),
fieldName: "manyToManyRelation",
relationshipType: RelationshipType.MANY_TO_MANY,
2023-02-23 10:40:50 +13:00
}
2023-01-19 06:07:09 +13:00
2023-02-07 23:46:34 +13:00
primaryPostgresTable = await config.createTable({
2023-02-23 10:40:50 +13:00
name: `p_${generator.word({ length: 6 })}`,
2023-02-07 03:54:49 +13:00
type: "external",
primary: ["id"],
2023-01-19 06:07:09 +13:00
schema: {
2023-02-07 03:54:49 +13:00
id: {
name: "id",
type: FieldType.AUTO,
2023-02-23 22:39:16 +13:00
autocolumn: true,
2023-02-07 03:54:49 +13:00
},
2023-01-19 06:07:09 +13:00
name: {
name: "name",
type: FieldType.STRING,
},
description: {
name: "description",
type: FieldType.STRING,
},
value: {
name: "value",
type: FieldType.NUMBER,
2023-01-19 05:46:40 +13:00
},
2023-02-23 10:40:50 +13:00
oneToManyRelation: {
2023-01-20 09:09:39 +13:00
type: FieldType.LINK,
constraints: {
type: "array",
},
2023-02-24 22:47:32 +13:00
fieldName: oneToManyRelationshipInfo.fieldName,
2023-02-23 10:40:50 +13:00
name: "oneToManyRelation",
relationshipType: RelationshipType.ONE_TO_MANY,
2023-02-24 22:47:32 +13:00
tableId: oneToManyRelationshipInfo.table._id,
2023-02-23 22:39:16 +13:00
main: true,
2023-02-23 10:40:50 +13:00
},
manyToOneRelation: {
type: FieldType.LINK,
constraints: {
type: "array",
},
2023-02-24 22:47:32 +13:00
fieldName: manyToOneRelationshipInfo.fieldName,
2023-02-23 10:40:50 +13:00
name: "manyToOneRelation",
relationshipType: RelationshipType.MANY_TO_ONE,
2023-02-24 22:47:32 +13:00
tableId: manyToOneRelationshipInfo.table._id,
2023-02-23 22:39:16 +13:00
main: true,
2023-02-23 10:40:50 +13:00
},
manyToManyRelation: {
type: FieldType.LINK,
constraints: {
type: "array",
},
2023-02-24 22:47:32 +13:00
fieldName: manyToManyRelationshipInfo.fieldName,
2023-02-23 10:40:50 +13:00
name: "manyToManyRelation",
relationshipType: RelationshipType.MANY_TO_MANY,
2023-02-24 22:47:32 +13:00
tableId: manyToManyRelationshipInfo.table._id,
2023-02-23 22:39:16 +13:00
main: true,
2023-01-20 09:09:39 +13:00
},
},
sourceId: postgresDatasource._id,
})
2023-01-19 05:46:40 +13:00
})
2023-01-18 06:22:31 +13:00
2023-02-23 10:40:50 +13:00
afterAll(config.end)
2023-01-19 06:07:09 +13:00
2023-02-07 23:46:34 +13:00
function generateRandomPrimaryRowData() {
2023-01-19 06:07:09 +13:00
return {
2023-02-02 23:43:18 +13:00
name: generator.name(),
description: generator.paragraph(),
2023-02-07 06:30:33 +13:00
value: generator.age(),
2023-01-19 06:07:09 +13:00
}
}
2023-01-18 06:22:31 +13:00
2023-02-08 00:27:46 +13:00
type PrimaryRowData = {
name: string
description: string
value: number
}
2023-02-23 22:39:16 +13:00
type ForeignTableInfo = {
table: Table
fieldName: string
relationshipType: RelationshipType
2023-02-23 22:39:16 +13:00
}
type ForeignRowsInfo = {
row: Row
relationshipType: RelationshipType
2023-02-23 22:39:16 +13:00
}
2023-02-07 23:46:34 +13:00
async function createPrimaryRow(opts: {
2023-02-08 00:27:46 +13:00
rowData: PrimaryRowData
2023-02-23 12:06:57 +13:00
createForeignRows?: {
2023-02-24 22:51:55 +13:00
createOneToMany?: boolean
createManyToOne?: number
createManyToMany?: number
2023-02-23 12:06:57 +13:00
}
2023-02-07 23:46:34 +13:00
}) {
2023-02-23 22:39:16 +13:00
let { rowData } = opts as any
2023-02-23 12:06:57 +13:00
let foreignRows: ForeignRowsInfo[] = []
2023-02-23 10:40:50 +13:00
2023-02-23 12:06:57 +13:00
async function createForeignRow(tableInfo: ForeignTableInfo) {
2023-02-23 22:39:16 +13:00
const foreignKey = `fk_${tableInfo.table.name}_${tableInfo.fieldName}`
2023-02-23 12:06:57 +13:00
const foreignRow = await config.createRow({
tableId: tableInfo.table._id,
2023-02-07 03:54:49 +13:00
title: generator.name(),
})
2023-02-07 23:46:34 +13:00
rowData = {
...rowData,
2023-02-23 12:06:57 +13:00
[foreignKey]: foreignRow.id,
2023-02-07 03:54:49 +13:00
}
2023-02-23 12:06:57 +13:00
foreignRows.push({
row: foreignRow,
2023-02-23 22:39:16 +13:00
2023-02-23 12:06:57 +13:00
relationshipType: tableInfo.relationshipType,
})
}
2023-02-24 22:51:55 +13:00
if (opts?.createForeignRows?.createOneToMany) {
2023-02-24 22:47:32 +13:00
const foreignKey = `fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`
2023-02-23 22:39:16 +13:00
const foreignRow = await config.createRow({
2023-02-24 22:47:32 +13:00
tableId: oneToManyRelationshipInfo.table._id,
2023-02-23 22:39:16 +13:00
title: generator.name(),
})
rowData = {
...rowData,
[foreignKey]: foreignRow.id,
}
foreignRows.push({
row: foreignRow,
2023-02-24 22:47:32 +13:00
relationshipType: oneToManyRelationshipInfo.relationshipType,
2023-02-23 22:39:16 +13:00
})
2023-02-23 12:06:57 +13:00
}
2023-02-24 22:51:55 +13:00
for (let i = 0; i < (opts?.createForeignRows?.createManyToOne || 0); i++) {
2023-02-23 22:39:16 +13:00
const foreignRow = await config.createRow({
2023-02-24 22:47:32 +13:00
tableId: manyToOneRelationshipInfo.table._id,
2023-02-23 22:39:16 +13:00
title: generator.name(),
})
rowData = {
...rowData,
2023-02-24 22:47:32 +13:00
[manyToOneRelationshipInfo.fieldName]:
rowData[manyToOneRelationshipInfo.fieldName] || [],
2023-02-23 22:39:16 +13:00
}
2023-02-24 22:47:32 +13:00
rowData[manyToOneRelationshipInfo.fieldName].push(foreignRow._id)
2023-02-23 22:39:16 +13:00
foreignRows.push({
row: foreignRow,
relationshipType: RelationshipType.MANY_TO_ONE,
2023-02-23 22:39:16 +13:00
})
2023-02-23 12:06:57 +13:00
}
2023-02-24 22:51:55 +13:00
for (let i = 0; i < (opts?.createForeignRows?.createManyToMany || 0); i++) {
2023-02-23 22:50:18 +13:00
const foreignRow = await config.createRow({
2023-02-24 22:47:32 +13:00
tableId: manyToManyRelationshipInfo.table._id,
2023-02-23 22:50:18 +13:00
title: generator.name(),
})
rowData = {
...rowData,
2023-02-24 22:47:32 +13:00
[manyToManyRelationshipInfo.fieldName]:
rowData[manyToManyRelationshipInfo.fieldName] || [],
2023-02-23 22:50:18 +13:00
}
2023-02-24 22:47:32 +13:00
rowData[manyToManyRelationshipInfo.fieldName].push(foreignRow._id)
2023-02-23 22:50:18 +13:00
foreignRows.push({
row: foreignRow,
relationshipType: RelationshipType.MANY_TO_MANY,
2023-02-23 22:50:18 +13:00
})
2023-02-07 03:54:49 +13:00
}
2023-02-07 23:46:34 +13:00
const row = await config.createRow({
tableId: primaryPostgresTable._id,
...rowData,
2023-01-20 00:35:00 +13:00
})
2023-02-07 23:46:34 +13:00
2023-02-23 12:06:57 +13:00
return { row, foreignRows }
2023-01-20 00:35:00 +13:00
}
2023-02-07 03:54:49 +13:00
async function createDefaultPgTable() {
return await config.createTable({
name: generator.word({ length: 10 }),
type: "external",
primary: ["id"],
schema: {
id: {
name: "id",
type: FieldType.AUTO,
},
},
sourceId: postgresDatasource._id,
})
}
2023-02-08 00:27:46 +13:00
async function populatePrimaryRows(
count: number,
2023-02-23 12:06:57 +13:00
opts?: {
2023-02-24 22:51:55 +13:00
createOneToMany?: boolean
createManyToOne?: number
createManyToMany?: number
2023-02-23 12:06:57 +13:00
}
2023-02-08 00:27:46 +13:00
) {
2023-01-19 06:07:09 +13:00
return await Promise.all(
Array(count)
.fill({})
.map(async () => {
2023-02-07 23:46:34 +13:00
const rowData = generateRandomPrimaryRowData()
2023-01-19 06:07:09 +13:00
return {
rowData,
2023-02-07 23:46:34 +13:00
...(await createPrimaryRow({
rowData,
2023-02-23 10:40:50 +13:00
createForeignRows: opts,
2023-02-07 23:46:34 +13:00
})),
2023-01-19 06:07:09 +13:00
}
})
)
2023-01-19 05:46:40 +13:00
}
2023-02-07 23:46:34 +13:00
it("validate table schema", async () => {
2023-01-20 05:21:37 +13:00
const res = await makeRequest(
"get",
2023-01-20 05:43:39 +13:00
`/api/datasources/${postgresDatasource._id}`
2023-01-20 05:21:37 +13:00
)
expect(res.status).toBe(200)
2023-01-20 05:43:39 +13:00
expect(res.body).toEqual({
config: {
ca: false,
database: "postgres",
2023-09-05 05:05:18 +12:00
host: postgresDatasource.config!.host,
2023-02-02 23:43:18 +13:00
password: "--secret-value--",
2023-09-05 05:05:18 +12:00
port: postgresDatasource.config!.port,
2023-01-20 05:43:39 +13:00
rejectUnauthorized: false,
schema: "public",
ssl: false,
2023-02-07 03:54:49 +13:00
user: "postgres",
2023-01-20 05:43:39 +13:00
},
plus: true,
source: "POSTGRES",
2023-04-27 03:45:00 +12:00
type: "datasource_plus",
2023-01-20 05:43:39 +13:00
_id: expect.any(String),
_rev: expect.any(String),
createdAt: expect.any(String),
updatedAt: expect.any(String),
2023-02-07 03:54:49 +13:00
entities: expect.any(Object),
2023-01-20 05:43:39 +13:00
})
2023-01-20 05:21:37 +13:00
})
2023-02-07 23:46:34 +13:00
describe("POST /api/:tableId/rows", () => {
2023-01-20 06:23:48 +13:00
const createRow = (tableId: string | undefined, body: object) =>
makeRequest("post", `/api/${tableId}/rows`, body)
2023-02-08 00:27:46 +13:00
describe("given than no row exists", () => {
it("adding a new one persists it", async () => {
const newRow = generateRandomPrimaryRowData()
2023-01-18 06:22:31 +13:00
2023-02-08 00:27:46 +13:00
const res = await createRow(primaryPostgresTable._id, newRow)
2023-01-18 06:22:31 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-18 06:39:59 +13:00
2023-02-08 00:27:46 +13:00
const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(1)
2023-02-07 03:54:49 +13:00
2023-02-08 00:27:46 +13:00
const expected = {
...res.body,
...newRow,
}
2023-02-07 03:54:49 +13:00
2023-02-08 00:27:46 +13:00
expect(persistedRows).toEqual([expect.objectContaining(expected)])
})
2023-01-19 01:26:26 +13:00
2023-02-08 00:27:46 +13:00
it("multiple rows can be persisted", async () => {
const numberOfRows = 10
const newRows = Array(numberOfRows).fill(generateRandomPrimaryRowData())
2023-01-19 01:26:26 +13:00
2023-02-08 00:27:46 +13:00
for (const newRow of newRows) {
const res = await createRow(primaryPostgresTable._id, newRow)
expect(res.status).toBe(200)
}
2023-01-19 01:26:26 +13:00
2023-02-08 00:27:46 +13:00
const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(numberOfRows)
expect(persistedRows).toEqual(
expect.arrayContaining(newRows.map(expect.objectContaining))
)
})
2023-01-19 01:26:26 +13:00
})
2023-01-18 06:22:31 +13:00
})
2023-01-19 05:46:40 +13:00
2023-02-07 23:46:34 +13:00
describe("PATCH /api/:tableId/rows", () => {
2023-01-20 06:23:48 +13:00
const updateRow = (tableId: string | undefined, body: Row) =>
makeRequest("patch", `/api/${tableId}/rows`, body)
2023-02-08 00:27:46 +13:00
describe("given than a row exists", () => {
let row: Row
beforeEach(async () => {
2023-02-23 10:40:50 +13:00
let rowResponse = _.sample(await populatePrimaryRows(1))!
2023-02-08 00:27:46 +13:00
row = rowResponse.row
})
2023-01-19 06:11:52 +13:00
2023-02-08 00:27:46 +13:00
it("updating it persists it", async () => {
const newName = generator.name()
const newValue = generator.age()
const updatedRow = {
...row,
name: newName,
value: newValue,
}
2023-01-19 06:11:52 +13:00
2023-02-08 00:27:46 +13:00
const res = await updateRow(primaryPostgresTable._id, updatedRow)
2023-01-19 06:11:52 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
expect(res.body).toEqual(updatedRow)
2023-01-19 06:11:52 +13:00
2023-02-08 00:27:46 +13:00
const persistedRow = await config.getRow(
primaryPostgresTable._id!,
row.id
)
2023-01-19 06:11:52 +13:00
2023-02-08 00:27:46 +13:00
expect(persistedRow).toEqual(
expect.objectContaining({
id: row.id,
name: newName,
value: newValue,
})
)
})
2023-01-19 06:11:52 +13:00
})
})
2023-02-07 23:46:34 +13:00
describe("DELETE /api/:tableId/rows", () => {
2023-01-20 06:23:48 +13:00
const deleteRow = (
tableId: string | undefined,
body: Row | { rows: Row[] }
) => makeRequest("delete", `/api/${tableId}/rows`, body)
2023-02-08 00:27:46 +13:00
describe("given than multiple row exist", () => {
2023-01-19 06:21:17 +13:00
const numberOfInitialRows = 5
2023-02-08 00:27:46 +13:00
let rows: Row[]
beforeEach(async () => {
rows = (await populatePrimaryRows(numberOfInitialRows)).map(x => x.row)
})
2023-01-19 06:21:17 +13:00
2023-02-08 00:27:46 +13:00
it("delete request removes it", async () => {
const row = _.sample(rows)!
const res = await deleteRow(primaryPostgresTable._id, row)
2023-01-19 06:21:17 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-19 06:21:17 +13:00
2023-02-08 00:27:46 +13:00
const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(numberOfInitialRows - 1)
2023-01-19 06:21:17 +13:00
2023-02-08 00:27:46 +13:00
expect(row.id).toBeDefined()
expect(persistedRows).not.toContain(
expect.objectContaining({ _id: row.id })
)
})
2023-01-20 06:23:48 +13:00
2023-02-08 00:27:46 +13:00
it("multiple rows can be removed at once", async () => {
let rowsToDelete = _.sampleSize(rows, 3)!
2023-01-20 06:28:42 +13:00
2023-02-08 00:27:46 +13:00
const res = await deleteRow(primaryPostgresTable._id, {
rows: rowsToDelete,
})
2023-01-20 06:28:42 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-20 06:28:42 +13:00
2023-02-08 00:27:46 +13:00
const persistedRows = await config.getRows(primaryPostgresTable._id!)
expect(persistedRows).toHaveLength(numberOfInitialRows - 3)
2023-01-20 06:28:42 +13:00
2023-02-08 00:27:46 +13:00
for (const row of rowsToDelete) {
expect(persistedRows).not.toContain(
expect.objectContaining({ _id: row.id })
)
}
})
2023-01-20 06:28:42 +13:00
})
2023-01-19 06:21:17 +13:00
})
2023-02-07 23:46:34 +13:00
describe("GET /api/:tableId/rows/:rowId", () => {
2023-01-20 06:23:48 +13:00
const getRow = (tableId: string | undefined, rowId?: string | undefined) =>
makeRequest("get", `/api/${tableId}/rows/${rowId}`)
2023-02-08 00:27:46 +13:00
describe("given than a table have a single row", () => {
let rowData: PrimaryRowData, row: Row
beforeEach(async () => {
const [createdRow] = await populatePrimaryRows(1)
rowData = createdRow.rowData
row = createdRow.row
})
2023-01-19 05:46:40 +13:00
2023-02-08 00:27:46 +13:00
it("the row can be retrieved successfully", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
2023-01-19 05:46:40 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-19 05:46:40 +13:00
2023-02-08 00:27:46 +13:00
expect(res.body).toEqual(expect.objectContaining(rowData))
})
2023-01-19 05:46:40 +13:00
})
2023-01-19 05:48:18 +13:00
2023-02-08 00:27:46 +13:00
describe("given than a table have a multiple rows", () => {
let rows: { row: Row; rowData: PrimaryRowData }[]
2023-01-19 05:48:18 +13:00
2023-02-08 00:27:46 +13:00
beforeEach(async () => {
2023-02-23 22:50:18 +13:00
rows = await populatePrimaryRows(5)
2023-02-08 00:27:46 +13:00
})
2023-01-19 05:48:18 +13:00
2023-02-08 00:27:46 +13:00
it("a single row can be retrieved successfully", async () => {
const { rowData, row } = _.sample(rows)!
2023-01-19 05:48:18 +13:00
2023-02-08 00:27:46 +13:00
const res = await getRow(primaryPostgresTable._id, row.id)
2023-01-20 09:09:39 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-20 09:09:39 +13:00
2023-02-08 00:27:46 +13:00
expect(res.body).toEqual(expect.objectContaining(rowData))
2023-01-20 09:09:39 +13:00
})
2023-02-08 00:27:46 +13:00
})
2023-01-20 09:09:39 +13:00
2023-02-08 00:27:46 +13:00
describe("given a row with relation data", () => {
let row: Row
2023-02-23 12:06:57 +13:00
let rowData: {
name: string
description: string
value: number
}
let foreignRows: ForeignRowsInfo[]
describe("with all relationship types", () => {
beforeEach(async () => {
let [createdRow] = await populatePrimaryRows(1, {
2023-02-24 22:51:55 +13:00
createOneToMany: true,
createManyToOne: 3,
createManyToMany: 2,
2023-02-23 12:06:57 +13:00
})
row = createdRow.row
rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
})
it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200)
const one2ManyForeignRows = foreignRows.filter(
x => x.relationshipType === RelationshipType.ONE_TO_MANY
2023-02-23 12:06:57 +13:00
)
expect(one2ManyForeignRows).toHaveLength(1)
expect(res.body).toEqual({
...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
2023-02-24 22:47:32 +13:00
[`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]:
2023-02-23 22:39:16 +13:00
one2ManyForeignRows[0].row.id,
2023-02-23 12:06:57 +13:00
})
2023-02-24 22:47:32 +13:00
expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
2023-02-08 00:27:46 +13:00
})
})
2023-01-20 09:09:39 +13:00
2023-02-23 12:06:57 +13:00
describe("with only one to many", () => {
beforeEach(async () => {
let [createdRow] = await populatePrimaryRows(1, {
2023-02-24 22:51:55 +13:00
createOneToMany: true,
2023-02-23 12:06:57 +13:00
})
row = createdRow.row
rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
})
2023-01-20 09:09:39 +13:00
2023-02-23 12:06:57 +13:00
it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
2023-02-08 00:27:46 +13:00
2023-02-23 12:06:57 +13:00
expect(res.status).toBe(200)
expect(foreignRows).toHaveLength(1)
expect(res.body).toEqual({
...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
2023-02-24 22:47:32 +13:00
[`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]:
2023-02-23 22:39:16 +13:00
foreignRows[0].row.id,
2023-02-23 12:06:57 +13:00
})
2023-02-24 22:47:32 +13:00
expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
2023-02-08 00:27:46 +13:00
})
2023-02-23 12:06:57 +13:00
})
describe("with only many to one", () => {
beforeEach(async () => {
let [createdRow] = await populatePrimaryRows(1, {
2023-02-24 22:51:55 +13:00
createManyToOne: 3,
2023-02-23 12:06:57 +13:00
})
row = createdRow.row
rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
})
it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200)
expect(foreignRows).toHaveLength(3)
expect(res.body).toEqual({
...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
})
2023-02-24 22:47:32 +13:00
expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
2023-02-23 12:06:57 +13:00
})
})
describe("with only many to many", () => {
beforeEach(async () => {
let [createdRow] = await populatePrimaryRows(1, {
2023-02-24 22:51:55 +13:00
createManyToMany: 2,
2023-02-23 12:06:57 +13:00
})
row = createdRow.row
rowData = createdRow.rowData
foreignRows = createdRow.foreignRows
})
it("only one to many foreign keys are retrieved", async () => {
const res = await getRow(primaryPostgresTable._id, row.id)
expect(res.status).toBe(200)
expect(foreignRows).toHaveLength(2)
expect(res.body).toEqual({
...rowData,
id: row.id,
tableId: row.tableId,
_id: expect.any(String),
_rev: expect.any(String),
})
2023-02-24 22:47:32 +13:00
expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined()
2023-02-23 12:06:57 +13:00
})
2023-01-20 09:09:39 +13:00
})
})
2023-01-19 05:46:40 +13:00
})
2023-01-20 00:00:51 +13:00
2023-02-07 23:46:34 +13:00
describe("POST /api/:tableId/search", () => {
2023-01-20 00:44:48 +13:00
const search = (tableId: string | undefined, body?: object) =>
2023-01-20 06:23:48 +13:00
makeRequest("post", `/api/${tableId}/search`, body)
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
describe("search without parameters", () => {
describe("given than a table has no rows", () => {
it("search without query returns empty", async () => {
const res = await search(primaryPostgresTable._id)
2023-01-20 00:06:41 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-20 00:06:41 +13:00
2023-02-08 00:27:46 +13:00
expect(res.body).toEqual({
rows: [],
bookmark: null,
hasNextPage: false,
})
2023-02-07 03:54:49 +13:00
})
2023-01-20 00:20:20 +13:00
})
2023-01-20 00:06:41 +13:00
2023-02-08 00:27:46 +13:00
describe("given than a table has multiple rows", () => {
2023-01-20 00:20:20 +13:00
const rowsCount = 6
2023-02-08 00:27:46 +13:00
let rows: {
row: Row
rowData: PrimaryRowData
}[]
beforeEach(async () => {
rows = await populatePrimaryRows(rowsCount)
})
2023-01-20 00:00:51 +13:00
2023-02-08 00:27:46 +13:00
it("search without query returns all of them", async () => {
const res = await search(primaryPostgresTable._id)
2023-01-20 00:00:51 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-20 00:00:51 +13:00
2023-02-08 00:27:46 +13:00
expect(res.body).toEqual({
rows: expect.arrayContaining(
rows.map(r => expect.objectContaining(r.rowData))
),
bookmark: null,
hasNextPage: false,
})
expect(res.body.rows).toHaveLength(rowsCount)
2023-01-20 06:23:48 +13:00
})
2023-01-20 00:20:20 +13:00
})
2023-02-08 00:27:46 +13:00
describe("given than multiple tables have multiple rows", () => {
2023-01-20 00:20:20 +13:00
const rowsCount = 6
2023-02-08 00:27:46 +13:00
beforeEach(async () => {
const createRandomTableWithRows = async () =>
await config.createRow({
tableId: (await createDefaultPgTable())._id,
title: generator.name(),
})
2023-01-20 00:20:20 +13:00
2023-02-08 00:27:46 +13:00
await createRandomTableWithRows()
await createRandomTableWithRows()
2023-02-07 23:46:34 +13:00
2023-02-08 00:27:46 +13:00
await populatePrimaryRows(rowsCount)
2023-01-20 00:20:20 +13:00
2023-02-08 00:27:46 +13:00
await createRandomTableWithRows()
})
it("search only return the requested ones", async () => {
const res = await search(primaryPostgresTable._id)
expect(res.status).toBe(200)
2023-01-20 00:20:20 +13:00
2023-02-08 00:27:46 +13:00
expect(res.body.rows).toHaveLength(rowsCount)
})
2023-01-20 00:20:20 +13:00
})
2023-01-20 00:00:51 +13:00
})
2023-01-20 00:06:41 +13:00
2023-02-07 23:46:34 +13:00
it("Querying by a string field returns the rows with field containing or starting by that value", async () => {
2023-02-02 23:43:18 +13:00
const name = generator.name()
2023-01-20 00:20:20 +13:00
const rowsToFilter = [
...Array(2).fill({
name,
2023-02-02 23:43:18 +13:00
description: generator.paragraph(),
2023-02-07 06:30:33 +13:00
value: generator.age(),
2023-01-20 00:20:20 +13:00
}),
...Array(2).fill({
2023-02-02 23:43:18 +13:00
name: `${name}${utils.newid()}`,
description: generator.paragraph(),
2023-02-07 06:30:33 +13:00
value: generator.age(),
2023-01-20 00:20:20 +13:00
}),
]
2023-02-07 23:46:34 +13:00
await populatePrimaryRows(3)
2023-01-20 00:20:20 +13:00
for (const row of rowsToFilter) {
2023-02-07 23:46:34 +13:00
await createPrimaryRow({
rowData: row,
})
2023-01-20 00:20:20 +13:00
}
2023-02-07 23:46:34 +13:00
await populatePrimaryRows(1)
2023-01-20 00:06:41 +13:00
2023-02-07 23:46:34 +13:00
const res = await search(primaryPostgresTable._id, {
2023-01-20 00:44:48 +13:00
query: {
string: {
name,
2023-01-20 00:20:20 +13:00
},
2023-01-20 00:44:48 +13:00
},
})
2023-01-20 00:06:41 +13:00
expect(res.status).toBe(200)
2023-01-20 06:23:48 +13:00
expect(res.body).toEqual({
rows: expect.arrayContaining(rowsToFilter.map(expect.objectContaining)),
2023-02-07 03:54:49 +13:00
bookmark: null,
hasNextPage: false,
2023-01-20 06:23:48 +13:00
})
expect(res.body.rows).toHaveLength(4)
2023-01-20 00:06:41 +13:00
})
2023-01-20 00:35:00 +13:00
2023-02-07 23:46:34 +13:00
it("Querying respects the limit fields", async () => {
await populatePrimaryRows(6)
2023-01-20 00:35:00 +13:00
2023-02-07 23:46:34 +13:00
const res = await search(primaryPostgresTable._id, {
2023-01-20 00:44:48 +13:00
limit: 2,
})
2023-01-20 00:35:00 +13:00
expect(res.status).toBe(200)
2023-01-20 06:23:48 +13:00
expect(res.body.rows).toHaveLength(2)
2023-01-20 00:35:00 +13:00
})
describe("sort", () => {
beforeEach(async () => {
2023-02-07 23:46:34 +13:00
const defaultValue = generateRandomPrimaryRowData()
2023-01-20 00:35:00 +13:00
2023-02-07 23:46:34 +13:00
await createPrimaryRow({
rowData: {
2023-01-20 00:35:00 +13:00
...defaultValue,
name: "d",
value: 3,
},
2023-02-07 23:46:34 +13:00
})
await createPrimaryRow({
rowData: { ...defaultValue, name: "aaa", value: 40 },
})
await createPrimaryRow({
rowData: { ...defaultValue, name: "ccccc", value: -5 },
})
await createPrimaryRow({
rowData: { ...defaultValue, name: "bb", value: 0 },
})
2023-01-20 00:35:00 +13:00
})
2023-02-07 23:46:34 +13:00
it("Querying respects the sort order when sorting ascending by a string value", async () => {
const res = await search(primaryPostgresTable._id, {
2023-01-20 06:23:48 +13:00
sort: "name",
sortOrder: "ascending",
sortType: "string",
2023-01-20 00:44:48 +13:00
})
2023-01-20 00:35:00 +13:00
expect(res.status).toBe(200)
2023-01-20 06:23:48 +13:00
expect(res.body.rows).toEqual([
2023-01-20 00:35:00 +13:00
expect.objectContaining({ name: "aaa" }),
expect.objectContaining({ name: "bb" }),
expect.objectContaining({ name: "ccccc" }),
expect.objectContaining({ name: "d" }),
])
})
2023-02-07 23:46:34 +13:00
it("Querying respects the sort order when sorting descending by a string value", async () => {
const res = await search(primaryPostgresTable._id, {
2023-01-20 06:23:48 +13:00
sort: "name",
sortOrder: "descending",
sortType: "string",
2023-01-20 00:44:48 +13:00
})
2023-01-20 00:35:00 +13:00
expect(res.status).toBe(200)
2023-01-20 06:23:48 +13:00
expect(res.body.rows).toEqual([
2023-01-20 00:35:00 +13:00
expect.objectContaining({ name: "d" }),
expect.objectContaining({ name: "ccccc" }),
expect.objectContaining({ name: "bb" }),
expect.objectContaining({ name: "aaa" }),
])
})
2023-02-07 23:46:34 +13:00
it("Querying respects the sort order when sorting ascending by a numeric value", async () => {
const res = await search(primaryPostgresTable._id, {
2023-01-20 06:23:48 +13:00
sort: "value",
sortOrder: "ascending",
sortType: "number",
2023-01-20 00:44:48 +13:00
})
2023-01-20 00:35:00 +13:00
expect(res.status).toBe(200)
2023-01-20 06:23:48 +13:00
expect(res.body.rows).toEqual([
2023-01-20 00:35:00 +13:00
expect.objectContaining({ value: -5 }),
expect.objectContaining({ value: 0 }),
expect.objectContaining({ value: 3 }),
expect.objectContaining({ value: 40 }),
])
})
2023-02-07 23:46:34 +13:00
it("Querying respects the sort order when sorting descending by a numeric value", async () => {
const res = await search(primaryPostgresTable._id, {
2023-01-20 06:23:48 +13:00
sort: "value",
sortOrder: "descending",
sortType: "number",
2023-01-20 00:44:48 +13:00
})
2023-01-20 00:35:00 +13:00
expect(res.status).toBe(200)
2023-01-20 06:23:48 +13:00
expect(res.body.rows).toEqual([
2023-01-20 00:35:00 +13:00
expect.objectContaining({ value: 40 }),
expect.objectContaining({ value: 3 }),
expect.objectContaining({ value: 0 }),
expect.objectContaining({ value: -5 }),
])
})
})
2023-01-20 00:00:51 +13:00
})
2023-01-20 00:44:48 +13:00
2023-02-07 23:46:34 +13:00
describe("GET /api/:tableId/:rowId/enrich", () => {
2023-01-20 09:09:39 +13:00
const getAll = (tableId: string | undefined, rowId: string | undefined) =>
makeRequest("get", `/api/${tableId}/${rowId}/enrich`)
2023-02-08 00:27:46 +13:00
describe("given a row with relation data", () => {
2023-02-23 12:06:57 +13:00
let row: Row, rowData: PrimaryRowData, foreignRows: ForeignRowsInfo[]
2023-02-08 00:27:46 +13:00
2023-02-23 22:50:18 +13:00
describe("with all relationship types", () => {
2023-02-23 12:06:57 +13:00
beforeEach(async () => {
rowData = generateRandomPrimaryRowData()
const rowsInfo = await createPrimaryRow({
rowData,
2023-02-23 22:50:18 +13:00
createForeignRows: {
2023-02-24 22:51:55 +13:00
createOneToMany: true,
createManyToOne: 3,
createManyToMany: 2,
2023-02-23 22:50:18 +13:00
},
2023-02-23 12:06:57 +13:00
})
2023-01-20 09:09:39 +13:00
2023-02-23 12:06:57 +13:00
row = rowsInfo.row
foreignRows = rowsInfo.foreignRows
})
2023-01-20 09:09:39 +13:00
2023-02-23 22:50:18 +13:00
it("enrich populates the foreign fields", async () => {
2023-02-23 12:06:57 +13:00
const res = await getAll(primaryPostgresTable._id, row.id)
2023-01-20 09:09:39 +13:00
2023-02-23 12:06:57 +13:00
expect(res.status).toBe(200)
2023-01-20 09:09:39 +13:00
2023-02-23 22:50:18 +13:00
const foreignRowsByType = _.groupBy(
foreignRows,
x => x.relationshipType
)
const m2mFieldName = manyToManyRelationshipInfo.fieldName,
o2mFieldName = oneToManyRelationshipInfo.fieldName,
m2oFieldName = manyToOneRelationshipInfo.fieldName
const m2mRow1 = res.body[m2mFieldName].find(
(row: Row) => row.id === 1
)
const m2mRow2 = res.body[m2mFieldName].find(
(row: Row) => row.id === 2
)
expect(m2mRow1).toEqual({
...foreignRowsByType[RelationshipType.MANY_TO_MANY][0].row,
[m2mFieldName]: [
2023-02-23 22:39:16 +13:00
{
_id: row._id,
2023-04-20 22:06:16 +12:00
primaryDisplay: "Invalid display column",
2023-02-23 22:39:16 +13:00
},
],
})
expect(m2mRow2).toEqual({
...foreignRowsByType[RelationshipType.MANY_TO_MANY][1].row,
[m2mFieldName]: [
2023-02-23 22:50:18 +13:00
{
_id: row._id,
2023-04-20 22:06:16 +12:00
primaryDisplay: "Invalid display column",
2023-02-23 22:50:18 +13:00
},
],
2023-02-23 22:39:16 +13:00
})
expect(res.body[m2oFieldName]).toEqual([
{
...foreignRowsByType[RelationshipType.MANY_TO_ONE][0].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id,
},
{
...foreignRowsByType[RelationshipType.MANY_TO_ONE][1].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id,
},
{
...foreignRowsByType[RelationshipType.MANY_TO_ONE][2].row,
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
row.id,
},
])
expect(res.body[o2mFieldName]).toEqual([
{
...foreignRowsByType[RelationshipType.ONE_TO_MANY][0].row,
_id: expect.any(String),
_rev: expect.any(String),
},
])
2023-02-23 22:39:16 +13:00
})
})
2023-01-20 09:09:39 +13:00
})
})
2023-02-07 23:46:34 +13:00
describe("GET /api/:tableId/rows", () => {
2023-01-20 05:46:05 +13:00
const getAll = (tableId: string | undefined) =>
makeRequest("get", `/api/${tableId}/rows`)
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
describe("given a table with no rows", () => {
it("get request returns empty", async () => {
const res = await getAll(primaryPostgresTable._id)
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
expect(res.body).toHaveLength(0)
})
2023-01-20 05:46:05 +13:00
})
2023-02-08 00:27:46 +13:00
describe("given a table with multiple rows", () => {
2023-01-20 05:46:05 +13:00
const rowsCount = 6
2023-02-08 00:27:46 +13:00
let rows: {
row: Row
2023-02-23 12:06:57 +13:00
foreignRows: ForeignRowsInfo[]
2023-02-08 00:27:46 +13:00
rowData: PrimaryRowData
}[]
beforeEach(async () => {
rows = await populatePrimaryRows(rowsCount)
})
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
it("get request returns all of them", async () => {
const res = await getAll(primaryPostgresTable._id)
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
expect(res.body).toHaveLength(rowsCount)
expect(res.body).toEqual(
expect.arrayContaining(
rows.map(r => expect.objectContaining(r.rowData))
)
2023-01-20 05:46:05 +13:00
)
2023-02-08 00:27:46 +13:00
})
2023-01-20 05:46:05 +13:00
})
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
describe("given multiple tables with multiple rows", () => {
2023-01-20 05:46:05 +13:00
const rowsCount = 6
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
beforeEach(async () => {
const createRandomTableWithRows = async () =>
await config.createRow({
tableId: (await createDefaultPgTable())._id,
title: generator.name(),
})
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
await createRandomTableWithRows()
await populatePrimaryRows(rowsCount)
await createRandomTableWithRows()
})
it("get returns the requested ones", async () => {
const res = await getAll(primaryPostgresTable._id)
2023-01-20 00:44:48 +13:00
2023-02-08 00:27:46 +13:00
expect(res.status).toBe(200)
expect(res.body).toHaveLength(rowsCount)
})
2023-01-20 05:46:05 +13:00
})
})
describe("POST /api/datasources/verify", () => {
it("should be able to verify the connection", async () => {
2023-09-05 05:05:18 +12:00
const response = await config.api.datasource.verify({
2023-09-05 21:11:47 +12:00
datasource: await provider.getDsConfig(),
2023-09-05 05:05:18 +12:00
})
expect(response.status).toBe(200)
expect(response.body.connected).toBe(true)
})
it("should state an invalid datasource cannot connect", async () => {
2023-09-05 21:11:47 +12:00
const dbConfig = await provider.getDsConfig()
2023-09-05 05:05:18 +12:00
const response = await config.api.datasource.verify({
datasource: {
2023-09-05 20:47:59 +12:00
...dbConfig,
config: {
...dbConfig.config,
password: "wrongpassword",
},
2023-09-05 05:05:18 +12:00
},
})
expect(response.status).toBe(200)
expect(response.body.connected).toBe(false)
expect(response.body.error).toBeDefined()
})
})
describe("POST /api/datasources/info", () => {
2023-05-23 23:22:22 +12:00
it("should fetch information about postgres datasource", async () => {
const primaryName = primaryPostgresTable.name
const response = await makeRequest("post", "/api/datasources/info", {
datasource: postgresDatasource,
})
expect(response.status).toBe(200)
expect(response.body.tableNames).toBeDefined()
2023-05-23 03:25:50 +12:00
expect(response.body.tableNames.indexOf(primaryName)).not.toBe(-1)
})
})
2023-01-18 06:22:31 +13:00
})