Files
2026-07-13 12:10:05 +08:00

146 lines
3.8 KiB
TypeScript

import Docker from 'dockerode';
import { sql } from 'drizzle-orm';
import { bigserial, geometry, line, pgTable, point } from 'drizzle-orm/pg-core';
import { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import getPort from 'get-port';
import postgres, { type Sql } from 'postgres';
import { v4 as uuid } from 'uuid';
import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest';
const ENABLE_LOGGING = false;
let pgContainer: Docker.Container;
let docker: Docker;
let client: Sql;
let db: PostgresJsDatabase;
async function createDockerDB(): Promise<string> {
const inDocker = (docker = new Docker());
const port = await getPort({ port: 5432 });
const image = 'postgis/postgis:16-3.4';
const pullStream = await docker.pull(image);
await new Promise((resolve, reject) =>
inDocker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err)))
);
pgContainer = await docker.createContainer({
Image: image,
Env: ['POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres'],
name: `drizzle-integration-tests-${uuid()}`,
HostConfig: {
AutoRemove: true,
PortBindings: {
'5432/tcp': [{ HostPort: `${port}` }],
},
},
});
await pgContainer.start();
return `postgres://postgres:postgres@localhost:${port}/postgres`;
}
beforeAll(async () => {
const connectionString = process.env['PG_POSTGIS_CONNECTION_STRING'] ?? (await createDockerDB());
const sleep = 1000;
let timeLeft = 20000;
let connected = false;
let lastError: unknown | undefined;
do {
try {
client = postgres(connectionString, {
max: 1,
onnotice: () => {
// disable notices
},
});
await client`select 1`;
connected = true;
break;
} catch (e) {
lastError = e;
await new Promise((resolve) => setTimeout(resolve, sleep));
timeLeft -= sleep;
}
} while (timeLeft > 0);
if (!connected) {
console.error('Cannot connect to Postgres');
await client?.end().catch(console.error);
await pgContainer?.stop().catch(console.error);
throw lastError;
}
db = drizzle(client, { logger: ENABLE_LOGGING });
await db.execute(sql`CREATE EXTENSION IF NOT EXISTS postgis;`);
});
afterAll(async () => {
await client?.end().catch(console.error);
await pgContainer?.stop().catch(console.error);
});
const items = pgTable('items', {
id: bigserial('id', { mode: 'number' }).primaryKey(),
point: point('point'),
pointObj: point('point_xy', { mode: 'xy' }),
line: line('line'),
lineObj: line('line_abc', { mode: 'abc' }),
geo: geometry('geo', { type: 'point' }),
geoObj: geometry('geo_obj', { type: 'point', mode: 'xy' }),
geoSrid: geometry('geo_options', { type: 'point', mode: 'xy', srid: 4000 }),
});
beforeEach(async () => {
await db.execute(sql`drop table if exists items cascade`);
await db.execute(sql`
CREATE TABLE items (
id bigserial PRIMARY KEY,
"point" point,
"point_xy" point,
"line" line,
"line_abc" line,
"geo" geometry(point),
"geo_obj" geometry(point),
"geo_options" geometry(point,4000)
);
`);
});
test('insert + select', async () => {
const insertedValues = await db.insert(items).values([{
point: [1, 2],
pointObj: { x: 1, y: 2 },
line: [1, 2, 3],
lineObj: { a: 1, b: 2, c: 3 },
geo: [1, 2],
geoObj: { x: 1, y: 2 },
geoSrid: { x: 1, y: 2 },
}]).returning();
const response = await db.select().from(items);
expect(insertedValues).toStrictEqual([{
id: 1,
point: [1, 2],
pointObj: { x: 1, y: 2 },
line: [1, 2, 3],
lineObj: { a: 1, b: 2, c: 3 },
geo: [1, 2],
geoObj: { x: 1, y: 2 },
geoSrid: { x: 1, y: 2 },
}]);
expect(response).toStrictEqual([{
id: 1,
point: [1, 2],
pointObj: { x: 1, y: 2 },
line: [1, 2, 3],
lineObj: { a: 1, b: 2, c: 3 },
geo: [1, 2],
geoObj: { x: 1, y: 2 },
geoSrid: { x: 1, y: 2 },
}]);
});