@budibase/server 2.22.15 → 2.22.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builder/assets/{index-d3456412.js → index-4c998325.js} +492 -492
- package/builder/index.html +1 -1
- package/client/budibase-client.js +2 -2
- package/dist/yarn.lock +24 -36
- package/package.json +9 -9
- package/scripts/test.sh +2 -2
- package/src/api/routes/public/tests/metrics.spec.js +0 -2
- package/src/api/routes/tests/appImport.spec.ts +0 -1
- package/src/api/routes/tests/automation.spec.ts +0 -2
- package/src/api/routes/tests/queries/generic-sql.spec.ts +34 -59
- package/src/api/routes/tests/queries/mongodb.spec.ts +17 -12
- package/src/api/routes/tests/row.spec.ts +18 -21
- package/src/api/routes/tests/user.spec.ts +0 -2
- package/src/api/routes/tests/viewV2.spec.ts +13 -18
- package/src/integration-test/mysql.spec.ts +61 -72
- package/src/integration-test/postgres.spec.ts +65 -61
- package/src/integrations/tests/utils/index.ts +78 -13
- package/src/integrations/tests/utils/mariadb.ts +31 -28
- package/src/integrations/tests/utils/mongodb.ts +25 -29
- package/src/integrations/tests/utils/mssql.ts +51 -36
- package/src/integrations/tests/utils/mysql.ts +36 -21
- package/src/integrations/tests/utils/postgres.ts +43 -26
- package/src/migrations/tests/index.spec.ts +0 -2
- package/src/sdk/app/rows/search/tests/external.spec.ts +0 -2
- package/src/tests/jestSetup.ts +2 -8
- package/src/tests/utilities/api/base.ts +41 -11
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Datasource, SourceName } from "@budibase/types"
|
|
2
|
-
import { GenericContainer, Wait
|
|
2
|
+
import { GenericContainer, Wait } from "testcontainers"
|
|
3
3
|
import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy"
|
|
4
|
+
import { rawQuery } from "./mysql"
|
|
5
|
+
import { generator, testContainerUtils } from "@budibase/backend-core/tests"
|
|
6
|
+
import { startContainer } from "."
|
|
4
7
|
|
|
5
|
-
let
|
|
8
|
+
let ports: Promise<testContainerUtils.Port[]>
|
|
6
9
|
|
|
7
10
|
class MariaDBWaitStrategy extends AbstractWaitStrategy {
|
|
8
11
|
async waitUntilReady(container: any, boundPorts: any, startTime?: Date) {
|
|
@@ -21,38 +24,38 @@ class MariaDBWaitStrategy extends AbstractWaitStrategy {
|
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
export async function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
export async function getDatasource(): Promise<Datasource> {
|
|
28
|
+
if (!ports) {
|
|
29
|
+
ports = startContainer(
|
|
30
|
+
new GenericContainer("mariadb:lts")
|
|
31
|
+
.withExposedPorts(3306)
|
|
32
|
+
.withEnvironment({ MARIADB_ROOT_PASSWORD: "password" })
|
|
33
|
+
.withWaitStrategy(new MariaDBWaitStrategy())
|
|
34
|
+
)
|
|
35
|
+
}
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
if (!
|
|
34
|
-
|
|
37
|
+
const port = (await ports).find(x => x.container === 3306)?.host
|
|
38
|
+
if (!port) {
|
|
39
|
+
throw new Error("MariaDB port not found")
|
|
35
40
|
}
|
|
36
|
-
const host = container.getHost()
|
|
37
|
-
const port = container.getMappedPort(3306)
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
const config = {
|
|
43
|
+
host: "127.0.0.1",
|
|
44
|
+
port,
|
|
45
|
+
user: "root",
|
|
46
|
+
password: "password",
|
|
47
|
+
database: "mysql",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const datasource = {
|
|
40
51
|
type: "datasource_plus",
|
|
41
52
|
source: SourceName.MYSQL,
|
|
42
53
|
plus: true,
|
|
43
|
-
config
|
|
44
|
-
host,
|
|
45
|
-
port,
|
|
46
|
-
user: "root",
|
|
47
|
-
password: "password",
|
|
48
|
-
database: "mysql",
|
|
49
|
-
},
|
|
54
|
+
config,
|
|
50
55
|
}
|
|
51
|
-
}
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
57
|
+
const database = generator.guid().replaceAll("-", "")
|
|
58
|
+
await rawQuery(datasource, `CREATE DATABASE \`${database}\``)
|
|
59
|
+
datasource.config.database = database
|
|
60
|
+
return datasource
|
|
58
61
|
}
|
|
@@ -1,43 +1,39 @@
|
|
|
1
|
+
import { generator, testContainerUtils } from "@budibase/backend-core/tests"
|
|
1
2
|
import { Datasource, SourceName } from "@budibase/types"
|
|
2
|
-
import { GenericContainer, Wait
|
|
3
|
+
import { GenericContainer, Wait } from "testcontainers"
|
|
4
|
+
import { startContainer } from "."
|
|
3
5
|
|
|
4
|
-
let
|
|
6
|
+
let ports: Promise<testContainerUtils.Port[]>
|
|
5
7
|
|
|
6
|
-
export async function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
export async function getDatasource(): Promise<Datasource> {
|
|
9
|
+
if (!ports) {
|
|
10
|
+
ports = startContainer(
|
|
11
|
+
new GenericContainer("mongo:7.0-jammy")
|
|
12
|
+
.withExposedPorts(27017)
|
|
13
|
+
.withEnvironment({
|
|
14
|
+
MONGO_INITDB_ROOT_USERNAME: "mongo",
|
|
15
|
+
MONGO_INITDB_ROOT_PASSWORD: "password",
|
|
16
|
+
})
|
|
17
|
+
.withWaitStrategy(
|
|
18
|
+
Wait.forSuccessfulCommand(
|
|
19
|
+
`mongosh --eval "db.version()"`
|
|
20
|
+
).withStartupTimeout(10000)
|
|
21
|
+
)
|
|
17
22
|
)
|
|
18
|
-
|
|
19
|
-
}
|
|
23
|
+
}
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
if (!
|
|
23
|
-
|
|
25
|
+
const port = (await ports).find(x => x.container === 27017)
|
|
26
|
+
if (!port) {
|
|
27
|
+
throw new Error("MongoDB port not found")
|
|
24
28
|
}
|
|
25
|
-
|
|
26
|
-
const port = container.getMappedPort(27017)
|
|
29
|
+
|
|
27
30
|
return {
|
|
28
31
|
type: "datasource",
|
|
29
32
|
source: SourceName.MONGODB,
|
|
30
33
|
plus: false,
|
|
31
34
|
config: {
|
|
32
|
-
connectionString: `mongodb://mongo:password
|
|
33
|
-
db:
|
|
35
|
+
connectionString: `mongodb://mongo:password@127.0.0.1:${port.host}`,
|
|
36
|
+
db: generator.guid(),
|
|
34
37
|
},
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
|
-
|
|
38
|
-
export async function stop() {
|
|
39
|
-
if (container) {
|
|
40
|
-
await container.stop()
|
|
41
|
-
container = undefined
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,43 +1,41 @@
|
|
|
1
1
|
import { Datasource, SourceName } from "@budibase/types"
|
|
2
|
-
import { GenericContainer, Wait
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"mcr.microsoft.com/mssql/server:2022-latest"
|
|
9
|
-
)
|
|
10
|
-
.withExposedPorts(1433)
|
|
11
|
-
.withEnvironment({
|
|
12
|
-
ACCEPT_EULA: "Y",
|
|
13
|
-
MSSQL_SA_PASSWORD: "Password_123",
|
|
14
|
-
// This is important, as Microsoft allow us to use the "Developer" edition
|
|
15
|
-
// of SQL Server for development and testing purposes. We can't use other
|
|
16
|
-
// versions without a valid license, and we cannot use the Developer
|
|
17
|
-
// version in production.
|
|
18
|
-
MSSQL_PID: "Developer",
|
|
19
|
-
})
|
|
20
|
-
.withWaitStrategy(
|
|
21
|
-
Wait.forSuccessfulCommand(
|
|
22
|
-
"/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Password_123 -q 'SELECT 1'"
|
|
23
|
-
)
|
|
24
|
-
)
|
|
25
|
-
.start()
|
|
26
|
-
}
|
|
2
|
+
import { GenericContainer, Wait } from "testcontainers"
|
|
3
|
+
import mssql from "mssql"
|
|
4
|
+
import { generator, testContainerUtils } from "@budibase/backend-core/tests"
|
|
5
|
+
import { startContainer } from "."
|
|
6
|
+
|
|
7
|
+
let ports: Promise<testContainerUtils.Port[]>
|
|
27
8
|
|
|
28
|
-
export async function
|
|
29
|
-
if (!
|
|
30
|
-
|
|
9
|
+
export async function getDatasource(): Promise<Datasource> {
|
|
10
|
+
if (!ports) {
|
|
11
|
+
ports = startContainer(
|
|
12
|
+
new GenericContainer("mcr.microsoft.com/mssql/server:2022-latest")
|
|
13
|
+
.withExposedPorts(1433)
|
|
14
|
+
.withEnvironment({
|
|
15
|
+
ACCEPT_EULA: "Y",
|
|
16
|
+
MSSQL_SA_PASSWORD: "Password_123",
|
|
17
|
+
// This is important, as Microsoft allow us to use the "Developer" edition
|
|
18
|
+
// of SQL Server for development and testing purposes. We can't use other
|
|
19
|
+
// versions without a valid license, and we cannot use the Developer
|
|
20
|
+
// version in production.
|
|
21
|
+
MSSQL_PID: "Developer",
|
|
22
|
+
})
|
|
23
|
+
.withWaitStrategy(
|
|
24
|
+
Wait.forSuccessfulCommand(
|
|
25
|
+
"/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Password_123 -q 'SELECT 1'"
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
)
|
|
31
29
|
}
|
|
32
|
-
const host = container.getHost()
|
|
33
|
-
const port = container.getMappedPort(1433)
|
|
34
30
|
|
|
35
|
-
|
|
31
|
+
const port = (await ports).find(x => x.container === 1433)?.host
|
|
32
|
+
|
|
33
|
+
const datasource: Datasource = {
|
|
36
34
|
type: "datasource_plus",
|
|
37
35
|
source: SourceName.SQL_SERVER,
|
|
38
36
|
plus: true,
|
|
39
37
|
config: {
|
|
40
|
-
server:
|
|
38
|
+
server: "127.0.0.1",
|
|
41
39
|
port,
|
|
42
40
|
user: "sa",
|
|
43
41
|
password: "Password_123",
|
|
@@ -46,11 +44,28 @@ export async function datasource(): Promise<Datasource> {
|
|
|
46
44
|
},
|
|
47
45
|
},
|
|
48
46
|
}
|
|
47
|
+
|
|
48
|
+
const database = generator.guid().replaceAll("-", "")
|
|
49
|
+
await rawQuery(datasource, `CREATE DATABASE "${database}"`)
|
|
50
|
+
datasource.config!.database = database
|
|
51
|
+
|
|
52
|
+
return datasource
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
export async function
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
export async function rawQuery(ds: Datasource, sql: string) {
|
|
56
|
+
if (!ds.config) {
|
|
57
|
+
throw new Error("Datasource config is missing")
|
|
58
|
+
}
|
|
59
|
+
if (ds.source !== SourceName.SQL_SERVER) {
|
|
60
|
+
throw new Error("Datasource source is not SQL Server")
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const pool = new mssql.ConnectionPool(ds.config! as mssql.config)
|
|
64
|
+
const client = await pool.connect()
|
|
65
|
+
try {
|
|
66
|
+
const { recordset } = await client.query(sql)
|
|
67
|
+
return recordset
|
|
68
|
+
} finally {
|
|
69
|
+
await pool.close()
|
|
55
70
|
}
|
|
56
71
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Datasource, SourceName } from "@budibase/types"
|
|
2
|
-
import { GenericContainer, Wait
|
|
2
|
+
import { GenericContainer, Wait } from "testcontainers"
|
|
3
3
|
import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy"
|
|
4
|
+
import mysql from "mysql2/promise"
|
|
5
|
+
import { generator, testContainerUtils } from "@budibase/backend-core/tests"
|
|
6
|
+
import { startContainer } from "."
|
|
4
7
|
|
|
5
|
-
let
|
|
8
|
+
let ports: Promise<testContainerUtils.Port[]>
|
|
6
9
|
|
|
7
10
|
class MySQLWaitStrategy extends AbstractWaitStrategy {
|
|
8
11
|
async waitUntilReady(container: any, boundPorts: any, startTime?: Date) {
|
|
@@ -24,38 +27,50 @@ class MySQLWaitStrategy extends AbstractWaitStrategy {
|
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
export async function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
export async function datasource(): Promise<Datasource> {
|
|
36
|
-
if (!container) {
|
|
37
|
-
container = await start()
|
|
30
|
+
export async function getDatasource(): Promise<Datasource> {
|
|
31
|
+
if (!ports) {
|
|
32
|
+
ports = startContainer(
|
|
33
|
+
new GenericContainer("mysql:8.3")
|
|
34
|
+
.withExposedPorts(3306)
|
|
35
|
+
.withEnvironment({ MYSQL_ROOT_PASSWORD: "password" })
|
|
36
|
+
.withWaitStrategy(new MySQLWaitStrategy().withStartupTimeout(10000))
|
|
37
|
+
)
|
|
38
38
|
}
|
|
39
|
-
const host = container.getHost()
|
|
40
|
-
const port = container.getMappedPort(3306)
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
const port = (await ports).find(x => x.container === 3306)?.host
|
|
41
|
+
|
|
42
|
+
const datasource: Datasource = {
|
|
43
43
|
type: "datasource_plus",
|
|
44
44
|
source: SourceName.MYSQL,
|
|
45
45
|
plus: true,
|
|
46
46
|
config: {
|
|
47
|
-
host,
|
|
47
|
+
host: "127.0.0.1",
|
|
48
48
|
port,
|
|
49
49
|
user: "root",
|
|
50
50
|
password: "password",
|
|
51
51
|
database: "mysql",
|
|
52
52
|
},
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
const database = generator.guid().replaceAll("-", "")
|
|
56
|
+
await rawQuery(datasource, `CREATE DATABASE \`${database}\``)
|
|
57
|
+
datasource.config!.database = database
|
|
58
|
+
return datasource
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
export async function
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
export async function rawQuery(ds: Datasource, sql: string) {
|
|
62
|
+
if (!ds.config) {
|
|
63
|
+
throw new Error("Datasource config is missing")
|
|
64
|
+
}
|
|
65
|
+
if (ds.source !== SourceName.MYSQL) {
|
|
66
|
+
throw new Error("Datasource source is not MySQL")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const connection = await mysql.createConnection(ds.config)
|
|
70
|
+
try {
|
|
71
|
+
const [rows] = await connection.query(sql)
|
|
72
|
+
return rows
|
|
73
|
+
} finally {
|
|
74
|
+
connection.end()
|
|
60
75
|
}
|
|
61
76
|
}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import { Datasource, SourceName } from "@budibase/types"
|
|
2
|
-
import { GenericContainer, Wait
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.withExposedPorts(5432)
|
|
9
|
-
.withEnvironment({ POSTGRES_PASSWORD: "password" })
|
|
10
|
-
.withWaitStrategy(
|
|
11
|
-
Wait.forSuccessfulCommand(
|
|
12
|
-
"pg_isready -h localhost -p 5432"
|
|
13
|
-
).withStartupTimeout(10000)
|
|
14
|
-
)
|
|
15
|
-
.start()
|
|
16
|
-
}
|
|
2
|
+
import { GenericContainer, Wait } from "testcontainers"
|
|
3
|
+
import pg from "pg"
|
|
4
|
+
import { generator, testContainerUtils } from "@budibase/backend-core/tests"
|
|
5
|
+
import { startContainer } from "."
|
|
6
|
+
|
|
7
|
+
let ports: Promise<testContainerUtils.Port[]>
|
|
17
8
|
|
|
18
|
-
export async function
|
|
19
|
-
if (!
|
|
20
|
-
|
|
9
|
+
export async function getDatasource(): Promise<Datasource> {
|
|
10
|
+
if (!ports) {
|
|
11
|
+
ports = startContainer(
|
|
12
|
+
new GenericContainer("postgres:16.1-bullseye")
|
|
13
|
+
.withExposedPorts(5432)
|
|
14
|
+
.withEnvironment({ POSTGRES_PASSWORD: "password" })
|
|
15
|
+
.withWaitStrategy(
|
|
16
|
+
Wait.forSuccessfulCommand(
|
|
17
|
+
"pg_isready -h localhost -p 5432"
|
|
18
|
+
).withStartupTimeout(10000)
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
21
|
}
|
|
22
|
-
const host = container.getHost()
|
|
23
|
-
const port = container.getMappedPort(5432)
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
const port = (await ports).find(x => x.container === 5432)?.host
|
|
24
|
+
|
|
25
|
+
const datasource: Datasource = {
|
|
26
26
|
type: "datasource_plus",
|
|
27
27
|
source: SourceName.POSTGRES,
|
|
28
28
|
plus: true,
|
|
29
29
|
config: {
|
|
30
|
-
host,
|
|
30
|
+
host: "127.0.0.1",
|
|
31
31
|
port,
|
|
32
32
|
database: "postgres",
|
|
33
33
|
user: "postgres",
|
|
@@ -38,11 +38,28 @@ export async function datasource(): Promise<Datasource> {
|
|
|
38
38
|
ca: false,
|
|
39
39
|
},
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
const database = generator.guid().replaceAll("-", "")
|
|
43
|
+
await rawQuery(datasource, `CREATE DATABASE "${database}"`)
|
|
44
|
+
datasource.config!.database = database
|
|
45
|
+
|
|
46
|
+
return datasource
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
export async function
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
export async function rawQuery(ds: Datasource, sql: string) {
|
|
50
|
+
if (!ds.config) {
|
|
51
|
+
throw new Error("Datasource config is missing")
|
|
52
|
+
}
|
|
53
|
+
if (ds.source !== SourceName.POSTGRES) {
|
|
54
|
+
throw new Error("Datasource source is not Postgres")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const client = new pg.Client(ds.config)
|
|
58
|
+
await client.connect()
|
|
59
|
+
try {
|
|
60
|
+
const { rows } = await client.query(sql)
|
|
61
|
+
return rows
|
|
62
|
+
} finally {
|
|
63
|
+
await client.end()
|
|
47
64
|
}
|
|
48
65
|
}
|
package/src/tests/jestSetup.ts
CHANGED
|
@@ -2,17 +2,11 @@ import env from "../environment"
|
|
|
2
2
|
import { env as coreEnv, timers } from "@budibase/backend-core"
|
|
3
3
|
import { testContainerUtils } from "@budibase/backend-core/tests"
|
|
4
4
|
|
|
5
|
-
if (!process.env.DEBUG) {
|
|
6
|
-
global.console.log = jest.fn() // console.log are ignored in tests
|
|
7
|
-
global.console.warn = jest.fn() // console.warn are ignored in tests
|
|
8
|
-
}
|
|
9
|
-
|
|
10
5
|
if (!process.env.CI) {
|
|
11
|
-
// set a longer timeout in dev for debugging
|
|
12
|
-
// 100 seconds
|
|
6
|
+
// set a longer timeout in dev for debugging 100 seconds
|
|
13
7
|
jest.setTimeout(100 * 1000)
|
|
14
8
|
} else {
|
|
15
|
-
jest.setTimeout(
|
|
9
|
+
jest.setTimeout(30 * 1000)
|
|
16
10
|
}
|
|
17
11
|
|
|
18
12
|
testContainerUtils.setupEnv(env, coreEnv)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import TestConfiguration from "../TestConfiguration"
|
|
2
|
-
import { SuperTest, Test, Response } from "supertest"
|
|
2
|
+
import request, { SuperTest, Test, Response } from "supertest"
|
|
3
3
|
import { ReadStream } from "fs"
|
|
4
|
+
import { getServer } from "../../../app"
|
|
4
5
|
|
|
5
6
|
type Headers = Record<string, string | string[] | undefined>
|
|
6
7
|
type Method = "get" | "post" | "put" | "patch" | "delete"
|
|
@@ -76,7 +77,8 @@ export abstract class TestAPI {
|
|
|
76
77
|
protected _requestRaw = async (
|
|
77
78
|
method: "get" | "post" | "put" | "patch" | "delete",
|
|
78
79
|
url: string,
|
|
79
|
-
opts?: RequestOpts
|
|
80
|
+
opts?: RequestOpts,
|
|
81
|
+
attempt = 0
|
|
80
82
|
): Promise<Response> => {
|
|
81
83
|
const {
|
|
82
84
|
headers = {},
|
|
@@ -107,26 +109,29 @@ export abstract class TestAPI {
|
|
|
107
109
|
const headersFn = publicUser
|
|
108
110
|
? this.config.publicHeaders.bind(this.config)
|
|
109
111
|
: this.config.defaultHeaders.bind(this.config)
|
|
110
|
-
|
|
112
|
+
|
|
113
|
+
const app = getServer()
|
|
114
|
+
let req = request(app)[method](url)
|
|
115
|
+
req = req.set(
|
|
111
116
|
headersFn({
|
|
112
117
|
"x-budibase-include-stacktrace": "true",
|
|
113
118
|
})
|
|
114
119
|
)
|
|
115
120
|
if (headers) {
|
|
116
|
-
|
|
121
|
+
req = req.set(headers)
|
|
117
122
|
}
|
|
118
123
|
if (body) {
|
|
119
|
-
|
|
124
|
+
req = req.send(body)
|
|
120
125
|
}
|
|
121
126
|
for (const [key, value] of Object.entries(fields)) {
|
|
122
|
-
|
|
127
|
+
req = req.field(key, value)
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
for (const [key, value] of Object.entries(files)) {
|
|
126
131
|
if (isAttachedFile(value)) {
|
|
127
|
-
|
|
132
|
+
req = req.attach(key, value.file, value.name)
|
|
128
133
|
} else {
|
|
129
|
-
|
|
134
|
+
req = req.attach(key, value as any)
|
|
130
135
|
}
|
|
131
136
|
}
|
|
132
137
|
if (expectations?.headers) {
|
|
@@ -136,11 +141,25 @@ export abstract class TestAPI {
|
|
|
136
141
|
`Got an undefined expected value for header "${key}", if you want to check for the absence of a header, use headersNotPresent`
|
|
137
142
|
)
|
|
138
143
|
}
|
|
139
|
-
|
|
144
|
+
req = req.expect(key, value as any)
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
|
|
143
|
-
|
|
148
|
+
try {
|
|
149
|
+
return await req
|
|
150
|
+
} catch (e: any) {
|
|
151
|
+
// We've found that occasionally the connection between supertest and the
|
|
152
|
+
// server supertest starts gets reset. Not sure why, but retrying it
|
|
153
|
+
// appears to work. I don't particularly like this, but it's better than
|
|
154
|
+
// flakiness.
|
|
155
|
+
if (e.code === "ECONNRESET") {
|
|
156
|
+
if (attempt > 2) {
|
|
157
|
+
throw e
|
|
158
|
+
}
|
|
159
|
+
return await this._requestRaw(method, url, opts, attempt + 1)
|
|
160
|
+
}
|
|
161
|
+
throw e
|
|
162
|
+
}
|
|
144
163
|
}
|
|
145
164
|
|
|
146
165
|
protected _checkResponse = (
|
|
@@ -170,7 +189,18 @@ export abstract class TestAPI {
|
|
|
170
189
|
}
|
|
171
190
|
}
|
|
172
191
|
|
|
173
|
-
|
|
192
|
+
if (response.error) {
|
|
193
|
+
// Sometimes the error can be between supertest and the app, and when
|
|
194
|
+
// that happens response.error is sometimes populated with `text` that
|
|
195
|
+
// gives more detail about the error. The `message` is almost always
|
|
196
|
+
// useless from what I've seen.
|
|
197
|
+
if (response.error.text) {
|
|
198
|
+
response.error.message = response.error.text
|
|
199
|
+
}
|
|
200
|
+
throw new Error(message, { cause: response.error })
|
|
201
|
+
} else {
|
|
202
|
+
throw new Error(message)
|
|
203
|
+
}
|
|
174
204
|
}
|
|
175
205
|
|
|
176
206
|
if (expectations?.headersNotPresent) {
|