@@ -77,4 +77,84 @@ func TestMigrationFile(t *testing.T) {
7777 assert .ErrorContains (t , err , "ERROR: schema \" public\" already exists (SQLSTATE 42P06)" )
7878 assert .ErrorContains (t , err , "At statement: 0\n create schema public" )
7979 })
80+
81+ t .Run ("provides helpful hint for extension type errors" , func (t * testing.T ) {
82+ migration := MigrationFile {
83+ Statements : []string {"CREATE TABLE test (path ltree NOT NULL)" },
84+ Version : "0" ,
85+ }
86+ // Setup mock postgres
87+ conn := pgtest .NewConn ()
88+ defer conn .Close (t )
89+ conn .Query (migration .Statements [0 ]).
90+ ReplyError ("42704" , `type "ltree" does not exist` ).
91+ Query (INSERT_MIGRATION_VERSION , "0" , "" , migration .Statements ).
92+ Reply ("INSERT 0 1" )
93+ // Run test
94+ err := migration .ExecBatch (context .Background (), conn .MockClient (t ))
95+ // Check error
96+ assert .ErrorContains (t , err , `type "ltree" does not exist` )
97+ assert .ErrorContains (t , err , "Hint: This type may be defined in a schema" )
98+ assert .ErrorContains (t , err , "extensions.ltree" )
99+ assert .ErrorContains (t , err , "supabase migration new --help" )
100+ assert .ErrorContains (t , err , "At statement: 0" )
101+ })
102+
103+ t .Run ("skips hint for schema-qualified type errors" , func (t * testing.T ) {
104+ migration := MigrationFile {
105+ Statements : []string {"CREATE TABLE test (path extensions.ltree NOT NULL)" },
106+ Version : "0" ,
107+ }
108+ // Setup mock postgres
109+ conn := pgtest .NewConn ()
110+ defer conn .Close (t )
111+ conn .Query (migration .Statements [0 ]).
112+ ReplyError ("42704" , `type "extensions.ltree" does not exist` ).
113+ Query (INSERT_MIGRATION_VERSION , "0" , "" , migration .Statements ).
114+ Reply ("INSERT 0 1" )
115+ // Run test
116+ err := migration .ExecBatch (context .Background (), conn .MockClient (t ))
117+ // Check error - should NOT contain hint since type is already schema-qualified
118+ assert .ErrorContains (t , err , `type "extensions.ltree" does not exist` )
119+ assert .NotContains (t , err .Error (), "Hint: This type may be defined in a schema" )
120+ })
121+ }
122+
123+ func TestExtractTypeName (t * testing.T ) {
124+ t .Run ("extracts type name from standard error message" , func (t * testing.T ) {
125+ result := extractTypeName (`type "ltree" does not exist` )
126+ assert .Equal (t , "ltree" , result )
127+ })
128+
129+ t .Run ("extracts schema-qualified type name" , func (t * testing.T ) {
130+ result := extractTypeName (`type "extensions.ltree" does not exist` )
131+ assert .Equal (t , "extensions.ltree" , result )
132+ })
133+
134+ t .Run ("extracts type with underscores" , func (t * testing.T ) {
135+ result := extractTypeName (`type "my_custom_type" does not exist` )
136+ assert .Equal (t , "my_custom_type" , result )
137+ })
138+
139+ t .Run ("returns empty string for non-matching message" , func (t * testing.T ) {
140+ result := extractTypeName (`column "name" does not exist` )
141+ assert .Equal (t , "" , result )
142+ })
143+
144+ t .Run ("returns empty string for empty message" , func (t * testing.T ) {
145+ result := extractTypeName ("" )
146+ assert .Equal (t , "" , result )
147+ })
148+
149+ t .Run ("handles type names with numbers" , func (t * testing.T ) {
150+ result := extractTypeName (`type "type123" does not exist` )
151+ assert .Equal (t , "type123" , result )
152+ })
153+ }
154+
155+ func TestIsSchemaQualified (t * testing.T ) {
156+ assert .True (t , IsSchemaQualified ("extensions.ltree" ))
157+ assert .True (t , IsSchemaQualified ("public.my_type" ))
158+ assert .False (t , IsSchemaQualified ("ltree" ))
159+ assert .False (t , IsSchemaQualified ("" ))
80160}
0 commit comments