Repository: liam-hq/liam
Pull request #1506:  test: add database CI workflow and PGTap tests for invite_organization_member
Author: login: hoshinotsuyoshi
Labels: name: Review effort 2/5
Created: 2025-04-25T04:00:27Z | Merged: 2025-04-25T10:10:36Z

## Issue

- resolve:

## Why is this change needed?
<!-- Please explain briefly why this change is necessary -->

Adding Database CI Workflow and PGTap Tests.

### Purpose of the Changes

This change establishes an automated testing environment for database functions, specifically for the `invite_organization_member` RPC function. This enhancement strengthens quality assurance for database functions and enables database tests to be run as part of the continuous integration (CI) process.

### Intent of the Changes

1. **Enhanced Quality Assurance**: Automatically verifying the behavior of database functions enables early detection and correction of bugs.
2. **Regression Testing**: Ensures that future changes do not break existing functionality.
3. **Improved Development Process**: Simplifies test execution in local environments, increasing developer productivity.
4. **Documentation Role**: Test code explicitly demonstrates the expected behavior of functions, aiding in code understanding.

### CI and Local Testing Environment Setup

### GitHub Actions Workflow

Added `.github/workflows/database-ci.yml` to automatically run database tests under the following conditions:
- When PRs are created/updated and database-related files are changed
- When merge group events occur
- When manually triggered

This workflow starts a Supabase environment, performs test setup, and then runs PGTap tests.

#### Test Environment Setup

Created `.github/workflows/setup-testing.sql` to provide the following features needed for test execution:
- Installation of the PGTap extension
- Creation of a test schema
- Functions for creating test users
- Functions for retrieving user IDs

#### Local Testing Script

Added `frontend/packages/db/supabase/tests/run-tests.sh` to allow developers to easily run tests in their local environment. This script provides the following functionality:
- Checks if Supabase is running and starts it if necessary
- Sets up the test environment
- Executes the tests

### Testing Implementation for invite_organization_member Function (01-invite_organization_member.test.sql)

Implemented tests using PGTap in `frontend/packages/db/supabase/tests/database/01-invite_organization_member.test.sql`. This test file covers the following test cases:

1. **New User Invitation**: Verifies that a new member can be invited to an organization
   - Validates that the invitation succeeds and returns the correct response
   - Confirms that the invitation record is correctly created in the database

2. **Duplicate Prevention**: Verifies that an error is returned when attempting to invite a user who is already a member of the organization
   - Validates that an error message is returned when attempting to invite an existing member

3. **Re-invitation Functionality**: Verifies the behavior when re-inviting a user who already has a pending invitation
   - Validates that the invitation succeeds and returns the correct response
   - Confirms that a new invitation record is not created, but the existing invitation is updated

4. **Case Insensitivity**: Verifies that email addresses are processed correctly regardless of case
   - Validates that invitations can be sent correctly even with email addresses containing uppercase letters

5. **Timestamp Update**: Verifies that the `invited_at` timestamp is appropriately updated during re-invitation
   - Validates that the invitation date is updated to the latest value when re-inviting

These tests ensure that the main functionality and edge cases of the `invite_organization_member` function are properly covered.

### Future Outlook

Building on this infrastructure, we plan to gradually add tests for other database functions. This will improve the overall quality and reliability of the database layer.



## What would you like reviewers to focus on?
<!-- What specific aspects are you requesting review for? -->

- Whether the test cases for `invite_organization_member` cover the expected edge cases and typical usage patterns appropriately  
- If the implementation of `.github/workflows/database-ci.yml` correctly integrates with GitHub Actions and runs the database tests as intended  
- Whether the `setup-testing.sql` script safely prepares the test environment without affecting production schema or data  
- Usability and clarity of the local test runner script `run-tests.sh`, including its robustness in various local setups  
- Suggestions on naming, organization, or documentation that could make the testing setup more maintainable or extensible in the future


## Testing Verification
<!-- Please describe how you verified these changes in your local environment using text/images/video -->

- CI check only. 🥲 
-  running ./frontend/packages/db/supabase/tests/run-tests.sh succeeds.

## What was done
<!-- This section will be filled by PR-Agent when the Pull Request is opened -->

### 🤖 Generated by PR Agent at 94b3a41543df86a878504cdf73838c2aadda1435

- Introduced automated database CI workflow using GitHub Actions and PGTap.
  - Runs on PRs, merge groups, or manual triggers.
  - Sets up Supabase and test environment automatically.
- Added comprehensive PGTap tests for `invite_organization_member` function.
  - Covers new user invitation, duplicate prevention, case-insensitive checks, and timestamp updates.
- Provided SQL setup for test users and helper functions.
  - Includes user creation and retrieval utilities for tests.
- Added shell script for easy local test execution.
  - Handles Supabase startup, environment setup, and test execution.


## Detailed Changes
<!-- This section will be filled by PR-Agent when the Pull Request is opened -->

<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>database-ci.yml</strong><dd><code>Add GitHub Actions workflow for database CI and PGTap tests</code></dd></summary>
<hr>

.github/workflows/database-ci.yml

<li>Introduces a GitHub Actions workflow for database CI.<br> <li> Runs PGTap tests on PRs, merge groups, or manual triggers.<br> <li> Automates Supabase startup and test environment setup.


</details>


  </td>
  <td><a href="https://github.com/liam-hq/liam/pull/1506/files#diff-45ac8470b923fac6df98ac18589b8cef7f12b9a5eb299b0d0729821e4aa9d538">+33/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>01-invite_organization_member.test.sql</strong><dd><code>Add PGTap tests for invite_organization_member RPC function</code></dd></summary>
<hr>

frontend/packages/db/supabase/tests/database/01-invite_organization_member.test.sql

<li>Implements 7 PGTap tests for <code>invite_organization_member</code>.<br> <li> Tests new invitations, duplicate prevention, re-invites, and case <br>insensitivity.<br> <li> Sets up test users, organization, and context for realistic scenarios.


</details>


  </td>
  <td><a href="https://github.com/liam-hq/liam/pull/1506/files#diff-921cdcc841a3dabeff6991a5e64f67a6fd93f2e56096ed25fdf2db19773b40e0">+119/-0</a>&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>setup-testing.sql</strong><dd><code>Add SQL setup for PGTap and test user utilities</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

.github/workflows/setup-testing.sql

<li>Creates and configures PGTap extension and test schema.<br> <li> Adds helper functions for test user creation and user ID retrieval.<br> <li> Ensures idempotent setup for test users and schema.


</details>


  </td>
  <td><a href="https://github.com/liam-hq/liam/pull/1506/files#diff-b183d6bf8b8ce766c3877c3f4fbce3b7cd77d99d9296cda2fd814e439e2c16c2">+84/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>run-tests.sh</strong><dd><code>Add shell script for local database test automation</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

frontend/packages/db/supabase/tests/run-tests.sh

<li>Adds shell script for local test execution.<br> <li> Automates Supabase startup and test environment setup.<br> <li> Runs database tests via Supabase CLI.


</details>


  </td>
  <td><a href="https://github.com/liam-hq/liam/pull/1506/files#diff-336cbc14a3d13be36af37285382ef6d54cfa574e97e47447e8fc959148c4dedf">+45/-0</a>&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

## Additional Notes
<!-- Any additional information for reviewers -->

___

> <details> <summary>  Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about Qodo Merge usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>


Diff:
diff --git a/.github/workflows/database-ci.yml b/.github/workflows/database-ci.yml
new file mode 100644
index 0000000000..ad20a03a53
--- /dev/null
+++ b/.github/workflows/database-ci.yml
@@ -0,0 +1,33 @@
+name: Database CI
+
+on:
+  pull_request:
+    paths:
+      - ".github/workflows/database-ci.yml"
+      - "frontend/packages/db/**"
+  merge_group:
+  workflow_dispatch:
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  pg_tap_tests:
+    name: PGTap Tests
+    timeout-minutes: 10
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: supabase/setup-cli@d347ba47d3fb7eeeddbbc793bc8d4779caf773ea # v1.5.0
+        with:
+          version: latest
+      - name: Supabase Start
+        run: supabase start
+      - name: Setup supabase testing
+        run: |
+          psql -v ON_ERROR_STOP=1 -U postgres -d postgres -h localhost -p 54322 -f frontend/packages/db/supabase/setup-testing.sql
+        env:
+          PGPASSWORD: postgres
+      - name: Run Tests
+        run: supabase test db
diff --git a/frontend/packages/db/supabase/setup-testing.sql b/frontend/packages/db/supabase/setup-testing.sql
new file mode 100644
index 0000000000..9785c428d0
--- /dev/null
+++ b/frontend/packages/db/supabase/setup-testing.sql
@@ -0,0 +1,84 @@
+-- Create pgtap extension if it doesn't exist
+create extension if not exists pgtap with schema extensions;
+
+-- Create tests schema
+CREATE SCHEMA IF NOT EXISTS tests;
+
+-- Drop existing functions to avoid parameter name change errors
+DROP FUNCTION IF EXISTS tests.create_supabase_user(text, text, jsonb);
+DROP FUNCTION IF EXISTS tests.get_supabase_uid(text);
+
+-- Create test user function with upsert logic
+CREATE FUNCTION tests.create_supabase_user(
+    p_email text,
+    p_name text DEFAULT NULL,
+    p_metadata jsonb DEFAULT '{}'::jsonb
+) RETURNS uuid AS $$
+DECLARE
+    user_id uuid;
+BEGIN
+    -- Check if user already exists in auth.users
+    SELECT id INTO user_id FROM auth.users WHERE email = p_email LIMIT 1;
+    
+    IF user_id IS NULL THEN
+        -- Create new user if not exists
+        INSERT INTO auth.users (
+            instance_id,
+            id,
+            aud,
+            role,
+            email,
+            encrypted_password,
+            email_confirmed_at,
+            recovery_sent_at,
+            last_sign_in_at,
+            raw_app_meta_data,
+            raw_user_meta_data,
+            created_at,
+            updated_at,
+            confirmation_token,
+            email_change,
+            email_change_token_new,
+            recovery_token
+        ) VALUES (
+            '00000000-0000-0000-0000-000000000000',
+            gen_random_uuid(),
+            'authenticated',
+            'authenticated',
+            p_email,
+            '$2a$10$Ht.3/NM5XGZnvtjP/X.YyeS0/QUJp.O6vQbGPT4WrCeY9fFIFVQQu', -- 'password123'
+            now(),
+            NULL,
+            now(),
+            '{"provider": "email", "providers": ["email"]}'::jsonb,
+            CASE WHEN p_name IS NULL THEN '{}' ELSE jsonb_build_object('name', p_name) END || p_metadata,
+            now(),
+            now(),
+            '',
+            '',
+            '',
+            ''
+        ) RETURNING id INTO user_id;
+        
+        -- Try to insert into public.users table, ignore if already exists
+        BEGIN
+            INSERT INTO public.users (id, email, name)
+            VALUES (user_id, p_email, COALESCE(p_name, p_email));
+        EXCEPTION WHEN unique_violation THEN
+            -- User already exists in public.users, do nothing
+        END;
+    END IF;
+
+    RETURN user_id;
+END;
+$$ LANGUAGE plpgsql;
+
+-- Get user ID function
+CREATE OR REPLACE FUNCTION tests.get_supabase_uid(email text) RETURNS uuid AS $$
+DECLARE
+    user_id uuid;
+BEGIN
+    SELECT id INTO user_id FROM auth.users WHERE auth.users.email = $1 LIMIT 1;
+    RETURN user_id;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/frontend/packages/db/supabase/tests/database/01-invite_organization_member.test.sql b/frontend/packages/db/supabase/tests/database/01-invite_organization_member.test.sql
new file mode 100644
index 0000000000..03d5e739f2
--- /dev/null
+++ b/frontend/packages/db/supabase/tests/database/01-invite_organization_member.test.sql
@@ -0,0 +1,119 @@
+-- Test file for invite_organization_member function
+BEGIN;
+
+-- Load the pgtap extension
+SELECT plan(7);
+
+-- Create test users and organization
+SELECT tests.create_supabase_user('inviter@example.com', 'inviter_user');
+SELECT tests.create_supabase_user('existing@example.com', 'existing_user');
+SELECT tests.create_supabase_user('new@example.com', 'new_user');
+SELECT tests.create_supabase_user('reinvite@example.com', 'reinvite_user');
+
+-- Create test organization
+INSERT INTO organizations (id, name)
+VALUES ('11111111-1111-1111-1111-111111111111', 'Test Organization')
+ON CONFLICT DO NOTHING;
+
+-- Store user IDs in temporary variables
+DO $$
+DECLARE
+    v_inviter_id uuid;
+    v_existing_id uuid;
+    v_new_id uuid;
+    v_reinvite_id uuid;
+BEGIN
+    -- Get user IDs
+    SELECT tests.get_supabase_uid('inviter@example.com') INTO v_inviter_id;
+    SELECT tests.get_supabase_uid('existing@example.com') INTO v_existing_id;
+    SELECT tests.get_supabase_uid('new@example.com') INTO v_new_id;
+    SELECT tests.get_supabase_uid('reinvite@example.com') INTO v_reinvite_id;
+    
+    -- Add inviter and existing user to the organization
+    INSERT INTO organization_members (user_id, organization_id)
+    VALUES 
+      (v_inviter_id, '11111111-1111-1111-1111-111111111111'),
+      (v_existing_id, '11111111-1111-1111-1111-111111111111')
+    ON CONFLICT DO NOTHING;
+    
+    -- Create an existing invitation for reinvite user
+    INSERT INTO invitations (id, email, invite_by_user_id, organization_id, invited_at)
+    VALUES (
+      '22222222-2222-2222-2222-222222222222',
+      'reinvite@example.com',
+      v_inviter_id,
+      '11111111-1111-1111-1111-111111111111',
+      NOW() - INTERVAL '1 day'
+    )
+    ON CONFLICT DO NOTHING;
+END $$;
+
+-- Set up authentication context for tests
+DO $$
+DECLARE
+    v_inviter_id uuid;
+BEGIN
+    -- Get inviter user ID
+    SELECT tests.get_supabase_uid('inviter@example.com') INTO v_inviter_id;
+    
+    -- Set up auth context to simulate the inviter user
+    EXECUTE format('SET LOCAL ROLE authenticated; SET LOCAL "request.jwt.claims" = ''{"sub": "%s"}''', v_inviter_id);
+END $$;
+
+-- Test 1: Successfully invite a new member
+SELECT is(
+  (SELECT invite_organization_member('new@example.com', '11111111-1111-1111-1111-111111111111')),
+  '{"success": true, "error": null}'::jsonb,
+  'Should successfully invite a new member'
+);
+
+-- Test 2: Verify the invitation was created
+SELECT is(
+  (SELECT COUNT(*) FROM invitations WHERE email = 'new@example.com' AND organization_id = '11111111-1111-1111-1111-111111111111'),
+  1::bigint,
+  'Should create a new invitation record'
+);
+
+-- Test 3: Attempt to invite a user who is already a member
+SELECT is(
+  (SELECT invite_organization_member('existing@example.com', '11111111-1111-1111-1111-111111111111')),
+  '{"success": false, "error": "this user is already a member of the organization"}'::jsonb,
+  'Should fail when inviting an existing member'
+);
+
+-- Test 4: Re-invite a user who already has a pending invitation
+SELECT is(
+  (SELECT invite_organization_member('reinvite@example.com', '11111111-1111-1111-1111-111111111111')),
+  '{"success": true, "error": null}'::jsonb,
+  'Should successfully re-invite a user'
+);
+
+-- Test 5: Verify the invitation was updated, not duplicated
+SELECT is(
+  (SELECT COUNT(*) FROM invitations WHERE email = 'reinvite@example.com' AND organization_id = '11111111-1111-1111-1111-111111111111'),
+  1::bigint,
+  'Should not create duplicate invitation'
+);
+
+-- Test 6: Verify the invited_at timestamp was updated
+SELECT ok(
+  (SELECT invited_at > (NOW() - INTERVAL '10 seconds') 
+   FROM invitations 
+   WHERE email = 'reinvite@example.com' AND organization_id = '11111111-1111-1111-1111-111111111111'),
+  'Should update the invited_at timestamp'
+);
+
+-- Test 7: Case insensitivity test
+SELECT is(
+  (SELECT invite_organization_member('NEW@example.com', '11111111-1111-1111-1111-111111111111')),
+  '{"success": true, "error": null}'::jsonb,
+  'Should be case insensitive when checking existing invitations'
+);
+
+-- Reset authentication context
+RESET ROLE;
+
+-- Finish the tests and print a diagnostic count
+SELECT * FROM finish();
+
+ROLLBACK;
diff --git a/frontend/packages/db/supabase/tests/run-tests.sh b/frontend/packages/db/supabase/tests/run-tests.sh
new file mode 100755
index 0000000000..8823b15264
--- /dev/null
+++ b/frontend/packages/db/supabase/tests/run-tests.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# Script to set up and run PostgreSQL RPC function tests
+
+# Check if Supabase CLI is installed
+if ! command -v pnpm &> /dev/null; then
+    echo "pnpm is not installed. Please install it first."
+    exit 1
+fi
+
+PROJECT_ROOT=$(pwd)
+echo "Project root: $PROJECT_ROOT"
+
+# Check if Supabase is running
+pnpm --filter @liam-hq/db exec supabase status > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+    echo "Starting Supabase..."
+    pnpm --filter @liam-hq/db exec supabase start
+else
+    echo "Supabase is already running."
+fi
+
+# Set up the testing environment
+echo "Setting up the testing environment..."
+PGPASSWORD=postgres psql -U postgres -d postgres -h localhost -p 54322 -f "$PROJECT_ROOT/frontend/packages/db/supabase/setup-testing.sql"
+
+# Change to the directory with the tests
+cd "$PROJECT_ROOT/frontend/packages/db/supabase"
+
+# Run the tests
+echo "Running tests..."
+pnpm --filter @liam-hq/db exec supabase test db
+
+echo "Tests completed."


Reviews:
user: login: MH4GF
body: LGTM, but I wrote a comment!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T08:06:34Z
user: login: hoshinotsuyoshi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T08:10:26Z
user: login: MH4GF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T08:11:48Z

Review comments:
path: .github/workflows/setup-testing.sql
user: login: MH4GF
body: I am uncomfortable with the placement of this file in `.github/workflows`, do you think it is `frontend/packages/db/supabase/tests`?
created_at: 2025-04-25T08:05:24Z
author_association: CONTRIBUTOR
path: .github/workflows/setup-testing.sql
user: login: hoshinotsuyoshi
body: > frontend/packages/db/supabase/tests

 the original is https://github.com/usebasejump/basejump/tree/main/.github/workflows , but , I think  `frontend/packages/db/supabase/tests` is more reasonable!  👍 

I'll fix it
created_at: 2025-04-25T08:10:26Z
author_association: MEMBER
path: .github/workflows/setup-testing.sql
user: login: MH4GF
body: I see, Thanks!!
created_at: 2025-04-25T08:11:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-04-25T04:00:31Z
body: ###  ⚠️  No Changeset found

Latest commit: 41641663abac48aaf0f7f225a1e4e07ed1e1c4f0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/liam-hq/liam/new/test-db-functions-2?filename=.changeset/happy-pandas-joke.md&value=---%0A%22%40liam-hq%2Fdb%22%3A%20patch%0A---%0A%0A%20test%3A%20add%20database%20CI%20workflow%20and%20PGTap%20tests%20for%20invite_organization_member%0A)


author_association: NONE
user: login: vercel[bot]
created_at: 2025-04-25T04:00:31Z
body: [vc]: #SHLYk6881Gtv8B0I5s49cj6/7zD8XCEQ1zbvBmMZtHM=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJsaWFtLWRvY3MiLCJyb290RGlyZWN0b3J5IjoiZnJvbnRlbmQvYXBwcy9kb2NzIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vcm91dGUtMDYtY29yZS9saWFtLWRvY3MvN29HVmlHbUZnRFpVelN6enhQOVpFS3hhd0tLTCIsIm5leHRDb21taXRTdGF0dXMiOiJJR05PUkVEIiwicHJldmlld1VybCI6ImxpYW0tZG9jcy1naXQtdGVzdC1kYi1mdW5jdGlvbnMtMi1yb3V0ZS0wNi1jb3JlLnZlcmNlbC5hcHAifSx7Im5hbWUiOiJsaWFtLWVyZC1zYW1wbGUiLCJyb290RGlyZWN0b3J5IjoiZnJvbnRlbmQvYXBwcy9lcmQtc2FtcGxlIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImxpYW0tZXJkLXNhbXBsZS1naXQtdGVzdC1kYi1mdW5jdGlvbnMtMi1yb3V0ZS0wNi1jb3JlLnZlcmNlbC5hcHAifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3JvdXRlLTA2LWNvcmUvbGlhbS1lcmQtc2FtcGxlL0ZIbXpFaDMxNXNLRVBZdkhZbXNQazJHanphaVYiLCJwcmV2aWV3VXJsIjoibGlhbS1lcmQtc2FtcGxlLWdpdC10ZXN0LWRiLWZ1bmN0aW9ucy0yLXJvdXRlLTA2LWNvcmUudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCJ9LHsibmFtZSI6ImxpYW0tYXBwIiwicm9vdERpcmVjdG9yeSI6ImZyb250ZW5kL2FwcHMvYXBwIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3JvdXRlLTA2LWNvcmUvbGlhbS1hcHAvRnRQV05NUDZUQ1NBWXdrRnprREI0NWRWU0JRZyIsInByZXZpZXdVcmwiOiJsaWFtLWFwcC1naXQtdGVzdC1kYi1mdW5jdGlvbnMtMi1yb3V0ZS0wNi1jb3JlLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoibGlhbS1hcHAtZ2l0LXRlc3QtZGItZnVuY3Rpb25zLTItcm91dGUtMDYtY29yZS52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **liam-app** | ✅ Ready ([Inspect](https://vercel.com/route-06-core/liam-app/FtPWNMP6TCSAYwkFzkDB45dVSBQg)) | [Visit Preview](https://liam-app-git-test-db-functions-2-route-06-core.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/liam-app-git-test-db-functions-2-route-06-core.vercel.app?via=pr-comment-feedback-link) | Apr 25, 2025 8:36am |
| **liam-erd-sample** | ✅ Ready ([Inspect](https://vercel.com/route-06-core/liam-erd-sample/FHmzEh315sKEPYvHYmsPk2GjzaiV)) | [Visit Preview](https://liam-erd-sample-git-test-db-functions-2-route-06-core.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/liam-erd-sample-git-test-db-functions-2-route-06-core.vercel.app?via=pr-comment-feedback-link) | Apr 25, 2025 8:36am |

<details><summary>1 Skipped Deployment</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **liam-docs** | ⬜️ Ignored ([Inspect](https://vercel.com/route-06-core/liam-docs/7oGViGmFgDZUzSzzxP9ZEKxawKKL)) | [Visit Preview](https://liam-docs-git-test-db-functions-2-route-06-core.vercel.app) |  | Apr 25, 2025 8:36am |
</details>




author_association: NONE
user: login: supabase[bot]
created_at: 2025-04-25T04:00:37Z
body: [supa]:egheriotvgsjhnpfdibk
**Updates to [Preview Branch (test-db-functions-2) ↗︎](https://supabase.com/dashboard/project/egheriotvgsjhnpfdibk)**

| Deployments | Status                      | Updated                   |
| ----------- | --------------------------- | ------------------------- |
| Database    | ✅ | Fri, 25 Apr 2025 08:34:45 UTC |
| Services    | ✅  | Fri, 25 Apr 2025 08:34:45 UTC  |
| APIs        | ✅      | Fri, 25 Apr 2025 08:34:45 UTC      |

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

| Tasks          | Status                       | Updated                    |
| -------------- | ---------------------------- | -------------------------- |
| Configurations | ✅ | Fri, 25 Apr 2025 08:34:45 UTC |
| Migrations     | ✅ | Fri, 25 Apr 2025 08:34:45 UTC |
| Seeding        | ✅   | Fri, 25 Apr 2025 08:34:45 UTC   |
| Edge Functions | ✅  | Fri, 25 Apr 2025 08:34:45 UTC  |

<hr/>




View logs for this [Workflow Run ↗︎](https://supabase.com/dashboard/project/egheriotvgsjhnpfdibk/branches).
Learn more about [Supabase for Git ↗︎](https://supabase.com/docs/guides/platform/branching).

author_association: NONE
user: login: qodo-free-for-open-source-projects[bot]
created_at: 2025-04-25T07:14:12Z
body: ## PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

<table>
<tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 2 🔵🔵⚪⚪⚪</td></tr>
<tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr>
<tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br>

<details><summary><a href='https://github.com/liam-hq/liam/pull/1506/files#diff-336cbc14a3d13be36af37285382ef6d54cfa574e97e47447e8fc959148c4dedfR12-R12'><strong>Directory Navigation</strong></a>

The script uses `cd $(pwd)` which is redundant as it navigates to the current directory. This could be removed without affecting functionality.
</summary>

```shell
cd $(pwd)
while [ ! -d ".github" ] && [ "$(pwd)" != "/" ]; do
```

</details>

<details><summary><a href='https://github.com/liam-hq/liam/pull/1506/files#diff-921cdcc841a3dabeff6991a5e64f67a6fd93f2e56096ed25fdf2db19773b40e0R107-R111'><strong>Test Inconsistency</strong></a>

Test 7 checks case insensitivity but uses a different email case ('NEW@example.com') than what was previously used for the same user ('new@example.com'). This might not accurately test the intended behavior if the function normalizes emails.
</summary>

```sql
SELECT is(
  (SELECT invite_organization_member('NEW@example.com', '11111111-1111-1111-1111-111111111111')),
  '{"success": true, "error": null}'::jsonb,
  'Should be case insensitive when checking existing invitations'
);
```

</details>

</td></tr>
</table>

author_association: CONTRIBUTOR
user: login: qodo-free-for-open-source-projects[bot]
created_at: 2025-04-25T07:14:31Z
body: ## PR Code Suggestions ✨

<!-- 94b3a41 -->

Explore these optional code suggestions:

<table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=2>Possible issue</td>
<td>



<details><summary>Fix incorrect command check<!-- not_implemented --></summary>

___


**The script is checking for <code>pnpm</code> when it should be checking for the Supabase CLI <br>as indicated by the comment. This will cause the script to fail with a <br>misleading error message.**

[frontend/packages/db/supabase/tests/run-tests.sh [5-9]](https://github.com/liam-hq/liam/pull/1506/files#diff-336cbc14a3d13be36af37285382ef6d54cfa574e97e47447e8fc959148c4dedfR5-R9)

```diff
 # Check if Supabase CLI is installed
-if ! command -v pnpm &> /dev/null; then
-    echo "pnpm is not installed. Please install it first."
+if ! command -v supabase &> /dev/null; then
+    echo "Supabase CLI is not installed. Please install it first."
     exit 1
 fi
```

- [ ] **Apply this suggestion** <!-- /improve --apply_suggestion=0 -->

<details><summary>Suggestion importance[1-10]: 9</summary>

__

Why: The script comment indicates it should check for Supabase CLI, but it's actually checking for pnpm. This mismatch would cause the script to fail with a misleading error message, potentially causing confusion during test setup.


</details></details></td><td align=center>High

</td></tr><tr><td>



<details><summary>Fix case insensitivity test</summary>

___


**This test is likely to fail because you've already invited 'new@example.com' in <br>Test 1, and now you're testing case insensitivity with 'NEW@example.com'. If the <br>function is truly case-insensitive, it should return an error about an existing <br>invitation, not success.**

[frontend/packages/db/supabase/tests/database/01-invite_organization_member.test.sql [106-111]](https://github.com/liam-hq/liam/pull/1506/files#diff-921cdcc841a3dabeff6991a5e64f67a6fd93f2e56096ed25fdf2db19773b40e0R106-R111)

```diff
 -- Test 7: Case insensitivity test
 SELECT is(
   (SELECT invite_organization_member('NEW@example.com', '11111111-1111-1111-1111-111111111111')),
-  '{"success": true, "error": null}'::jsonb,
+  '{"success": false, "error": "an invitation for this email already exists"}'::jsonb,
   'Should be case insensitive when checking existing invitations'
 );
```

- [ ] **Apply this suggestion** <!-- /improve --apply_suggestion=1 -->

<details><summary>Suggestion importance[1-10]: 8</summary>

__

Why: The test has a logical error - if the function is truly case-insensitive, inviting 'NEW@example.com' after already inviting 'new@example.com' should return an error about an existing invitation, not success. This correction ensures the test properly validates case insensitivity.


</details></details></td><td align=center>Medium

</td></tr>
<tr><td align="center" colspan="2">

- [ ] Update <!-- /improve --more_suggestions=true -->

</td><td></td></tr></tbody></table>
author_association: CONTRIBUTOR