11import { PostgreSqlContainer } from "@testcontainers/postgresql" ;
22import { describe , expect , it , vi } from "vitest" ;
3- import { buildReplicaClient , buildWriterClient , selectRunOpsTopology } from "~/db.server" ;
3+ import {
4+ buildReplicaClient ,
5+ buildWriterClient ,
6+ sameDatabaseTarget ,
7+ selectRunOpsTopology ,
8+ } from "~/db.server" ;
49
510const cp = { writer : { } as any , replica : { } as any } ;
611
@@ -44,7 +49,34 @@ describe("selectRunOpsTopology (pure)", () => {
4449 expect ( buildLegacyWriter ) . not . toHaveBeenCalled ( ) ;
4550 } ) ;
4651
47- it ( "split ON: legacy builds its OWN writer + replica (Track 2: no longer aliased to control-plane)" , ( ) => {
52+ it ( "split ON + legacySharesControlPlane: aliases legacy to control-plane and builds NO legacy client" , ( ) => {
53+ const newWriter = { tag : "nw" } as any ;
54+ const newReplica = { tag : "nr" } as any ;
55+ const buildNewWriter = vi . fn ( ) . mockReturnValue ( newWriter ) ;
56+ const buildNewReplica = vi . fn ( ) . mockReturnValue ( newReplica ) ;
57+ const buildLegacyWriter = vi . fn ( ) ;
58+ const buildLegacyReplica = vi . fn ( ) ;
59+ const topo = selectRunOpsTopology (
60+ {
61+ splitEnabled : true ,
62+ legacyUrl : "postgres://same" ,
63+ legacyReplicaUrl : "postgres://same-r" ,
64+ newUrl : "postgres://new" ,
65+ newReplicaUrl : "postgres://new-r" ,
66+ legacySharesControlPlane : true ,
67+ } ,
68+ { controlPlane : cp , buildNewWriter, buildNewReplica, buildLegacyWriter, buildLegacyReplica }
69+ ) ;
70+ // Legacy reuses the control-plane pair by reference — no second pool against the same server.
71+ expect ( topo . legacyRunOps ) . toBe ( cp ) ;
72+ expect ( buildLegacyWriter ) . not . toHaveBeenCalled ( ) ;
73+ expect ( buildLegacyReplica ) . not . toHaveBeenCalled ( ) ;
74+ // New run-ops still builds its own (independent) client.
75+ expect ( topo . newRunOps . writer ) . toBe ( newWriter ) ;
76+ expect ( topo . newRunOps . replica ) . toBe ( newReplica ) ;
77+ } ) ;
78+
79+ it ( "split ON (flag off): legacy builds its OWN writer + replica (independent, not aliased)" , ( ) => {
4880 const newWriter = { tag : "nw" } as any ;
4981 const newReplica = { tag : "nr" } as any ;
5082 const legacyWriter = { tag : "lw" } as any ;
@@ -112,6 +144,49 @@ describe("selectRunOpsTopology (pure)", () => {
112144 } ) ;
113145} ) ;
114146
147+ describe ( "sameDatabaseTarget" , ( ) => {
148+ it ( "same host/port/db/user is a match despite differing query params and password" , ( ) => {
149+ expect (
150+ sameDatabaseTarget (
151+ "postgresql://user:secret1@db.internal:5432/trigger?connection_limit=10&application_name=api" ,
152+ "postgresql://user:secret2@db.internal:5432/trigger?connection_limit=55"
153+ )
154+ ) . toBe ( true ) ;
155+ } ) ;
156+
157+ it ( "treats a missing port as the default 5432" , ( ) => {
158+ expect (
159+ sameDatabaseTarget (
160+ "postgresql://user@db.internal/trigger" ,
161+ "postgresql://user@db.internal:5432/trigger"
162+ )
163+ ) . toBe ( true ) ;
164+ } ) ;
165+
166+ it ( "differs on host, port, dbname, or user" , ( ) => {
167+ const base = "postgresql://user@db.internal:5432/trigger" ;
168+ expect ( sameDatabaseTarget ( base , "postgresql://user@other.internal:5432/trigger" ) ) . toBe ( false ) ;
169+ expect ( sameDatabaseTarget ( base , "postgresql://user@db.internal:6432/trigger" ) ) . toBe ( false ) ;
170+ expect ( sameDatabaseTarget ( base , "postgresql://user@db.internal:5432/other" ) ) . toBe ( false ) ;
171+ expect ( sameDatabaseTarget ( base , "postgresql://other@db.internal:5432/trigger" ) ) . toBe ( false ) ;
172+ } ) ;
173+
174+ it ( "returns false for undefined or unparseable input" , ( ) => {
175+ expect ( sameDatabaseTarget ( undefined , "postgresql://user@db/trigger" ) ) . toBe ( false ) ;
176+ expect ( sameDatabaseTarget ( "postgresql://user@db/trigger" , undefined ) ) . toBe ( false ) ;
177+ expect ( sameDatabaseTarget ( "not a url" , "also not a url" ) ) . toBe ( false ) ;
178+ } ) ;
179+
180+ it ( "matches the prod-shaped legacy-vs-cp writer pair, not the new replica" , ( ) => {
181+ const cpWriter = "postgresql://master:pw@rds-writer.internal:5432/pgtrigger" ;
182+ const legacyWriter =
183+ "postgresql://master:pw@rds-writer.internal:5432/pgtrigger?connection_limit=25" ;
184+ const newReplica = "postgresql://master%7Creplica:pw@ps-host.internal:5432/pgtrigger" ;
185+ expect ( sameDatabaseTarget ( cpWriter , legacyWriter ) ) . toBe ( true ) ;
186+ expect ( sameDatabaseTarget ( cpWriter , newReplica ) ) . toBe ( false ) ;
187+ } ) ;
188+ } ) ;
189+
115190describe ( "selectRunOpsTopology (integration, real containers)" , ( ) => {
116191 it ( "split OFF: opens exactly one DB; all run-ops handles share the control-plane client" , async ( ) => {
117192 const pg = await new PostgreSqlContainer ( "docker.io/postgres:14" ) . start ( ) ;
@@ -152,7 +227,7 @@ describe("selectRunOpsTopology (integration, real containers)", () => {
152227 }
153228 } , 60_000 ) ;
154229
155- it ( "split ON: constructs CP + INDEPENDENT legacy-run-ops + new-run-ops + replicas" , async ( ) => {
230+ it ( "split ON (flag off) : constructs CP + INDEPENDENT legacy-run-ops + new-run-ops + replicas" , async ( ) => {
156231 const rds = await new PostgreSqlContainer ( "docker.io/postgres:14" ) . start ( ) ;
157232 const ps = await new PostgreSqlContainer ( "docker.io/postgres:17" ) . start ( ) ;
158233 try {
@@ -161,8 +236,8 @@ describe("selectRunOpsTopology (integration, real containers)", () => {
161236 const topo = selectRunOpsTopology (
162237 {
163238 splitEnabled : true ,
164- // Same-DSN stage: legacy points at the same physical DB as the control plane, but the
165- // client is an INDEPENDENT instance ( its own pool) — never the cp object.
239+ // Divergent-DB stage (legacySharesControlPlane omitted) : legacy builds an INDEPENDENT
240+ // client with its own pool — never the cp object.
166241 legacyUrl : rds . getConnectionUri ( ) ,
167242 legacyReplicaUrl : rds . getConnectionUri ( ) ,
168243 newUrl : ps . getConnectionUri ( ) ,
@@ -195,4 +270,47 @@ describe("selectRunOpsTopology (integration, real containers)", () => {
195270 await ps . stop ( ) ;
196271 }
197272 } , 120_000 ) ;
273+
274+ it ( "split ON + legacySharesControlPlane: legacy reuses the CP pool, only the new DB opens a client" , async ( ) => {
275+ const rds = await new PostgreSqlContainer ( "docker.io/postgres:14" ) . start ( ) ;
276+ const ps = await new PostgreSqlContainer ( "docker.io/postgres:17" ) . start ( ) ;
277+ try {
278+ const cpWriter = buildWriterClient ( { url : rds . getConnectionUri ( ) , clientType : "cp" } ) ;
279+ const cp = { writer : cpWriter , replica : cpWriter } ;
280+ const legacyBuilds : string [ ] = [ ] ;
281+ const topo = selectRunOpsTopology (
282+ {
283+ splitEnabled : true ,
284+ legacyUrl : rds . getConnectionUri ( ) ,
285+ legacyReplicaUrl : rds . getConnectionUri ( ) ,
286+ newUrl : ps . getConnectionUri ( ) ,
287+ legacySharesControlPlane : true ,
288+ } ,
289+ {
290+ controlPlane : cp ,
291+ buildNewWriter : ( url , ct ) => buildWriterClient ( { url, clientType : ct } ) as any ,
292+ buildNewReplica : ( url , ct ) => buildReplicaClient ( { url, clientType : ct } ) as any ,
293+ buildLegacyWriter : ( url , ct ) => {
294+ legacyBuilds . push ( url ) ;
295+ return buildWriterClient ( { url, clientType : ct } ) ;
296+ } ,
297+ buildLegacyReplica : ( url , ct ) => {
298+ legacyBuilds . push ( url ) ;
299+ return buildReplicaClient ( { url, clientType : ct } ) ;
300+ } ,
301+ }
302+ ) ;
303+ expect ( legacyBuilds ) . toHaveLength ( 0 ) ; // no redundant legacy pool against the shared server
304+ expect ( topo . legacyRunOps ) . toBe ( cp ) ;
305+ expect ( topo . legacyRunOps . writer ) . toBe ( cpWriter ) ;
306+ expect ( topo . newRunOps . writer ) . not . toBe ( cpWriter ) ;
307+ await topo . legacyRunOps . writer . $queryRawUnsafe ( "SELECT 1" ) ; // legacy queries run on the CP pool
308+ await topo . newRunOps . writer . $queryRawUnsafe ( "SELECT 1" ) ;
309+ await cpWriter . $disconnect ( ) ;
310+ await topo . newRunOps . writer . $disconnect ( ) ;
311+ } finally {
312+ await rds . stop ( ) ;
313+ await ps . stop ( ) ;
314+ }
315+ } , 120_000 ) ;
198316} ) ;
0 commit comments