Got container reuse strategy in place, need to convert tests now.

This commit is contained in:
Sam Rose 2024-03-26 11:22:40 +00:00
parent df52b6937a
commit 158964c4d2
No known key found for this signature in database
7 changed files with 98 additions and 123 deletions

View File

@ -16,7 +16,7 @@ import {
import _ from "lodash" import _ from "lodash"
import { generator } from "@budibase/backend-core/tests" import { generator } from "@budibase/backend-core/tests"
import { utils } from "@budibase/backend-core" import { utils } from "@budibase/backend-core"
import { databaseTestProviders } from "../integrations/tests/utils" import { DatabaseName, getDatasource } from "../integrations/tests/utils"
import { Client } from "pg" import { Client } from "pg"
// @ts-ignore // @ts-ignore
fetch.mockSearch() fetch.mockSearch()
@ -41,18 +41,17 @@ describe("postgres integrations", () => {
makeRequest = generateMakeRequest(apiKey, true) makeRequest = generateMakeRequest(apiKey, true)
postgresDatasource = await config.api.datasource.create( postgresDatasource = await config.api.datasource.create(
await databaseTestProviders.postgres.datasource() await getDatasource(DatabaseName.POSTGRES)
) )
}) })
afterAll(async () => {
await databaseTestProviders.postgres.stop()
})
beforeEach(async () => { beforeEach(async () => {
async function createAuxTable(prefix: string) { async function createAuxTable(prefix: string) {
return await config.createTable({ return await config.createTable({
name: `${prefix}_${generator.word({ length: 6 })}`, name: `${prefix}_${generator
.guid()
.replaceAll("-", "")
.substring(0, 6)}`,
type: "table", type: "table",
primary: ["id"], primary: ["id"],
primaryDisplay: "title", primaryDisplay: "title",
@ -89,7 +88,7 @@ describe("postgres integrations", () => {
} }
primaryPostgresTable = await config.createTable({ primaryPostgresTable = await config.createTable({
name: `p_${generator.word({ length: 6 })}`, name: `p_${generator.guid().replaceAll("-", "").substring(0, 6)}`,
type: "table", type: "table",
primary: ["id"], primary: ["id"],
schema: { schema: {
@ -251,7 +250,7 @@ describe("postgres integrations", () => {
async function createDefaultPgTable() { async function createDefaultPgTable() {
return await config.createTable({ return await config.createTable({
name: generator.word({ length: 10 }), name: generator.guid().replaceAll("-", "").substring(0, 10),
type: "table", type: "table",
primary: ["id"], primary: ["id"],
schema: { schema: {
@ -1043,7 +1042,7 @@ describe("postgres integrations", () => {
it("should be able to verify the connection", async () => { it("should be able to verify the connection", async () => {
await config.api.datasource.verify( await config.api.datasource.verify(
{ {
datasource: await databaseTestProviders.postgres.datasource(), datasource: await getDatasource(DatabaseName.POSTGRES),
}, },
{ {
body: { body: {
@ -1054,7 +1053,7 @@ describe("postgres integrations", () => {
}) })
it("should state an invalid datasource cannot connect", async () => { it("should state an invalid datasource cannot connect", async () => {
const dbConfig = await databaseTestProviders.postgres.datasource() const dbConfig = await getDatasource(DatabaseName.POSTGRES)
await config.api.datasource.verify( await config.api.datasource.verify(
{ {
datasource: { datasource: {
@ -1089,21 +1088,21 @@ describe("postgres integrations", () => {
describe("POST /api/datasources/:datasourceId/schema", () => { describe("POST /api/datasources/:datasourceId/schema", () => {
let client: Client let client: Client
let tableName: string
beforeEach(async () => { beforeEach(async () => {
client = new Client( tableName = generator.guid().replaceAll("-", "").substring(0, 10)
(await databaseTestProviders.postgres.datasource()).config! client = new Client((await getDatasource(DatabaseName.POSTGRES)).config!)
)
await client.connect() await client.connect()
}) })
afterEach(async () => { afterEach(async () => {
await client.query(`DROP TABLE IF EXISTS "table"`) await client.query(`DROP TABLE IF EXISTS "${tableName}"`)
await client.end() await client.end()
}) })
it("recognises when a table has no primary key", async () => { it("recognises when a table has no primary key", async () => {
await client.query(`CREATE TABLE "table" (id SERIAL)`) await client.query(`CREATE TABLE "${tableName}" (id SERIAL)`)
const response = await makeRequest( const response = await makeRequest(
"post", "post",
@ -1111,12 +1110,14 @@ describe("postgres integrations", () => {
) )
expect(response.body.errors).toEqual({ expect(response.body.errors).toEqual({
table: "Table must have a primary key.", [tableName]: "Table must have a primary key.",
}) })
}) })
it("recognises when a table is using a reserved column name", async () => { it("recognises when a table is using a reserved column name", async () => {
await client.query(`CREATE TABLE "table" (_id SERIAL PRIMARY KEY) `) await client.query(
`CREATE TABLE "${tableName}" (_id SERIAL PRIMARY KEY) `
)
const response = await makeRequest( const response = await makeRequest(
"post", "post",
@ -1124,18 +1125,22 @@ describe("postgres integrations", () => {
) )
expect(response.body.errors).toEqual({ expect(response.body.errors).toEqual({
table: "Table contains invalid columns.", [tableName]: "Table contains invalid columns.",
}) })
}) })
}) })
describe("Integration compatibility with postgres search_path", () => { describe("Integration compatibility with postgres search_path", () => {
let client: Client, pathDatasource: Datasource let client: Client,
const schema1 = "test1", pathDatasource: Datasource,
schema2 = "test-2" schema1: string,
schema2: string
beforeAll(async () => { beforeEach(async () => {
const dsConfig = await databaseTestProviders.postgres.datasource() schema1 = generator.guid().replaceAll("-", "")
schema2 = generator.guid().replaceAll("-", "")
const dsConfig = await getDatasource(DatabaseName.POSTGRES)
const dbConfig = dsConfig.config! const dbConfig = dsConfig.config!
client = new Client(dbConfig) client = new Client(dbConfig)
@ -1153,7 +1158,7 @@ describe("postgres integrations", () => {
pathDatasource = await config.api.datasource.create(pathConfig) pathDatasource = await config.api.datasource.create(pathConfig)
}) })
afterAll(async () => { afterEach(async () => {
await client.query(`DROP SCHEMA "${schema1}" CASCADE;`) await client.query(`DROP SCHEMA "${schema1}" CASCADE;`)
await client.query(`DROP SCHEMA "${schema2}" CASCADE;`) await client.query(`DROP SCHEMA "${schema2}" CASCADE;`)
await client.end() await client.end()

View File

@ -1,19 +1,50 @@
jest.unmock("pg") jest.unmock("pg")
import { Datasource } from "@budibase/types" import { Datasource } from "@budibase/types"
import * as postgres from "./postgres" import { postgres } from "./postgres"
import * as mongodb from "./mongodb" import { mongodb } from "./mongodb"
import * as mysql from "./mysql" import { mysql } from "./mysql"
import * as mssql from "./mssql" import { mssql } from "./mssql"
import * as mariadb from "./mariadb" import { mariadb } from "./mariadb"
import { StartedTestContainer } from "testcontainers"
jest.setTimeout(30000) export type DatasourceProvider = () => Promise<Datasource>
export interface DatabaseProvider { export enum DatabaseName {
start(): Promise<StartedTestContainer> POSTGRES = "postgres",
stop(): Promise<void> MONGODB = "mongodb",
datasource(): Promise<Datasource> MYSQL = "mysql",
SQL_SERVER = "mssql",
MARIADB = "mariadb",
}
const providers: Record<DatabaseName, DatasourceProvider> = {
[DatabaseName.POSTGRES]: postgres,
[DatabaseName.MONGODB]: mongodb,
[DatabaseName.MYSQL]: mysql,
[DatabaseName.SQL_SERVER]: mssql,
[DatabaseName.MARIADB]: mariadb,
}
export function getDatasourceProviders(
...sourceNames: DatabaseName[]
): Promise<Datasource>[] {
return sourceNames.map(sourceName => providers[sourceName]())
}
export function getDatasourceProvider(
sourceName: DatabaseName
): DatasourceProvider {
return providers[sourceName]
}
export function getDatasource(sourceName: DatabaseName): Promise<Datasource> {
return providers[sourceName]()
}
export async function getDatasources(
...sourceNames: DatabaseName[]
): Promise<Datasource[]> {
return Promise.all(sourceNames.map(sourceName => providers[sourceName]()))
} }
export const databaseTestProviders = { export const databaseTestProviders = {

View File

@ -1,9 +1,7 @@
import { Datasource, SourceName } from "@budibase/types" import { Datasource, SourceName } from "@budibase/types"
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" import { GenericContainer, Wait } from "testcontainers"
import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy"
let container: StartedTestContainer | undefined
class MariaDBWaitStrategy extends AbstractWaitStrategy { class MariaDBWaitStrategy extends AbstractWaitStrategy {
async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) {
// Because MariaDB first starts itself up, runs an init script, then restarts, // Because MariaDB first starts itself up, runs an init script, then restarts,
@ -21,18 +19,15 @@ class MariaDBWaitStrategy extends AbstractWaitStrategy {
} }
} }
export async function start(): Promise<StartedTestContainer> { export async function mariadb(): Promise<Datasource> {
return await new GenericContainer("mariadb:lts") const container = await new GenericContainer("mariadb:lts")
.withName("budibase-test-mariadb")
.withReuse()
.withExposedPorts(3306) .withExposedPorts(3306)
.withEnvironment({ MARIADB_ROOT_PASSWORD: "password" }) .withEnvironment({ MARIADB_ROOT_PASSWORD: "password" })
.withWaitStrategy(new MariaDBWaitStrategy()) .withWaitStrategy(new MariaDBWaitStrategy())
.start() .start()
}
export async function datasource(): Promise<Datasource> {
if (!container) {
container = await start()
}
const host = container.getHost() const host = container.getHost()
const port = container.getMappedPort(3306) const port = container.getMappedPort(3306)
@ -49,10 +44,3 @@ export async function datasource(): Promise<Datasource> {
}, },
} }
} }
export async function stop() {
if (container) {
await container.stop()
container = undefined
}
}

View File

@ -1,10 +1,10 @@
import { Datasource, SourceName } from "@budibase/types" import { Datasource, SourceName } from "@budibase/types"
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" import { GenericContainer, Wait } from "testcontainers"
let container: StartedTestContainer | undefined export async function mongodb(): Promise<Datasource> {
const container = await new GenericContainer("mongo:7.0-jammy")
export async function start(): Promise<StartedTestContainer> { .withName("budibase-test-mongodb")
return await new GenericContainer("mongo:7.0-jammy") .withReuse()
.withExposedPorts(27017) .withExposedPorts(27017)
.withEnvironment({ .withEnvironment({
MONGO_INITDB_ROOT_USERNAME: "mongo", MONGO_INITDB_ROOT_USERNAME: "mongo",
@ -16,14 +16,10 @@ export async function start(): Promise<StartedTestContainer> {
).withStartupTimeout(10000) ).withStartupTimeout(10000)
) )
.start() .start()
}
export async function datasource(): Promise<Datasource> {
if (!container) {
container = await start()
}
const host = container.getHost() const host = container.getHost()
const port = container.getMappedPort(27017) const port = container.getMappedPort(27017)
return { return {
type: "datasource", type: "datasource",
source: SourceName.MONGODB, source: SourceName.MONGODB,
@ -34,10 +30,3 @@ export async function datasource(): Promise<Datasource> {
}, },
} }
} }
export async function stop() {
if (container) {
await container.stop()
container = undefined
}
}

View File

@ -1,12 +1,12 @@
import { Datasource, SourceName } from "@budibase/types" import { Datasource, SourceName } from "@budibase/types"
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" import { GenericContainer, Wait } from "testcontainers"
let container: StartedTestContainer | undefined export async function mssql(): Promise<Datasource> {
const container = await new GenericContainer(
export async function start(): Promise<StartedTestContainer> {
return await new GenericContainer(
"mcr.microsoft.com/mssql/server:2022-latest" "mcr.microsoft.com/mssql/server:2022-latest"
) )
.withName("budibase-test-mssql")
.withReuse()
.withExposedPorts(1433) .withExposedPorts(1433)
.withEnvironment({ .withEnvironment({
ACCEPT_EULA: "Y", ACCEPT_EULA: "Y",
@ -23,12 +23,7 @@ export async function start(): Promise<StartedTestContainer> {
) )
) )
.start() .start()
}
export async function datasource(): Promise<Datasource> {
if (!container) {
container = await start()
}
const host = container.getHost() const host = container.getHost()
const port = container.getMappedPort(1433) const port = container.getMappedPort(1433)
@ -47,10 +42,3 @@ export async function datasource(): Promise<Datasource> {
}, },
} }
} }
export async function stop() {
if (container) {
await container.stop()
container = undefined
}
}

View File

@ -1,9 +1,7 @@
import { Datasource, SourceName } from "@budibase/types" import { Datasource, SourceName } from "@budibase/types"
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" import { GenericContainer, Wait } from "testcontainers"
import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy"
let container: StartedTestContainer | undefined
class MySQLWaitStrategy extends AbstractWaitStrategy { class MySQLWaitStrategy extends AbstractWaitStrategy {
async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { async waitUntilReady(container: any, boundPorts: any, startTime?: Date) {
// Because MySQL first starts itself up, runs an init script, then restarts, // Because MySQL first starts itself up, runs an init script, then restarts,
@ -24,18 +22,14 @@ class MySQLWaitStrategy extends AbstractWaitStrategy {
} }
} }
export async function start(): Promise<StartedTestContainer> { export async function mysql(): Promise<Datasource> {
return await new GenericContainer("mysql:8.3") const container = await new GenericContainer("mysql:8.3")
.withName("budibase-test-mysql")
.withReuse()
.withExposedPorts(3306) .withExposedPorts(3306)
.withEnvironment({ MYSQL_ROOT_PASSWORD: "password" }) .withEnvironment({ MYSQL_ROOT_PASSWORD: "password" })
.withWaitStrategy(new MySQLWaitStrategy().withStartupTimeout(10000)) .withWaitStrategy(new MySQLWaitStrategy().withStartupTimeout(10000))
.start() .start()
}
export async function datasource(): Promise<Datasource> {
if (!container) {
container = await start()
}
const host = container.getHost() const host = container.getHost()
const port = container.getMappedPort(3306) const port = container.getMappedPort(3306)
@ -52,10 +46,3 @@ export async function datasource(): Promise<Datasource> {
}, },
} }
} }
export async function stop() {
if (container) {
await container.stop()
container = undefined
}
}

View File

@ -1,10 +1,10 @@
import { Datasource, SourceName } from "@budibase/types" import { Datasource, SourceName } from "@budibase/types"
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" import { GenericContainer, Wait } from "testcontainers"
let container: StartedTestContainer | undefined export async function postgres(): Promise<Datasource> {
const container = await new GenericContainer("postgres:16.1-bullseye")
export async function start(): Promise<StartedTestContainer> { .withName("budibase-test-postgres")
return await new GenericContainer("postgres:16.1-bullseye") .withReuse()
.withExposedPorts(5432) .withExposedPorts(5432)
.withEnvironment({ POSTGRES_PASSWORD: "password" }) .withEnvironment({ POSTGRES_PASSWORD: "password" })
.withWaitStrategy( .withWaitStrategy(
@ -13,12 +13,6 @@ export async function start(): Promise<StartedTestContainer> {
).withStartupTimeout(10000) ).withStartupTimeout(10000)
) )
.start() .start()
}
export async function datasource(): Promise<Datasource> {
if (!container) {
container = await start()
}
const host = container.getHost() const host = container.getHost()
const port = container.getMappedPort(5432) const port = container.getMappedPort(5432)
@ -39,10 +33,3 @@ export async function datasource(): Promise<Datasource> {
}, },
} }
} }
export async function stop() {
if (container) {
await container.stop()
container = undefined
}
}