Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move more tests out of sql.spec.ts #13740

Merged
merged 13 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/server/src/api/routes/tests/row.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,25 @@ describe.each([
)
expect(response.message).toBe("Cannot create new user entry.")
})

it("should not mis-parse date string out of JSON", async () => {
const table = await config.api.table.save(
saveTableRequest({
schema: {
name: {
type: FieldType.STRING,
name: "name",
},
},
})
)

const row = await config.api.row.save(table._id!, {
name: `{ "foo": "2023-01-26T11:48:57.000Z" }`,
})

expect(row.name).toEqual(`{ "foo": "2023-01-26T11:48:57.000Z" }`)
})
})

describe("get", () => {
Expand Down
103 changes: 92 additions & 11 deletions packages/server/src/api/routes/tests/search.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
TableSchema,
User,
Row,
RelationshipType,
} from "@budibase/types"
import _ from "lodash"
import tk from "timekeeper"
Expand Down Expand Up @@ -73,7 +74,7 @@ describe.each([
})

async function createTable(schema: TableSchema) {
table = await config.api.table.save(
return await config.api.table.save(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

tableForDatasource(datasource, { schema })
)
}
Expand Down Expand Up @@ -190,7 +191,7 @@ describe.each([

describe("boolean", () => {
beforeAll(async () => {
await createTable({
table = await createTable({
isTrue: { name: "isTrue", type: FieldType.BOOLEAN },
})
await createRows([{ isTrue: true }, { isTrue: false }])
Expand Down Expand Up @@ -320,7 +321,7 @@ describe.each([
})
)

await createTable({
table = await createTable({
name: { name: "name", type: FieldType.STRING },
appointment: { name: "appointment", type: FieldType.DATETIME },
single_user: {
Expand Down Expand Up @@ -596,7 +597,7 @@ describe.each([

describe.each([FieldType.STRING, FieldType.LONGFORM])("%s", () => {
beforeAll(async () => {
await createTable({
table = await createTable({
name: { name: "name", type: FieldType.STRING },
})
await createRows([{ name: "foo" }, { name: "bar" }])
Expand Down Expand Up @@ -716,6 +717,20 @@ describe.each([
expectQuery({
range: { name: { low: "g", high: "h" } },
}).toFindNothing())

!isLucene &&
it("ignores low if it's an empty object", () =>
expectQuery({
// @ts-ignore
range: { name: { low: {}, high: "z" } },
}).toContainExactly([{ name: "foo" }, { name: "bar" }]))

!isLucene &&
it("ignores high if it's an empty object", () =>
expectQuery({
// @ts-ignore
range: { name: { low: "a", high: {} } },
}).toContainExactly([{ name: "foo" }, { name: "bar" }]))
})

describe("empty", () => {
Expand Down Expand Up @@ -780,7 +795,7 @@ describe.each([

describe("numbers", () => {
beforeAll(async () => {
await createTable({
table = await createTable({
age: { name: "age", type: FieldType.NUMBER },
})
await createRows([{ age: 1 }, { age: 10 }])
Expand Down Expand Up @@ -889,7 +904,7 @@ describe.each([
const JAN_10TH = "2020-01-10T00:00:00.000Z"

beforeAll(async () => {
await createTable({
table = await createTable({
dob: { name: "dob", type: FieldType.DATETIME },
})

Expand Down Expand Up @@ -1012,7 +1027,7 @@ describe.each([
const NULL_TIME__ID = `null_time__id`

beforeAll(async () => {
await createTable({
table = await createTable({
timeid: { name: "timeid", type: FieldType.STRING },
time: { name: "time", type: FieldType.DATETIME, timeOnly: true },
})
Expand Down Expand Up @@ -1154,7 +1169,7 @@ describe.each([

describe.each([FieldType.ARRAY, FieldType.OPTIONS])("%s", () => {
beforeAll(async () => {
await createTable({
table = await createTable({
numbers: {
name: "numbers",
type: FieldType.ARRAY,
Expand Down Expand Up @@ -1234,7 +1249,7 @@ describe.each([
const BIG = "9223372036854775807"

beforeAll(async () => {
await createTable({
table = await createTable({
num: { name: "num", type: FieldType.BIGINT },
})
await createRows([{ num: SMALL }, { num: MEDIUM }, { num: BIG }])
Expand Down Expand Up @@ -1325,7 +1340,7 @@ describe.each([
isInternal &&
describe("auto", () => {
beforeAll(async () => {
await createTable({
table = await createTable({
auto: {
name: "auto",
type: FieldType.AUTO,
Expand Down Expand Up @@ -1452,6 +1467,25 @@ describe.each([
{ auto: 2 },
{ auto: 1 },
]))

// This is important for pagination. The order of results must always
// be stable or pagination will break. We don't want the user to need
// to specify an order for pagination to work.
it("is stable without a sort specified", async () => {
let { rows } = await config.api.row.search(table._id!, {
tableId: table._id!,
query: {},
})

for (let i = 0; i < 10; i++) {
const response = await config.api.row.search(table._id!, {
tableId: table._id!,
limit: 1,
query: {},
})
expect(response.rows).toEqual(rows)
}
})
})

// TODO(samwho): fix for SQS
Expand Down Expand Up @@ -1490,7 +1524,7 @@ describe.each([

describe("field name 1:name", () => {
beforeAll(async () => {
await createTable({
table = await createTable({
"1:name": { name: "1:name", type: FieldType.STRING },
})
await createRows([{ "1:name": "bar" }, { "1:name": "foo" }])
Expand All @@ -1506,4 +1540,51 @@ describe.each([
expectQuery({ equal: { "1:1:name": "none" } }).toFindNothing())
})
})

// This will never work for Lucene.
!isLucene &&
describe("relations", () => {
let otherTable: Table
let rows: Row[]

beforeAll(async () => {
otherTable = await createTable({
one: { name: "one", type: FieldType.STRING },
})
table = await createTable({
two: { name: "two", type: FieldType.STRING },
other: {
type: FieldType.LINK,
relationshipType: RelationshipType.ONE_TO_MANY,
name: "other",
fieldName: "other",
tableId: otherTable._id!,
constraints: {
type: "array",
},
},
})

rows = await Promise.all([
config.api.row.save(otherTable._id!, { one: "foo" }),
config.api.row.save(otherTable._id!, { one: "bar" }),
])

await Promise.all([
config.api.row.save(table._id!, {
two: "foo",
other: [rows[0]._id],
}),
config.api.row.save(table._id!, {
two: "bar",
other: [rows[1]._id],
}),
])
})

it("can search through relations", () =>
expectQuery({
equal: { [`${otherTable.name}.one`]: "foo" },
}).toContainExactly([{ two: "foo", other: [{ _id: rows[0]._id }] }]))
})
})
106 changes: 0 additions & 106 deletions packages/server/src/integrations/tests/sql.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,6 @@ function generateReadJson({
}
}

function generateCreateJson(table = TABLE_NAME, body = {}): QueryJson {
return {
endpoint: endpoint(table, "CREATE"),
meta: {
table: TABLE,
},
body,
}
}

function generateRelationshipJson(config: { schema?: string } = {}): QueryJson {
return {
endpoint: {
Expand Down Expand Up @@ -146,24 +136,6 @@ describe("SQL query builder", () => {
sql = new Sql(client, limit)
})

it("should allow filtering on a related field", () => {
const query = sql._query(
generateReadJson({
filters: {
equal: {
age: 10,
"task.name": "task 1",
},
},
})
)
// order of bindings changes because relationship filters occur outside inner query
expect(query).toEqual({
bindings: [10, limit, "task 1"],
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."age" = $1 limit $2) as "${TABLE_NAME}" where "task"."name" = $3`,
})
})

it("should add the schema to the LEFT JOIN", () => {
const query = sql._query(generateRelationshipJson({ schema: "production" }))
expect(query).toEqual({
Expand All @@ -190,44 +162,6 @@ describe("SQL query builder", () => {
})
})

it("should ignore high range value if it is an empty object", () => {
const query = sql._query(
generateReadJson({
filters: {
range: {
dob: {
low: "2000-01-01 00:00:00",
high: {},
},
},
},
})
)
expect(query).toEqual({
bindings: ["2000-01-01 00:00:00", 500],
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."dob" >= $1 limit $2) as "${TABLE_NAME}"`,
})
})

it("should ignore low range value if it is an empty object", () => {
const query = sql._query(
generateReadJson({
filters: {
range: {
dob: {
low: {},
high: "2010-01-01 00:00:00",
},
},
},
})
)
expect(query).toEqual({
bindings: ["2010-01-01 00:00:00", 500],
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."dob" <= $1 limit $2) as "${TABLE_NAME}"`,
})
})

it("should lowercase the values for Oracle LIKE statements", () => {
let query = new Sql(SqlClient.ORACLE, limit)._query(
generateReadJson({
Expand Down Expand Up @@ -272,44 +206,4 @@ describe("SQL query builder", () => {
sql: `select * from (select * from (select * from "test" where LOWER("test"."name") LIKE :1) where rownum <= :2) "test"`,
})
})

it("should sort SQL Server tables by the primary key if no sort data is provided", () => {
let query = new Sql(SqlClient.MS_SQL, limit)._query(
generateReadJson({
sort: {},
paginate: {
limit: 10,
},
})
)
expect(query).toEqual({
bindings: [10],
sql: `select * from (select top (@p0) * from [test] order by [test].[id] asc) as [test]`,
})
})

it("should not parse JSON string as Date", () => {
let query = new Sql(SqlClient.POSTGRES, limit)._query(
generateCreateJson(TABLE_NAME, {
name: '{ "created_at":"2023-09-09T03:21:06.024Z" }',
})
)
expect(query).toEqual({
bindings: ['{ "created_at":"2023-09-09T03:21:06.024Z" }'],
sql: `insert into "test" ("name") values ($1) returning *`,
})
})

it("should parse and trim valid string as Date", () => {
const dateObj = new Date("2023-09-09T03:21:06.024Z")
let query = new Sql(SqlClient.POSTGRES, limit)._query(
generateCreateJson(TABLE_NAME, {
name: " 2023-09-09T03:21:06.024Z ",
})
)
expect(query).toEqual({
bindings: [dateObj],
sql: `insert into "test" ("name") values ($1) returning *`,
})
})
})
16 changes: 7 additions & 9 deletions packages/server/src/sdk/app/rows/search/sqs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ function buildInternalFieldList(
return fieldList
}

function tableInFilter(name: string) {
return `:${name}.`
function tableNameInFieldRegex(tableName: string) {
return new RegExp(`^${tableName}.|:${tableName}.`, "g")
}

function cleanupFilters(filters: SearchFilters, tables: Table[]) {
Expand All @@ -72,15 +72,13 @@ function cleanupFilters(filters: SearchFilters, tables: Table[]) {
// relationship, switch to table ID
const tableRelated = tables.find(
table =>
table.originalName && key.includes(tableInFilter(table.originalName))
table.originalName &&
key.match(tableNameInFieldRegex(table.originalName))
)
if (tableRelated && tableRelated.originalName) {
filter[
key.replace(
tableInFilter(tableRelated.originalName),
tableInFilter(tableRelated._id!)
)
] = filter[key]
// only replace the first, not replaceAll
filter[key.replace(tableRelated.originalName, tableRelated._id!)] =
filter[key]
delete filter[key]
}
}
Expand Down
Loading
Loading