testServer.ts 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. import { execFileSync } from 'node:child_process';
  2. import { mkdirSync, rmSync } from 'node:fs';
  3. import path from 'node:path';
  4. import type { AddressInfo } from 'node:net';
  5. import { createServer, type RequestListener } from 'node:http';
  6. const ROOT = path.resolve(import.meta.dirname, '../..');
  7. /**
  8. * Point Prisma at a throwaway SQLite file and apply migrations to it.
  9. * Must run before anything imports `src/store/db.ts`, because PrismaClient
  10. * reads DATABASE_URL when it is constructed.
  11. */
  12. export function prepareTestDatabase(name: string): string {
  13. const dir = path.join(ROOT, 'data');
  14. mkdirSync(dir, { recursive: true });
  15. const file = path.join(dir, `${name}.db`);
  16. rmSync(file, { force: true });
  17. rmSync(`${file}-journal`, { force: true });
  18. const url = `file:${file}`;
  19. process.env.DATABASE_URL = url;
  20. execFileSync('npx', ['prisma', 'migrate', 'deploy'], {
  21. cwd: ROOT,
  22. env: { ...process.env, DATABASE_URL: url },
  23. stdio: 'pipe',
  24. });
  25. return url;
  26. }
  27. export interface RunningServer {
  28. baseUrl: string;
  29. close: () => Promise<void>;
  30. }
  31. export async function startServer(app: RequestListener): Promise<RunningServer> {
  32. const server = createServer(app);
  33. await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
  34. const { port } = server.address() as AddressInfo;
  35. return {
  36. baseUrl: `http://127.0.0.1:${port}`,
  37. close: () =>
  38. new Promise<void>((resolve, reject) =>
  39. server.close((err) => (err ? reject(err) : resolve())),
  40. ),
  41. };
  42. }