Supabase Plugin - Full-Stack Database Testing
The Supabase plugin enables comprehensive testing of Supabase applications, providing coverage of database operations, authentication, storage, and PostgreSQL RPC functions. Test your entire Supabase stack from database CRUD operations to file storage and user authentication workflows.
Key Features
- CRUD Operations - Create, read, update, and delete data with advanced filtering
- PostgreSQL RPC Functions - Call stored procedures and custom database functions
- Authentication Testing - User signup, signin, and session management
- Storage Operations - File upload, download, and bucket management
- Advanced Filtering - 15+ filter operators including like, in, range queries
- JSON Path Extraction - Save and chain data between test steps
- Row Level Security - Test RLS policies and permissions
- Real-time Features - Test subscriptions and live data updates
Prerequisites
Before using the Supabase plugin, you need:
- Supabase Project - Create a project at supabase.com
- Project Credentials - Your project URL and API keys
- Database Schema - Tables and functions set up for testing
# Get your project details from Supabase Dashboard
PROJECT_URL="https://your-project.supabase.co"
ANON_KEY="your-anon-key"
SERVICE_KEY="your-service-role-key" # For admin operations
Basic Configuration
plugin: supabase
config:
url: "https://your-project.supabase.co"
key: "your-anon-key"
operation: "select" # Required: CRUD or special operation
table: "users" # Required for CRUD operations
CRUD Operations
SELECT - Reading Data
Query data with filtering, ordering, and pagination:
- name: "Query active users"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "select"
table: "users"
select:
columns: ["id", "name", "email", "created_at"]
filters:
- column: "status"
operator: "eq"
value: "active"
- column: "created_at"
operator: "gte"
value: "2024-01-01"
order:
- column: "created_at"
ascending: false
limit: 10
offset: 0
count: "exact" # Get total count
INSERT - Creating Data
Create new records with optional upsert handling:
- name: "Create new user"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "insert"
table: "users"
insert:
data:
name: "John Doe"
email: "john@example.com"
metadata:
role: "user"
preferences: { "theme": "dark" }
upsert: false # Set to true for upsert behavior
save:
- json_path: ".[0].id"
as: "new_user_id"
UPDATE - Modifying Data
Update existing records with conditional filters:
- name: "Update user status"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "update"
table: "users"
update:
data:
status: "verified"
updated_at: "2024-01-15T10:30:00Z"
filters:
- column: "id"
operator: "eq"
value: "{{ new_user_id }}"
assertions:
- type: json_path
path: ".[0].status"
expected: "verified"
DELETE - Removing Data
Delete records with required safety filters:
- name: "Delete test user"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "delete"
table: "users"
delete:
filters: # Filters are required for safety
- column: "id"
operator: "eq"
value: "{{ new_user_id }}"
Filter Operators
The Supabase plugin supports comprehensive filtering options:
Operator | Description | Example |
---|---|---|
eq |
Equal | {"operator": "eq", "value": "active"} |
neq |
Not equal | {"operator": "neq", "value": "deleted"} |
gt |
Greater than | {"operator": "gt", "value": 100} |
gte |
Greater than or equal | {"operator": "gte", "value": 18} |
lt |
Less than | {"operator": "lt", "value": 1000} |
lte |
Less than or equal | {"operator": "lte", "value": 65} |
like |
Pattern matching | {"operator": "like", "value": "%test%"} |
ilike |
Case-insensitive like | {"operator": "ilike", "value": "%TEST%"} |
is |
Null check | {"operator": "is", "value": null} |
in |
Value in list | {"operator": "in", "value": ["a", "b"]} |
Complex Filtering Example
select:
columns: ["id", "name", "email", "age", "status"]
filters:
- column: "status"
operator: "in"
value: ["active", "premium"]
- column: "age"
operator: "gte"
value: 18
- column: "name"
operator: "ilike"
value: "%smith%"
order:
- column: "created_at"
ascending: false
- column: "name"
ascending: true
limit: 50
RPC Function Calls
Execute PostgreSQL functions and stored procedures:
- name: "Call simple function"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "rpc"
rpc:
function: "get_user_count"
save:
- json_path: "."
as: "total_users"
- name: "Call function with parameters"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "rpc"
rpc:
function: "create_user_profile"
params:
user_id: "{{ new_user_id }}"
profile_data:
bio: "Test user profile"
preferences: { "notifications": true }
assertions:
- type: json_path
path: ".success"
expected: true
Authentication Operations
User Signup
Create new user accounts with metadata:
- name: "User registration"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "auth_sign_up"
auth:
email: "newuser@example.com"
password: "SecurePassword123!"
user_metadata:
first_name: "John"
last_name: "Doe"
role: "customer"
save:
- json_path: ".user.id"
as: "auth_user_id"
assertions:
- type: json_path
path: ".user.email"
expected: "newuser@example.com"
User Signin
Authenticate existing users:
- name: "User authentication"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "auth_sign_in"
auth:
email: "newuser@example.com"
password: "SecurePassword123!"
save:
- json_path: ".access_token"
as: "user_token"
- json_path: ".refresh_token"
as: "refresh_token"
assertions:
- type: json_path
path: ".access_token"
expected: "exists"
Admin User Operations
Manage users with service role permissions:
- name: "Create user (admin)"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_service_key }}" # Service role required
operation: "auth_create_user"
auth:
email: "admin-created@example.com"
password: "AdminPassword123!"
user_metadata:
created_by: "admin"
role: "staff"
- name: "Delete user (admin)"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_service_key }}"
operation: "auth_delete_user"
auth:
user_id: "{{ auth_user_id }}"
Storage Operations
Create Storage Bucket
- name: "Create storage bucket"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "storage_create_bucket"
storage:
bucket: "test-uploads"
public: true
File Upload
- name: "Upload test file"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "storage_upload"
storage:
bucket: "test-uploads"
path: "documents/test-file.txt"
file_content: |
This is test file content.
Line 2 of the file.
End of content.
content_type: "text/plain"
cache_control: "3600"
File Download
- name: "Download and verify file"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "storage_download"
storage:
bucket: "test-uploads"
path: "documents/test-file.txt"
assertions:
- type: json_path
path: "."
expected: |
This is test file content.
Line 2 of the file.
End of content.
File Deletion
- name: "Clean up test file"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "storage_delete"
storage:
bucket: "test-uploads"
path: "documents/test-file.txt"
Data Extraction and Chaining
Save Operation Syntax
Extract data from responses for use in subsequent steps:
save:
# Extract from JSON response data
- json_path: ".[0].id"
as: "record_id"
- json_path: ".user.email"
as: "user_email"
# Extract from response headers
- header: "Content-Type"
as: "response_type"
# Optional extractions (won't fail if missing)
- json_path: ".optional_field"
as: "optional_data"
required: false
JSON Path Examples
Path | Description | Example Result |
---|---|---|
".[0].id" |
First record's ID | 123 |
".count" |
Total count from response | 45 |
".user.metadata.role" |
Nested object property | "admin" |
"length" |
Array length | 10 |
Configuration with Variables
Use variables for reusable and secure configurations:
vars:
supabase_url: "https://your-project.supabase.co"
supabase_anon_key: "{{ .env.SUPABASE_ANON_KEY }}"
supabase_service_key: "{{ .env.SUPABASE_SERVICE_KEY }}"
test_email: "test@example.com"
tests:
- name: "User lifecycle test"
steps:
- name: "Create user"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "insert"
table: "users"
insert:
data:
email: "{{ .vars.test_email }}"
status: "pending"
Assertions
Validate Supabase operations with built-in assertions:
assertions:
# Standard JSON path assertions
- type: json_path
path: ".[0].status"
expected: "active"
# Check array lengths
- type: json_path
path: "length"
expected: 5
# Verify nested data
- type: json_path
path: ".user.metadata.role"
expected: "admin"
# Check for existence (any truthy value)
- type: json_path
path: ".access_token"
expected: "exists"
Complete Workflow Example
name: "Supabase E2E User Journey"
version: "v1.0.0"
vars:
supabase_url: "https://your-project.supabase.co"
supabase_anon_key: "your-anon-key"
test_email: "integration-test@example.com"
test_password: "TestPassword123!"
tests:
- name: "Complete user workflow"
steps:
# 1. User Registration
- name: "User signup"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "auth_sign_up"
auth:
email: "{{ .vars.test_email }}"
password: "{{ .vars.test_password }}"
user_metadata:
name: "Test User"
source: "integration_test"
save:
- json_path: ".user.id"
as: "user_id"
# 2. Create User Profile
- name: "Create profile"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "insert"
table: "profiles"
insert:
data:
user_id: "{{ user_id }}"
display_name: "Test User"
bio: "Integration test profile"
preferences:
theme: "dark"
notifications: true
save:
- json_path: ".[0].id"
as: "profile_id"
# 3. User Authentication
- name: "User signin"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "auth_sign_in"
auth:
email: "{{ .vars.test_email }}"
password: "{{ .vars.test_password }}"
save:
- json_path: ".access_token"
as: "auth_token"
# 4. File Upload
- name: "Upload profile picture"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "storage_upload"
storage:
bucket: "profiles"
path: "avatars/{{ user_id }}.jpg"
file_content: "fake-image-data-for-testing"
content_type: "image/jpeg"
# 5. Update Profile with Image
- name: "Update profile with avatar"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "update"
table: "profiles"
update:
data:
avatar_url: "avatars/{{ user_id }}.jpg"
updated_at: "{{ .now }}"
filters:
- column: "user_id"
operator: "eq"
value: "{{ user_id }}"
# 6. Query Final State
- name: "Verify complete profile"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "select"
table: "profiles"
select:
columns: ["*"]
filters:
- column: "user_id"
operator: "eq"
value: "{{ user_id }}"
assertions:
- type: json_path
path: ".[0].display_name"
expected: "Test User"
- type: json_path
path: ".[0].avatar_url"
expected: "avatars/{{ user_id }}.jpg"
# 7. Cleanup
- name: "Delete test data"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "delete"
table: "profiles"
delete:
filters:
- column: "user_id"
operator: "eq"
value: "{{ user_id }}"
Testing with Row Level Security
Test RLS policies by using different authentication contexts:
- name: "Test RLS with user context"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ user_auth_token }}" # User's JWT token
operation: "select"
table: "private_data"
select:
columns: ["id", "user_id", "data"]
assertions:
# Should only return data owned by this user
- type: json_path
path: "length"
expected: 1
- name: "Test RLS with anon context"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}" # Anonymous access
operation: "select"
table: "private_data"
select:
columns: ["id", "data"]
assertions:
# Should return no data (RLS blocks anonymous access)
- type: json_path
path: "length"
expected: 0
Running Tests
# Run basic Supabase tests
rocketship run -af examples/supabase-testing/rocketship.yaml
# Run with environment variables
SUPABASE_URL=your-url SUPABASE_ANON_KEY=your-key rocketship run -af your-test.yaml
# Run against different environments
rocketship run -af tests/supabase/staging.yaml
rocketship run -af tests/supabase/production.yaml
Best Practices
1. Use Environment Variables for Credentials
vars:
supabase_url: "{{ .env.SUPABASE_URL }}"
supabase_anon_key: "{{ .env.SUPABASE_ANON_KEY }}"
supabase_service_key: "{{ .env.SUPABASE_SERVICE_KEY }}"
2. Clean Up Test Data
Always clean up test data to maintain test isolation:
# At the end of your test
- name: "Cleanup test data"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "delete"
table: "test_data"
delete:
filters:
- column: "created_by"
operator: "eq"
value: "integration_test"
3. Test Both Success and Error Cases
# Test successful operations
- name: "Valid user creation"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "insert"
table: "users"
insert:
data:
email: "valid@example.com"
name: "Valid User"
# Test error conditions
- name: "Duplicate email should fail"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "insert"
table: "users"
insert:
data:
email: "valid@example.com" # Same email
name: "Duplicate User"
# This step should fail due to unique constraint
4. Use Meaningful Test Data
insert:
data:
email: "integration-test-{{ .timestamp }}@example.com"
name: "Test User {{ .timestamp }}"
metadata:
test_run_id: "{{ .run_id }}"
created_by: "rocketship_integration"
5. Test Database Functions
- name: "Test business logic function"
plugin: supabase
config:
url: "{{ .vars.supabase_url }}"
key: "{{ .vars.supabase_anon_key }}"
operation: "rpc"
rpc:
function: "calculate_user_score"
params:
user_id: "{{ user_id }}"
include_bonus: true
assertions:
- type: json_path
path: ".score"
expected: 850
- type: json_path
path: ".bonus_applied"
expected: true
Troubleshooting
Connection Issues
"Invalid project URL"
- Verify your Supabase project URL format:
https://project-id.supabase.co
- Check that your project is active and not paused
"Invalid API key"
- Verify you're using the correct key for your operation:
anon
key for most operationsservice_role
key for admin operations- Check key hasn't expired or been regenerated
Permission Errors
"Insufficient privileges"
- Check Row Level Security policies on your tables
- Verify user authentication context
- Use service role key for admin operations
"Table not found"
- Verify table exists in your database
- Check table name spelling and case sensitivity
- Ensure you have proper permissions
Authentication Issues
"Email already registered"
- Use unique emails for each test run
- Clean up test users after testing
- Use email templates with timestamps
"Invalid credentials"
- Verify email/password combination
- Check if email confirmation is required
- Ensure user account isn't disabled
Storage Issues
"Bucket not found"
- Create storage bucket first using
storage_create_bucket
- Verify bucket name spelling
- Check bucket permissions
"File upload failed"
- Verify bucket exists and is accessible
- Check file path format
- Ensure proper permissions for upload
General Debugging
Enable debug logging for detailed operation information:
Check Supabase Dashboard logs for server-side errors and detailed operation results.
The Supabase plugin provides comprehensive testing capabilities for modern full-stack applications, enabling you to validate your entire Supabase stack from database operations to user authentication and file storage.