Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is **test_factory**, a PostgreSQL extension that provides a framework for managing unit test data in databases. It solves the common problem of creating and maintaining test data by providing a system to register test data definitions once and retrieve them efficiently with automatic dependency resolution.

## Build System & Development Commands

This project uses PGXNtool for build management. Key commands:

### Building and Installation
```bash
make # Build the extension
make install # Install to PostgreSQL
make clean # Clean build artifacts
make distclean # Clean all generated files including META.json
```

### Testing
```bash
make test # Run full test suite (install, then test)
make installcheck # Run tests only (no clean/install)
make results # Update expected test results (only after verifying tests pass!)
```

### Distribution
```bash
make tag # Create git tag for current version
make dist # Create distribution zip file
make forcetag # Force recreate tag if it exists
make forcedist # Force tag + distribution
```

## Architecture & Key Components

### Core API Functions
- `tf.register(table_name, test_sets[])` - Register test data definitions for a table
- `tf.get(table_type, set_name)` - Retrieve test data, creating it if it doesn't exist
- `tf.tap(table_name, set_name)` - pgTAP integration wrapper for testing

### Database Schema Organization
- `tf` schema: User-facing API (functions, types)
- `_tf` schema: Internal implementation (tables, security definer functions)
- `_test_factory_test_data` schema: Cached test data storage
- Uses dedicated `test_factory__owner` role for security isolation

### Security Model
- Role-based access with `test_factory__owner` for data management
- Security definer functions with `search_path=pg_catalog`
- Proper permission isolation between user and system operations

### Key Data Structures
```sql
CREATE TYPE tf.test_set AS (
set_name text, -- Name to reference this test data set
insert_sql text -- SQL command that returns test data rows
);
```

### Test Data Workflow
1. **Registration**: Use `tf.register()` to define how test data is created
2. **Retrieval**: Call `tf.get()` to obtain test data (creates on first call)
3. **Caching**: Test data is stored permanently for fast subsequent access
4. **Dependencies**: Test sets can reference other test sets via embedded `tf.get()` calls

### Performance & Caching
- Test data created once and cached in permanent tables
- Subsequent `tf.get()` calls return cached data without recreation
- Data remains available even if source tables are modified/truncated
- Dependency resolution handled automatically during creation

## File Structure Key Points

### SQL Files
- `sql/test_factory.sql` and `sql/test_factory--0.5.0.sql`: Main extension code
- Complex role management and schema setup with proper cleanup
- Security definer functions for safe cross-schema operations

### Build Configuration
- `META.in.json`: Template for PGXN metadata (processed to `META.json`)
- `test_factory.control`: PostgreSQL extension control file
- `Makefile`: Simple inclusion of pgxntool's build system

## Development Workflow

1. **Making Changes**: Modify source files in `sql/` directory
2. **Testing**: Run `make test` to ensure all tests pass
3. **Version Updates**: Update version in both `META.in.json` and `test_factory.control`
4. **Distribution**: Use `make dist` to create release packages

## Extension Architecture Details

The extension handles complex bootstrapping during installation:
- Creates temporary role tracking for safe installation
- Sets up three schemas with proper ownership and permissions
- Uses security definer pattern for controlled access to internal functions
- Automatically restores original database role after installation
- Implements dependency resolution through recursive `tf.get()` calls

## Usage Patterns

### Basic Registration
```sql
SELECT tf.register(
'customer',
array[
row('base', 'INSERT INTO customer VALUES (DEFAULT, ''Test'', ''User'') RETURNING *')::tf.test_set
]
);
```

### With Dependencies
```sql
SELECT tf.register(
'invoice',
array[
row('base', 'INSERT INTO invoice VALUES (DEFAULT, (tf.get(NULL::customer, ''base'')).customer_id, current_date) RETURNING *')::tf.test_set
]
);
```

### Data Retrieval
```sql
-- Gets customer test data, creating it if needed
SELECT * FROM tf.get(NULL::customer, 'base');

-- Gets invoice test data, automatically creating dependent customer data
SELECT * FROM tf.get(NULL::invoice, 'base');
```
132 changes: 132 additions & 0 deletions test/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Test Framework Documentation

This file provides guidance for understanding and working with the test_factory extension test suite.

## Test Framework Overview

The test_factory extension uses **pgTAP** (PostgreSQL's unit testing framework) for comprehensive testing. Tests are organized using PGXNtool's standardized testing infrastructure.

## Test Structure

### Test Files
- `test/sql/base.sql` - Core functionality tests (22 tests)
- `test/sql/install.sql` - Extension installation/uninstallation tests
- `test/sql/pgtap.sql` - pgTAP integration and `tf.tap()` function tests

### Expected Results
- `test/expected/*.out` - Expected test output for regression testing
- `test/results/*.out` - Actual test output (generated during test runs)

### Test Helpers
- `test/helpers/setup.sql` - Test environment initialization and pgTAP setup
- `test/helpers/create.sql` - Test data registration and security validation
- `test/helpers/create_extension.sql` - Extension creation wrapper
- `test/helpers/deps.sql` - Test dependency management
- Other helper files for role management and pgTAP integration

## Test Coverage Analysis

### Core Functionality Tests (`base.sql`)
1. **Extension Setup** - Creates extension and test tables
2. **Data Registration** - Tests `tf.register()` with multiple test sets
3. **Basic Retrieval** - Tests `tf.get()` returns correct data
4. **Dependency Resolution** - Tests automatic creation of dependent data (customer → invoice)
5. **Caching Behavior** - Verifies data consistency across multiple `tf.get()` calls
6. **Table Independence** - Tests that cached data persists after source table changes
7. **Function-based Test Data** - Tests using functions as test data sources

### Security Tests (`create.sql`)
- **Role Management** - Validates proper role restoration after installation
- **Security Definer Functions** - Ensures all privileged functions use `search_path=pg_catalog`
- **Permission Isolation** - Tests with unprivileged `test_role`
- **Temp Table Cleanup** - Verifies temporary installation objects are removed

### Installation Tests (`install.sql`)
- **Dependency Validation** - Tests extension dependency requirements
- **Clean Installation** - Tests CREATE EXTENSION without conflicts
- **Clean Removal** - Tests DROP EXTENSION without orphaned objects

### pgTAP Integration Tests (`pgtap.sql`)
- **tf.tap() Function** - Tests pgTAP wrapper functionality
- **Error Handling** - Tests proper error reporting for invalid inputs
- **Extension Dependencies** - Validates test_factory_pgtap requires test_factory

## Test Data Model

### Test Tables
```sql
CREATE TABLE customer(
customer_id serial PRIMARY KEY,
first_name text NOT NULL,
last_name text NOT NULL
);

CREATE TABLE invoice(
invoice_id serial PRIMARY KEY,
customer_id int NOT NULL REFERENCES customer,
invoice_date date NOT NULL,
due_date date
);
```

### Test Data Sets
- **customer 'insert'** - Simple INSERT statement returning customer data
- **customer 'function'** - Function-based test data creation
- **invoice 'base'** - Invoice with dependency on customer 'insert' set

## Running Tests

### Basic Test Execution
```bash
make test # Full test suite with clean install
make installcheck # Run tests against already installed extension
```

### Test Development Workflow
```bash
# Make changes to test files
vim test/sql/base.sql

# Run tests to verify
make test

# If tests pass but output differs, update expected results
make results
```

### Test Debugging
- Test output appears in `test/results/`
- Differences shown in `test/regression.diffs` if tests fail
- Use `\set ECHO all` in test SQL files for detailed debugging

## Test Architecture Details

### pgTAP Integration
- Tests use pgTAP assertion functions: `is()`, `results_eq()`, `bag_eq()`, `lives_ok()`
- `no_plan()` allows dynamic test counting
- Tests run in transactions with automatic rollback

### Security Testing Strategy
- Creates unprivileged `test_role` to validate security boundaries
- Tests run with restricted permissions to catch privilege escalation issues
- Validates all security definer functions use safe search_path settings

### Dependency Testing
- Tests multi-level dependencies (invoice → customer)
- Validates data creation order and consistency
- Tests that dependency data is created automatically and cached

### Error Condition Testing
- Tests invalid table names and missing test sets
- Validates proper error messages and SQL state codes
- Tests edge cases like non-existent tables in tf.tap()

## Test Data Lifecycle

1. **Setup Phase** - Creates test role, schemas, and tables
2. **Registration Phase** - Registers test data definitions
3. **Execution Phase** - Calls tf.get() to trigger data creation
4. **Validation Phase** - Verifies data correctness and caching behavior
5. **Cleanup Phase** - Transaction rollback removes all test data

This comprehensive test suite ensures the test_factory extension works correctly across different PostgreSQL versions and usage patterns, with particular attention to security and data integrity.