1+ import { stripVTControlCharacters } from "util" ;
12import { createConnectedClient } from "../../internal/createConnectedClient" ;
3+ import { stderr , stdout } from "../../internal/logging" ;
24import { libSchema } from "../../internal/names" ;
35import { normalizeDsn } from "../../internal/normalizeDsn" ;
46import { ident , join , sql } from "../../internal/quote" ;
57import { runSql } from "../../internal/runSql" ;
68
7- jest . mock ( "../../internal/names" , ( ) => ( {
8- ...jest . requireActual ( "../../internal/names" ) ,
9- libSchema : ( ) =>
10- ident (
11- `microsharding_test_${ String . fromCharCode ( "a" . charCodeAt ( 0 ) + parseInt ( process . env [ "JEST_WORKER_ID" ] ! ) - 1 ) } ` ,
12- ) ,
13- } ) ) ;
14-
9+ /**
10+ * Creates a brand new test database with the name equals to the current DB name
11+ * with "~" + JEST_WORKER_ID suffix. The database is cleaned before returning.
12+ */
1513export async function ensureTestDBExistsBeforeAll ( {
1614 from,
1715 to,
1816 state,
17+ dbNameSuffix,
1918} : {
20- from : number ;
21- to : number ;
22- state : "active" | "inactive" ;
19+ from ?: number ;
20+ to ?: number ;
21+ state ?: "active" | "inactive" ;
22+ dbNameSuffix ?: string ;
2323} ) : Promise < string > {
2424 const env = {
2525 PGHOST : process . env [ "PGHOST" ] || process . env [ "DB_HOST_DEFAULT" ] ,
@@ -28,43 +28,116 @@ export async function ensureTestDBExistsBeforeAll({
2828 PGPASSWORD : process . env [ "PGPASSWORD" ] || process . env [ "DB_PASS" ] ,
2929 PGDATABASE : process . env [ "PGDATABASE" ] || process . env [ "DB_DATABASE" ] ,
3030 } ;
31- const testDsn = normalizeDsn ( env . PGHOST , env ) ! ;
32- if ( ! testDsn ) {
31+ const dsn = normalizeDsn ( env . PGHOST , env ) ! ;
32+ if ( ! dsn ) {
3333 throw new Error ( "No PGHOST or DB_HOST_DEFAULT passed." ) ;
3434 }
3535
36+ const testDB =
37+ new URL ( dsn ) . pathname . substring ( 1 ) +
38+ "~pg-micro-" +
39+ process . env [ "JEST_WORKER_ID" ] +
40+ ( dbNameSuffix ?? "" ) ;
41+ const testDsnUrl = new URL ( dsn ) ;
42+ testDsnUrl . pathname = `/${ testDB } ` ;
43+ const testDsn = testDsnUrl . toString ( ) ;
44+
45+ if (
46+ ! ( await runSql . one (
47+ dsn ,
48+ sql `SELECT datname FROM pg_database WHERE datname=${ testDB } ` ,
49+ ) )
50+ ) {
51+ await runSql ( dsn , sql `CREATE DATABASE ${ ident ( testDB ) } ` ) ;
52+ } else {
53+ const schemas = await runSql . column (
54+ testDsn ,
55+ sql `SELECT nspname FROM pg_namespace WHERE nspname = ${ libSchema ( ) . toString ( ) } OR nspname LIKE 'sh%'` ,
56+ ) ;
57+ if ( schemas . length > 0 ) {
58+ await runSql (
59+ testDsn ,
60+ sql `DROP SCHEMA IF EXISTS ${ join ( schemas . map ( ident ) , ", " ) } CASCADE` ,
61+ ) ;
62+ }
63+ }
64+
3665 await runSql (
3766 testDsn ,
3867 join (
3968 [
4069 sql `CREATE SCHEMA IF NOT EXISTS ${ libSchema ( ) } ;` ,
4170 sql `SET search_path TO ${ libSchema ( ) } ;` ,
4271 sql `\\ir ${ __dirname } /../../../sql/pg-microsharding-up.sql` ,
43- sql `
44- CREATE OR REPLACE FUNCTION ${ libSchema ( ) } .microsharding_schema_name_(
45- shard integer
46- ) RETURNS text
47- LANGUAGE sql
48- SET search_path FROM CURRENT
49- AS $$
50- SELECT ${ libSchema ( ) + "_" } || lpad($1::text, 4, '0')
51- $$;
52- ` ,
53- sql `SELECT ${ libSchema ( ) } .microsharding_ensure_exist(${ from } , ${ to } );` ,
54- state === "active"
55- ? sql `SELECT ${ libSchema ( ) } .microsharding_ensure_active(${ from } , ${ to } );`
56- : sql `SELECT ${ libSchema ( ) } .microsharding_ensure_inactive(${ from } , ${ to } );` ,
72+ ...( from && to
73+ ? [
74+ sql `SELECT ${ libSchema ( ) } .microsharding_ensure_exist(${ from } , ${ to } );` ,
75+ state === "active"
76+ ? sql `SELECT ${ libSchema ( ) } .microsharding_ensure_active(${ from } , ${ to } );`
77+ : sql `SELECT ${ libSchema ( ) } .microsharding_ensure_inactive(${ from } , ${ to } );` ,
78+ ]
79+ : [ ] ) ,
5780 ] ,
5881 "\n" ,
5982 ) ,
6083 ) ;
6184 return testDsn ;
6285}
6386
87+ /**
88+ * Creates a connected client to the test database.
89+ */
6490export async function createTestConnectedClient (
6591 dsn : string ,
6692) : ReturnType < typeof createConnectedClient > {
6793 const client = await createConnectedClient ( dsn ) ;
6894 await client . query ( `SET search_path = ${ libSchema ( ) } ` ) ;
6995 return client ;
7096}
97+
98+ /**
99+ * Intercepts stdout/stderr from logging module.
100+ */
101+ export function mockStd ( ) : {
102+ stdout : string ;
103+ stderr : string ;
104+ out : string ;
105+ print : ( ) => void ;
106+ } {
107+ const stdoutBuf : Buffer [ ] = [ ] ;
108+ const stderrBuf : Buffer [ ] = [ ] ;
109+ const outBuf : Buffer [ ] = [ ] ;
110+
111+ for ( const [ std , buf ] of [
112+ [ stdout , stdoutBuf ] ,
113+ [ stderr , stderrBuf ] ,
114+ ] as const ) {
115+ jest . spyOn ( std , "write" ) . mockImplementation ( ( chunk , maybeCb , cb ) => {
116+ chunk = typeof chunk === "string" ? Buffer . from ( chunk ) : chunk ;
117+ if ( typeof maybeCb === "function" ) {
118+ cb = maybeCb ;
119+ }
120+
121+ buf . push ( chunk ) ;
122+ outBuf . push ( chunk ) ;
123+ cb ?.( null ) ;
124+ return true ;
125+ } ) ;
126+ }
127+
128+ return {
129+ get stdout ( ) {
130+ return stripVTControlCharacters ( Buffer . concat ( stdoutBuf ) . toString ( ) ) ;
131+ } ,
132+ get stderr ( ) {
133+ return stripVTControlCharacters ( Buffer . concat ( stderrBuf ) . toString ( ) ) ;
134+ } ,
135+ get out ( ) {
136+ return stripVTControlCharacters ( Buffer . concat ( outBuf ) . toString ( ) ) ;
137+ } ,
138+ print ( ) {
139+ // eslint-disable-next-line no-console
140+ console . log ( this . out ) ;
141+ } ,
142+ } ;
143+ }
0 commit comments