@@ -326,6 +326,91 @@ describe("init module", () => {
326326 expect ( calls [ calls . length - 1 ] . toLowerCase ( ) ) . toBe ( "rollback;" ) ;
327327 } ) ;
328328
329+ test ( "verifyInitSetup skips search_path check for supabase provider" , async ( ) => {
330+ const calls : string [ ] = [ ] ;
331+ const client = {
332+ query : async ( sql : string , params ?: any ) => {
333+ calls . push ( String ( sql ) ) ;
334+
335+ if ( String ( sql ) . toLowerCase ( ) . startsWith ( "begin isolation level repeatable read" ) ) {
336+ return { rowCount : 1 , rows : [ ] } ;
337+ }
338+ if ( String ( sql ) . toLowerCase ( ) === "rollback;" ) {
339+ return { rowCount : 1 , rows : [ ] } ;
340+ }
341+ // Return empty rolconfig - would fail without provider=supabase
342+ if ( String ( sql ) . includes ( "select rolconfig" ) ) {
343+ return { rowCount : 1 , rows : [ { rolconfig : null } ] } ;
344+ }
345+ if ( String ( sql ) . includes ( "from pg_catalog.pg_roles" ) ) {
346+ return { rowCount : 1 , rows : [ { rolname : DEFAULT_MONITORING_USER } ] } ;
347+ }
348+ if ( String ( sql ) . includes ( "has_database_privilege" ) ) {
349+ return { rowCount : 1 , rows : [ { ok : true } ] } ;
350+ }
351+ if ( String ( sql ) . includes ( "pg_has_role" ) ) {
352+ return { rowCount : 1 , rows : [ { ok : true } ] } ;
353+ }
354+ if ( String ( sql ) . includes ( "has_table_privilege" ) ) {
355+ return { rowCount : 1 , rows : [ { ok : true } ] } ;
356+ }
357+ if ( String ( sql ) . includes ( "to_regclass" ) ) {
358+ return { rowCount : 1 , rows : [ { ok : true } ] } ;
359+ }
360+ if ( String ( sql ) . includes ( "has_function_privilege" ) ) {
361+ return { rowCount : 1 , rows : [ { ok : true } ] } ;
362+ }
363+ if ( String ( sql ) . includes ( "has_schema_privilege" ) ) {
364+ return { rowCount : 1 , rows : [ { ok : true } ] } ;
365+ }
366+
367+ throw new Error ( `unexpected sql: ${ sql } params=${ JSON . stringify ( params ) } ` ) ;
368+ } ,
369+ } ;
370+
371+ // With provider=supabase, should pass even without search_path
372+ const r = await init . verifyInitSetup ( {
373+ client : client as any ,
374+ database : "mydb" ,
375+ monitoringUser : DEFAULT_MONITORING_USER ,
376+ includeOptionalPermissions : false ,
377+ provider : "supabase" ,
378+ } ) ;
379+ expect ( r . ok ) . toBe ( true ) ;
380+ expect ( r . missingRequired . length ) . toBe ( 0 ) ;
381+ // Should not have queried for rolconfig since we skip search_path check
382+ expect ( calls . some ( ( c ) => c . includes ( "select rolconfig" ) ) ) . toBe ( false ) ;
383+ } ) ;
384+
385+ test ( "buildInitPlan preserves comments when filtering ALTER USER" , async ( ) => {
386+ const plan = await init . buildInitPlan ( {
387+ database : "mydb" ,
388+ monitoringUser : DEFAULT_MONITORING_USER ,
389+ monitoringPassword : "pw" ,
390+ includeOptionalPermissions : false ,
391+ provider : "supabase" ,
392+ } ) ;
393+ const permStep = plan . steps . find ( ( s ) => s . name === "02.permissions" ) ;
394+ expect ( permStep ) . toBeDefined ( ) ;
395+ // Should have removed ALTER USER but kept comments
396+ expect ( permStep ! . sql . toLowerCase ( ) ) . not . toMatch ( / ^ \s * a l t e r \s + u s e r / m) ;
397+ // Should still have comment lines
398+ expect ( permStep ! . sql ) . toMatch ( / ^ - - / m) ;
399+ } ) ;
400+
401+ test ( "validateProvider returns null for known providers" , ( ) => {
402+ expect ( init . validateProvider ( undefined ) ) . toBe ( null ) ;
403+ expect ( init . validateProvider ( "self-managed" ) ) . toBe ( null ) ;
404+ expect ( init . validateProvider ( "supabase" ) ) . toBe ( null ) ;
405+ } ) ;
406+
407+ test ( "validateProvider returns warning for unknown providers" , ( ) => {
408+ const warning = init . validateProvider ( "unknown-provider" ) ;
409+ expect ( warning ) . not . toBe ( null ) ;
410+ expect ( warning ) . toMatch ( / U n k n o w n p r o v i d e r / ) ;
411+ expect ( warning ) . toMatch ( / u n k n o w n - p r o v i d e r / ) ;
412+ } ) ;
413+
329414 test ( "redactPasswordsInSql redacts password literals with embedded quotes" , async ( ) => {
330415 const plan = await init . buildInitPlan ( {
331416 database : "mydb" ,
@@ -355,6 +440,23 @@ describe("CLI commands", () => {
355440 expect ( r . stdout ) . toMatch ( new RegExp ( `grant connect on database "mydb" to "${ DEFAULT_MONITORING_USER } "` , "i" ) ) ;
356441 } ) ;
357442
443+ test ( "cli: prepare-db --print-sql with --provider supabase skips role step" , ( ) => {
444+ const r = runCli ( [ "prepare-db" , "--print-sql" , "-d" , "mydb" , "--password" , "monpw" , "--provider" , "supabase" ] ) ;
445+ expect ( r . status ) . toBe ( 0 ) ;
446+ expect ( r . stdout ) . toMatch ( / p r o v i d e r : s u p a b a s e / ) ;
447+ // Should not have 01.role step
448+ expect ( r . stdout ) . not . toMatch ( / - - 0 1 \. r o l e / ) ;
449+ // Should have 02.permissions step
450+ expect ( r . stdout ) . toMatch ( / - - 0 2 \. p e r m i s s i o n s / ) ;
451+ } ) ;
452+
453+ test ( "cli: prepare-db warns about unknown provider" , ( ) => {
454+ const r = runCli ( [ "prepare-db" , "--print-sql" , "-d" , "mydb" , "--password" , "monpw" , "--provider" , "unknown-cloud" ] ) ;
455+ expect ( r . status ) . toBe ( 0 ) ;
456+ // Should warn about unknown provider
457+ expect ( r . stderr ) . toMatch ( / U n k n o w n p r o v i d e r .* u n k n o w n - c l o u d / ) ;
458+ } ) ;
459+
358460 test ( "pgai wrapper forwards to postgresai CLI" , ( ) => {
359461 const r = runPgai ( [ "--help" ] ) ;
360462 expect ( r . status ) . toBe ( 0 ) ;
0 commit comments