diff --git a/src/server/templates/typescript.ts b/src/server/templates/typescript.ts index 1b527686..75ca7de4 100644 --- a/src/server/templates/typescript.ts +++ b/src/server/templates/typescript.ts @@ -681,8 +681,10 @@ export type Database = { {} as Record ) for (const fnName in schemaFunctionsGroupedByName) { - schemaFunctionsGroupedByName[fnName].sort((a, b) => - b.fn.definition.localeCompare(a.fn.definition) + schemaFunctionsGroupedByName[fnName].sort( + (a, b) => + a.fn.argument_types.localeCompare(b.fn.argument_types) || + a.fn.return_type.localeCompare(b.fn.return_type) ) } diff --git a/test/server/typegen.ts b/test/server/typegen.ts index 2005339e..852eddce 100644 --- a/test/server/typegen.ts +++ b/test/server/typegen.ts @@ -667,9 +667,7 @@ test('typegen: typescript', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -678,7 +676,7 @@ test('typegen: typescript', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -701,7 +699,9 @@ test('typegen: typescript', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -710,7 +710,7 @@ test('typegen: typescript', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -732,16 +732,14 @@ test('typegen: typescript', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -762,14 +760,16 @@ test('typegen: typescript', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -777,28 +777,28 @@ test('typegen: typescript', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -865,32 +865,34 @@ test('typegen: typescript', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -909,22 +911,20 @@ test('typegen: typescript', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -958,28 +958,28 @@ test('typegen: typescript', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -1854,9 +1854,7 @@ test('typegen w/ one-to-one relationships', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -1865,7 +1863,7 @@ test('typegen w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -1888,7 +1886,9 @@ test('typegen w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -1897,7 +1897,7 @@ test('typegen w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -1919,16 +1919,14 @@ test('typegen w/ one-to-one relationships', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -1949,14 +1947,16 @@ test('typegen w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -1964,28 +1964,28 @@ test('typegen w/ one-to-one relationships', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -2052,32 +2052,34 @@ test('typegen w/ one-to-one relationships', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -2096,22 +2098,20 @@ test('typegen w/ one-to-one relationships', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -2145,28 +2145,28 @@ test('typegen w/ one-to-one relationships', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -3041,9 +3041,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -3052,7 +3050,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -3075,7 +3073,9 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -3084,7 +3084,7 @@ test('typegen: typescript w/ one-to-one relationships', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -3106,16 +3106,14 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -3136,14 +3134,16 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -3151,28 +3151,28 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -3239,32 +3239,34 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -3283,22 +3285,20 @@ test('typegen: typescript w/ one-to-one relationships', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -3332,28 +3332,28 @@ test('typegen: typescript w/ one-to-one relationships', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -4233,9 +4233,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { } get_single_user_summary_from_view: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { todo_count: number | null todo_details: string[] | null @@ -4244,7 +4242,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "users_view" + from: "*" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -4267,7 +4265,9 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { todo_count: number | null todo_details: string[] | null @@ -4276,7 +4276,7 @@ test('typegen: typescript w/ postgrestVersion', async () => { user_status: Database["public"]["Enums"]["user_status"] | null } SetofOptions: { - from: "*" + from: "users_view" to: "user_todos_summary_view" isOneToOne: true isSetofReturn: true @@ -4298,16 +4298,14 @@ test('typegen: typescript w/ postgrestVersion', async () => { } get_todos_from_user: | { - Args: { - userview_row: Database["public"]["Views"]["users_view"]["Row"] - } + Args: { search_user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users_view" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -4328,14 +4326,16 @@ test('typegen: typescript w/ postgrestVersion', async () => { } } | { - Args: { search_user_id: number } + Args: { + userview_row: Database["public"]["Views"]["users_view"]["Row"] + } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "users_view" to: "todos" isOneToOne: false isSetofReturn: true @@ -4343,28 +4343,28 @@ test('typegen: typescript w/ postgrestVersion', async () => { } get_todos_setof_rows: | { - Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } + Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "users" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { todo_row: Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_row: Database["public"]["Tables"]["users"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "users" to: "todos" isOneToOne: false isSetofReturn: true @@ -4431,32 +4431,34 @@ test('typegen: typescript w/ postgrestVersion', async () => { } postgres_fdw_handler: { Args: never; Returns: unknown } postgrest_resolvable_with_override_function: + | { Args: never; Returns: undefined } | { Args: { a: string }; Returns: number } + | { Args: { b: number }; Returns: string } | { - Args: { user_id: number } + Args: { completed: boolean; todo_id: number } Returns: { - decimal: number | null + details: string | null id: number - name: string | null - status: Database["public"]["Enums"]["user_status"] | null + "user-id": number }[] SetofOptions: { from: "*" - to: "users" + to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { completed: boolean; todo_id: number } + Args: { user_id: number } Returns: { - details: string | null + decimal: number | null id: number - "user-id": number + name: string | null + status: Database["public"]["Enums"]["user_status"] | null }[] SetofOptions: { from: "*" - to: "todos" + to: "users" isOneToOne: false isSetofReturn: true } @@ -4475,22 +4477,20 @@ test('typegen: typescript w/ postgrestVersion', async () => { isSetofReturn: true } } - | { Args: { b: number }; Returns: string } - | { Args: never; Returns: undefined } postgrest_unresolvable_function: + | { Args: never; Returns: undefined } | { - Args: { a: string } + Args: { a: number } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } | { - Args: { a: number } + Args: { a: string } Returns: { error: true } & "Could not choose the best candidate function between: public.postgrest_unresolvable_function(a => int4), public.postgrest_unresolvable_function(a => text). Try renaming the parameters or the function itself in the database so function overloading can be resolved" } - | { Args: never; Returns: undefined } search_todos_by_details: { Args: { search_details: string } Returns: { @@ -4524,28 +4524,28 @@ test('typegen: typescript w/ postgrestVersion', async () => { } test_unnamed_row_setof: | { - Args: { user_id: number } + Args: { "": Database["public"]["Tables"]["todos"]["Row"] } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "*" + from: "todos" to: "todos" isOneToOne: false isSetofReturn: true } } | { - Args: { "": Database["public"]["Tables"]["todos"]["Row"] } + Args: { user_id: number } Returns: { details: string | null id: number "user-id": number }[] SetofOptions: { - from: "todos" + from: "*" to: "todos" isOneToOne: false isSetofReturn: true @@ -4992,6 +4992,125 @@ test('typegen: typescript consistent types definitions orders', async () => { expect(firstCall).toEqual(secondCall) }) +test('typegen: typescript function override order stability', async () => { + // Helper function to clean up test entities + const cleanupTestEntities = async () => { + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Drop functions with all possible signatures + DROP FUNCTION IF EXISTS test_func_override(integer, text) CASCADE; + DROP FUNCTION IF EXISTS test_func_override(text, integer) CASCADE; + DROP FUNCTION IF EXISTS test_func_override(boolean, integer, text) CASCADE; + DROP FUNCTION IF EXISTS test_func_override(text, boolean) CASCADE; + `, + }, + }) + } + + // Clean up any existing test entities + await cleanupTestEntities() + + // === FIRST ROUND: Create function overrides in order 1 === + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Create function overrides in specific order + CREATE FUNCTION test_func_override(param_a integer, param_b text) + RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a text, param_b integer) + RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text) + RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a text, param_b boolean) + RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE; + `, + }, + }) + + // Generate types for first configuration + const { body: firstCall } = await app.inject({ + method: 'GET', + path: '/generators/typescript', + query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' }, + }) + + // === SECOND ROUND: Modify function definitions without changing signatures === + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Modify function definitions (using CREATE OR REPLACE) + -- This should preserve the order + CREATE OR REPLACE FUNCTION test_func_override(param_a integer, param_b text) + RETURNS integer AS 'SELECT param_a + 100' LANGUAGE sql IMMUTABLE; + + CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b integer) + RETURNS text AS 'SELECT param_a || ''_'' || param_b::text' LANGUAGE sql IMMUTABLE; + + CREATE OR REPLACE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text) + RETURNS boolean AS 'SELECT NOT param_a' LANGUAGE sql IMMUTABLE; + + CREATE OR REPLACE FUNCTION test_func_override(param_a text, param_b boolean) + RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a || ''_true'' ELSE ''false'' END' LANGUAGE sql IMMUTABLE; + `, + }, + }) + + // Generate types for second configuration (after modifying definitions) + const { body: secondCall } = await app.inject({ + method: 'GET', + path: '/generators/typescript', + query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' }, + }) + + // === THIRD ROUND: Drop and recreate in different order === + await cleanupTestEntities() + + // Create functions in reverse order + await app.inject({ + method: 'POST', + path: '/query', + payload: { + query: ` + -- Create function overrides in reverse order + CREATE FUNCTION test_func_override(param_a text, param_b boolean) + RETURNS text AS 'SELECT CASE WHEN param_b THEN param_a ELSE '''' END' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a boolean, param_b integer, param_c text) + RETURNS boolean AS 'SELECT param_a' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a text, param_b integer) + RETURNS text AS 'SELECT param_a || param_b::text' LANGUAGE sql IMMUTABLE; + + CREATE FUNCTION test_func_override(param_a integer, param_b text) + RETURNS integer AS 'SELECT param_a + 1' LANGUAGE sql IMMUTABLE; + `, + }, + }) + + // Generate types for third configuration (recreated in different order) + const { body: thirdCall } = await app.inject({ + method: 'GET', + path: '/generators/typescript', + query: { detect_one_to_one_relationships: 'true', postgrest_version: '13' }, + }) + + // Clean up test entities + await cleanupTestEntities() + + expect(firstCall).toEqual(secondCall) + expect(secondCall).toEqual(thirdCall) +}) + test('typegen: go', async () => { const { body } = await app.inject({ method: 'GET', path: '/generators/go' }) expect(body).toMatchInlineSnapshot(`