Skip to content
Merged
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
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
# Editor files
.*.swp

# Claude Code local settings
.claude/*.local.json

# Explicitly exclude META.json!
!/META.json

# Generated make files
meta.mk
control.mk

# Compiler output
*.o
*.so
.deps/

# built targets
/sql/*--*
!/sql/*--*--*.sql
# Note: Version-specific files (sql/*--*.sql) are now tracked in git and should be committed

# Test artifacts
results/
Expand All @@ -24,3 +27,6 @@ regression.out
# Misc
tmp/
.DS_Store

# pg_tle generated files
/pg_tle/
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');
```
78 changes: 78 additions & 0 deletions pgxntool/.claude/commands/commit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
description: Create a git commit following project standards and safety protocols
allowed-tools: Bash(git status:*), Bash(git log:*), Bash(git add:*), Bash(git diff:*), Bash(git commit:*), Bash(make test:*)
---

# commit

Create a git commit following all project standards and safety protocols for pgxntool-test.

**CRITICAL REQUIREMENTS:**

1. **Git Safety**: Never update `git config`, never force push to `main`/`master`, never skip hooks unless explicitly requested

2. **Commit Attribution**: Do NOT add "Generated with Claude Code" to commit message body. The standard Co-Authored-By trailer is acceptable per project CLAUDE.md.

3. **Testing**: ALL tests must pass before committing:
- Run `make test`
- Check the output carefully for any "not ok" lines
- Count passing vs total tests
- **If ANY tests fail: STOP. Do NOT commit. Ask the user what to do.**
- There is NO such thing as an "acceptable" failing test
- Do NOT rationalize failures as "pre-existing" or "unrelated"

**WORKFLOW:**

1. Run in parallel: `git status`, `git diff --stat`, `git log -10 --oneline`

2. Check test status - THIS IS MANDATORY:
- Run `make test 2>&1 | tee /tmp/test-output.txt`
- Check for failing tests: `grep "^not ok" /tmp/test-output.txt`
- If ANY tests fail: STOP immediately and inform the user
- Only proceed if ALL tests pass

3. Analyze changes and draft concise commit message following this repo's style:
- Look at `git log -10 --oneline` to match existing style
- Be factual and direct (e.g., "Fix BATS dist test to create its own distribution")
- Focus on "why" when it adds value, otherwise just describe "what"
- List items in roughly decreasing order of impact
- Keep related items grouped together
- **In commit messages**: Wrap all code references in backticks - filenames, paths, commands, function names, variables, make targets, etc.
- Examples: `helpers.bash`, `make test-recursion`, `setup_sequential_test()`, `TEST_REPO`, `.envs/`, `01-meta.bats`
- Prevents markdown parsing issues and improves clarity

4. **PRESENT the proposed commit message to the user and WAIT for approval before proceeding**

5. After receiving approval, stage changes appropriately using `git add`

6. **VERIFY staged files with `git status`**:
- If user did NOT specify a subset: Confirm ALL modified/untracked files are staged
- If user specified only certain files: Confirm ONLY those files are staged
- STOP and ask user if staging doesn't match intent

7. After verification, commit using `HEREDOC` format:
```bash
git commit -m "$(cat <<'EOF'
Subject line (imperative mood, < 72 chars)

Additional context if needed, wrapped at 72 characters.

Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
```

8. Run `git status` after commit to verify success

9. If pre-commit hook modifies files: Check authorship (`git log -1 --format='%an %ae'`) and branch status, then amend if safe or create new commit

**REPOSITORY CONTEXT:**

This is pgxntool-test, a test harness for the pgxntool framework. Key facts:
- Tests live in `tests/` directory
- `.envs/` contains test environments (gitignored)

**RESTRICTIONS:**
- DO NOT push unless explicitly asked
- DO NOT commit files with actual secrets (`.env`, `credentials.json`, etc.)
- Never use `-i` flags (`git commit -i`, `git rebase -i`, etc.)
19 changes: 19 additions & 0 deletions pgxntool/.claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"permissions": {
"allow": [
"Bash(cat:*)",
"Bash(make test:*)",
"Bash(tee:*)",
"Bash(echo:*)",
"Bash(git show:*)",
"Bash(git log:*)",
"Bash(ls:*)",
"Bash(find:*)",
"Bash(git checkout:*)",
"Bash(head:*)"
],
"additionalDirectories": [
"../pgxntool-test/"
]
}
}
3 changes: 3 additions & 0 deletions pgxntool/.gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.gitattributes export-ignore
.claude/ export-ignore
*.md export-ignore
.DS_Store export-ignore
*.asc export-ignore
*.adoc export-ignore
*.html export-ignore
1 change: 1 addition & 0 deletions pgxntool/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.*.swp
.claude/*.local.json
Loading