0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-06 22:10:10 -05:00
astro/packages/db/test/test-utils.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

172 lines
4.1 KiB
JavaScript
Raw Normal View History

import { createServer } from 'node:http';
import { LibsqlError, createClient } from '@libsql/client';
import { z } from 'zod';
import { cli } from '../dist/core/cli/index.js';
import { resolveDbConfig } from '../dist/core/load-file.js';
[db] Fix duplicate calls to recreate tables on startup (#10919) * fix: move recreateTables() to integration hooks * feat: recreate and seed at load, not in virtual runtime * feat: eager build db on startup and seed file change * fix: respect database_file in dbUrl * chore: remove duplicate recreateTables call * chore: remove now self-explanatory comments * fix: remove invalidateModule call for eager loading * feat: respect seed package paths * fix: remove duplicate recreateTables() call * refactor: move recreateTables() to vite-plugin-db * refactor: move queries.ts from runtime/ to core/ * fix: update test import to core/queries * refactor: move executeSeedFile to vite-plugin-db * refactor: extract seeding and recreating to helper fns * chore: changeset * chore: revert connectToStudio refactor * wip: log db url * fix(test): normalize astro_database_file flag for windows * Revert "wip: log db url" This reverts commit 558e2de67a09a611377929b625127c649b8504d6. * Revert "Revert "wip: log db url"" This reverts commit ffd004e00dff485b7bc5ddde0278dde6ff058b9e. * fix: correctly resolve relative paths with unit test * chore: remove unused dbDirPath Co-authored-by: Chris Swithinbank <swithinbank@gmail.com> * chore: remove unused import Co-authored-by: Chris Swithinbank <swithinbank@gmail.com> * chore: remove unused type Co-authored-by: Chris Swithinbank <swithinbank@gmail.com> * fix: remove bad import * [db] Load seed files with vite dev server (#10941) * feat: load seed files with full vite dev server * chore: remove unused export --------- Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
2024-05-03 10:08:50 -05:00
import { getCreateIndexQueries, getCreateTableQuery } from '../dist/core/queries.js';
const singleQuerySchema = z.object({
sql: z.string(),
args: z.array(z.any()).or(z.record(z.string(), z.any())),
});
const querySchema = singleQuerySchema.or(z.array(singleQuerySchema));
let portIncrementer = 8030;
/**
* @param {import('astro').AstroConfig} astroConfig
* @param {number | undefined} port
*/
export async function setupRemoteDbServer(astroConfig) {
const port = portIncrementer++;
process.env.ASTRO_STUDIO_REMOTE_DB_URL = `http://localhost:${port}`;
process.env.ASTRO_INTERNAL_TEST_REMOTE = true;
const server = createRemoteDbServer().listen(port);
const { dbConfig } = await resolveDbConfig(astroConfig);
const setupQueries = [];
for (const [name, table] of Object.entries(dbConfig?.tables ?? {})) {
const createQuery = getCreateTableQuery(name, table);
const indexQueries = getCreateIndexQueries(name, table);
setupQueries.push(createQuery, ...indexQueries);
}
await fetch(`http://localhost:${port}/db/query`, {
method: 'POST',
body: JSON.stringify(setupQueries.map((sql) => ({ sql, args: [] }))),
headers: {
'Content-Type': 'application/json',
},
});
await cli({
config: astroConfig,
flags: {
_: [undefined, 'astro', 'db', 'execute', 'db/seed.ts'],
remote: true,
},
});
return {
server,
async stop() {
delete process.env.ASTRO_STUDIO_REMOTE_DB_URL;
delete process.env.ASTRO_INTERNAL_TEST_REMOTE;
return new Promise((resolve, reject) => {
server.close((err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
},
};
}
export async function initializeRemoteDb(astroConfig) {
await cli({
config: astroConfig,
flags: {
_: [undefined, 'astro', 'db', 'push'],
remote: true,
},
});
await cli({
config: astroConfig,
flags: {
_: [undefined, 'astro', 'db', 'execute', 'db/seed.ts'],
remote: true,
},
});
}
/**
* Clears the environment variables related to Astro DB and Astro Studio.
*/
export function clearEnvironment() {
const keys = Array.from(Object.keys(process.env));
for (const key of keys) {
if (key.startsWith('ASTRO_DB_') || key.startsWith('ASTRO_STUDIO_')) {
delete process.env[key];
}
}
}
function createRemoteDbServer() {
const dbClient = createClient({
url: ':memory:',
});
const server = createServer((req, res) => {
if (
!req.url.startsWith('/db/query') ||
req.method !== 'POST' ||
req.headers['content-type'] !== 'application/json'
) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
success: false,
}),
);
return;
}
const rawBody = [];
req.on('data', (chunk) => {
rawBody.push(chunk);
});
req.on('end', async () => {
let json;
try {
json = JSON.parse(Buffer.concat(rawBody).toString());
} catch {
applyParseError(res);
return;
}
const parsed = querySchema.safeParse(json);
if (parsed.success === false) {
applyParseError(res);
return;
}
const body = parsed.data;
try {
const result = Array.isArray(body)
? await dbClient.batch(body)
: await dbClient.execute(body);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(result));
} catch (e) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.statusMessage = e.message;
res.end(
JSON.stringify({
success: false,
error: {
code: e instanceof LibsqlError ? e.code : 'SQLITE_QUERY_FAILED',
details: e.message,
2024-05-13 11:21:32 -05:00
},
}),
);
}
});
});
server.on('close', () => {
dbClient.close();
});
return server;
}
function applyParseError(res) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.statusMessage = 'Invalid request body';
res.end(
JSON.stringify({
// Use JSON response with `success: boolean` property
// to match remote error responses.
success: false,
}),
);
}