pg_multitenant_schemas 0.1.3 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.actrc +17 -0
  3. data/.env.local.example +21 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +86 -0
  6. data/LOCAL_TESTING_SUMMARY.md +141 -0
  7. data/README.md +269 -16
  8. data/TESTING_LOCALLY.md +208 -0
  9. data/docs/README.md +81 -0
  10. data/docs/configuration.md +340 -0
  11. data/docs/context.md +292 -0
  12. data/docs/errors.md +498 -0
  13. data/docs/github_actions_permissions_fix.md +136 -0
  14. data/docs/github_actions_setup.md +181 -0
  15. data/docs/integration_testing.md +454 -0
  16. data/docs/local_workflow_testing.md +314 -0
  17. data/docs/migrator.md +291 -0
  18. data/docs/rails_integration.md +468 -0
  19. data/docs/schema_switcher.md +182 -0
  20. data/docs/tenant_resolver.md +394 -0
  21. data/docs/testing.md +358 -0
  22. data/examples/context_management.rb +198 -0
  23. data/examples/migration_workflow.rb +50 -0
  24. data/examples/rails_integration/controller_examples.rb +368 -0
  25. data/examples/schema_operations.rb +124 -0
  26. data/lib/pg_multitenant_schemas/configuration.rb +4 -4
  27. data/lib/pg_multitenant_schemas/migration_display_reporter.rb +30 -0
  28. data/lib/pg_multitenant_schemas/migration_executor.rb +81 -0
  29. data/lib/pg_multitenant_schemas/migration_schema_operations.rb +54 -0
  30. data/lib/pg_multitenant_schemas/migration_status_reporter.rb +65 -0
  31. data/lib/pg_multitenant_schemas/migrator.rb +89 -0
  32. data/lib/pg_multitenant_schemas/schema_switcher.rb +40 -66
  33. data/lib/pg_multitenant_schemas/tasks/advanced_tasks.rake +21 -0
  34. data/lib/pg_multitenant_schemas/tasks/basic_tasks.rake +20 -0
  35. data/lib/pg_multitenant_schemas/tasks/pg_multitenant_schemas.rake +53 -143
  36. data/lib/pg_multitenant_schemas/tasks/tenant_tasks.rake +65 -0
  37. data/lib/pg_multitenant_schemas/tenant_task_helpers.rb +102 -0
  38. data/lib/pg_multitenant_schemas/version.rb +1 -1
  39. data/lib/pg_multitenant_schemas.rb +10 -5
  40. data/pg_multitenant_schemas.gemspec +10 -9
  41. data/pre-push-check.sh +95 -0
  42. data/rails_integration/app/controllers/application_controller.rb +6 -0
  43. data/rails_integration/app/models/tenant.rb +6 -0
  44. data/test-github-setup.sh +85 -0
  45. data/validate-github-commands.sh +47 -0
  46. metadata +49 -17
@@ -0,0 +1,181 @@
1
+ # GitHub Actions Setup Guide
2
+
3
+ This document explains how to set up GitHub Actions for automated testing and releases.
4
+
5
+ ## Overview
6
+
7
+ The repository uses two main workflows:
8
+
9
+ 1. **CI Workflow** (`.github/workflows/main.yml`) - Runs tests on every push and PR
10
+ 2. **Release Workflow** (`.github/workflows/release.yml`) - Automatically releases to RubyGems when version changes
11
+
12
+ ## Setting Up RubyGems API Key
13
+
14
+ To enable automatic releases to RubyGems, you need to set up a GitHub secret with your RubyGems API key.
15
+
16
+ ### Step 1: Get Your RubyGems API Key
17
+
18
+ 1. Go to [RubyGems.org](https://rubygems.org/)
19
+ 2. Sign in to your account
20
+ 3. Go to your profile settings
21
+ 4. Navigate to "API Keys" section
22
+ 5. Create a new API key or use an existing one
23
+ 6. Copy the API key (it should start with `rubygems_`)
24
+
25
+ ### Step 2: Add GitHub Secret
26
+
27
+ 1. Go to your GitHub repository
28
+ 2. Click on "Settings" tab
29
+ 3. In the left sidebar, click "Secrets and variables" → "Actions"
30
+ 4. Click "New repository secret"
31
+ 5. Name: `RUBYGEMS_API_KEY`
32
+ 6. Value: Paste your RubyGems API key
33
+ 7. Click "Add secret"
34
+
35
+ ### 3. Configure Repository Permissions
36
+
37
+ **Important:** Configure GitHub Actions permissions to allow the release workflow to create tags and releases:
38
+
39
+ 1. Go to your repository Settings
40
+ 2. Navigate to **Actions > General**
41
+ 3. Under "Workflow permissions":
42
+ - Select **"Read and write permissions"**
43
+ - Check **"Allow GitHub Actions to create and approve pull requests"**
44
+ 4. Click **Save**
45
+
46
+ Without these permissions, you'll get errors like:
47
+ ```
48
+ remote: Permission to username/repo.git denied to github-actions[bot].
49
+ fatal: unable to access 'https://github.com/username/repo/': The requested URL returned error: 403
50
+ ```
51
+
52
+ ## How the Workflows Work
53
+
54
+ ### CI Workflow
55
+
56
+ **Triggers:**
57
+ - Every push to `main` branch
58
+ - Every pull request to `main` branch
59
+
60
+ **What it does:**
61
+ - Tests on Ruby 3.2, 3.3, and 3.4
62
+ - Runs RuboCop for code quality
63
+ - Runs unit tests (excluding integration tests)
64
+ - Runs integration tests (with PostgreSQL database)
65
+ - Runs security audit with bundle-audit
66
+
67
+ ### Release Workflow
68
+
69
+ **Triggers:**
70
+ - Push to `main` branch that changes `lib/pg_multitenant_schemas/version.rb`
71
+
72
+ **What it does:**
73
+ - Checks if the version in `version.rb` has changed
74
+ - If version changed:
75
+ 1. Builds the gem
76
+ 2. Creates a Git tag (e.g., `v0.2.1`)
77
+ 3. Creates a GitHub release with changelog notes
78
+ 4. Publishes the gem to RubyGems
79
+
80
+ ## Release Process
81
+
82
+ To release a new version:
83
+
84
+ 1. **Update the version** in `lib/pg_multitenant_schemas/version.rb`:
85
+ ```ruby
86
+ module PgMultitenantSchemas
87
+ VERSION = "0.2.1" # Increment this
88
+ end
89
+ ```
90
+
91
+ 2. **Update the changelog** in `CHANGELOG.md`:
92
+ ```markdown
93
+ ## [0.2.1] - 2025-09-07
94
+
95
+ ### Added
96
+ - New feature description
97
+
98
+ ### Fixed
99
+ - Bug fix description
100
+ ```
101
+
102
+ 3. **Commit and push** to main branch:
103
+ ```bash
104
+ git add lib/pg_multitenant_schemas/version.rb CHANGELOG.md
105
+ git commit -m "Bump version to 0.2.1"
106
+ git push origin main
107
+ ```
108
+
109
+ 4. **Automatic release** will trigger:
110
+ - GitHub Actions will detect the version change
111
+ - Create a Git tag and GitHub release
112
+ - Publish to RubyGems automatically
113
+
114
+ ## Manual Release (Alternative)
115
+
116
+ If you prefer manual releases or need to troubleshoot:
117
+
118
+ ```bash
119
+ # Build the gem
120
+ gem build pg_multitenant_schemas.gemspec
121
+
122
+ # Push to RubyGems (requires authentication)
123
+ gem push pg_multitenant_schemas-0.2.1.gem
124
+
125
+ # Create Git tag
126
+ git tag v0.2.1
127
+ git push origin v0.2.1
128
+ ```
129
+
130
+ ## Workflow Status
131
+
132
+ You can monitor workflow runs:
133
+
134
+ 1. Go to your GitHub repository
135
+ 2. Click the "Actions" tab
136
+ 3. View running and completed workflows
137
+ 4. Click on individual runs to see detailed logs
138
+
139
+ ## Security Considerations
140
+
141
+ - **API Keys**: Never commit API keys to the repository
142
+ - **Secrets**: Use GitHub Secrets for sensitive information
143
+ - **Permissions**: The `GITHUB_TOKEN` has limited permissions for creating releases
144
+ - **Audit**: The security workflow checks for vulnerable dependencies
145
+
146
+ ## Troubleshooting
147
+
148
+ ### Common Issues
149
+
150
+ 1. **RubyGems authentication failed**
151
+ - Check that `RUBYGEMS_API_KEY` secret is set correctly
152
+ - Ensure the API key has publishing permissions
153
+
154
+ 2. **Git tag already exists**
155
+ - The workflow checks for existing tags
156
+ - If tag exists, release is skipped
157
+
158
+ 3. **Tests failing**
159
+ - CI must pass before release workflow runs
160
+ - Check test logs in the Actions tab
161
+
162
+ 4. **PostgreSQL connection issues**
163
+ - Integration tests require PostgreSQL service
164
+ - Check service configuration in workflow
165
+
166
+ ### Debug Steps
167
+
168
+ 1. Check workflow logs in GitHub Actions
169
+ 2. Verify secrets are set correctly
170
+ 3. Test locally with same Ruby versions
171
+ 4. Check RubyGems.org for published gems
172
+
173
+ ## Best Practices
174
+
175
+ 1. **Version Bumping**: Use semantic versioning (MAJOR.MINOR.PATCH)
176
+ 2. **Changelog**: Always update changelog before releasing
177
+ 3. **Testing**: Ensure all tests pass locally before pushing
178
+ 4. **Security**: Regularly update dependencies and run security audits
179
+ 5. **Documentation**: Update documentation for breaking changes
180
+
181
+ This automated setup ensures consistent, reliable releases while maintaining code quality through comprehensive testing.
@@ -0,0 +1,454 @@
1
+ # Integration Testing Guide - PostgreSQL Multi-Schema Operations
2
+
3
+ ## 🎯 **Integration Test Overview**
4
+
5
+ Integration tests validate real PostgreSQL multi-schema operations, ensuring the gem works correctly with actual database instances. These tests are tagged with `:integration` and require a running PostgreSQL server.
6
+
7
+ ## 🏗️ **Integration Test Architecture**
8
+
9
+ ### **Test Categories**
10
+
11
+ #### **1. PostgreSQL Integration Tests**
12
+ - **File**: `spec/postgresql_integration_spec.rb`
13
+ - **Purpose**: Basic PostgreSQL schema operations
14
+ - **Examples**: 5 tests
15
+ - **Focus**: Core schema creation, switching, and deletion
16
+
17
+ #### **2. Multiple Schemas Integration Tests**
18
+ - **File**: `spec/multiple_schemas_integration_spec.rb`
19
+ - **Purpose**: Complex multi-tenant scenarios
20
+ - **Examples**: 8 tests
21
+ - **Focus**: Schema isolation, concurrent access, complex scenarios
22
+
23
+ #### **3. Multiple Schema Database Operations**
24
+ - **File**: `spec/multiple_schemas_database_spec.rb`
25
+ - **Purpose**: Bulk operations and data management
26
+ - **Examples**: 4 tests
27
+ - **Focus**: Cross-schema queries, bulk operations, dependency management
28
+
29
+ #### **4. Multiple Tenant Context Tests**
30
+ - **File**: `spec/multiple_tenants_context_spec.rb`
31
+ - **Purpose**: Context switching between tenants
32
+ - **Examples**: 4 tests
33
+ - **Focus**: Thread safety, context isolation
34
+
35
+ ## 🔧 **Database Configuration**
36
+
37
+ ### **Environment Setup**
38
+
39
+ Integration tests use environment variables for PostgreSQL connection:
40
+
41
+ ```bash
42
+ # Required PostgreSQL settings
43
+ export PG_HOST=localhost # Database host
44
+ export PG_PORT=5432 # Database port
45
+ export PG_TEST_DATABASE=pg_multitenant_test # Test database
46
+ export PG_USER=postgres # Database user
47
+ export PG_PASSWORD= # Database password (if needed)
48
+ ```
49
+
50
+ ### **Database Preparation**
51
+
52
+ ```bash
53
+ # Create test database
54
+ createdb pg_multitenant_test
55
+
56
+ # Alternative: Using psql
57
+ psql -c "CREATE DATABASE pg_multitenant_test;"
58
+
59
+ # Grant permissions (if needed)
60
+ psql -d pg_multitenant_test -c "GRANT ALL PRIVILEGES ON SCHEMA public TO postgres;"
61
+ ```
62
+
63
+ ### **Connection Configuration**
64
+
65
+ Each integration test establishes its own database connection:
66
+
67
+ ```ruby
68
+ let(:db_config) do
69
+ {
70
+ host: ENV["PG_HOST"] || "localhost",
71
+ port: ENV["PG_PORT"] || 5432,
72
+ dbname: ENV["PG_TEST_DATABASE"] || "pg_multitenant_test",
73
+ user: ENV["PG_USER"] || "postgres",
74
+ password: ENV["PG_PASSWORD"] || ""
75
+ }
76
+ end
77
+
78
+ let(:conn) do
79
+ PG.connect(db_config)
80
+ rescue PG::ConnectionBad => e
81
+ skip "PostgreSQL not available: #{e.message}"
82
+ end
83
+ ```
84
+
85
+ ## 🧪 **Test Execution**
86
+
87
+ ### **Running Integration Tests**
88
+
89
+ ```bash
90
+ # Run all integration tests
91
+ bundle exec rspec --tag integration
92
+
93
+ # Run with documentation format
94
+ bundle exec rspec --tag integration --format documentation
95
+
96
+ # Run specific integration test file
97
+ bundle exec rspec spec/postgresql_integration_spec.rb
98
+
99
+ # Run specific test example
100
+ bundle exec rspec ./spec/multiple_schemas_integration_spec.rb:90
101
+ ```
102
+
103
+ ### **Expected Output**
104
+
105
+ ```bash
106
+ Multiple Schema Database Operations
107
+ multiple schema creation and management
108
+ ✓ creates and manages multiple tenant schemas
109
+ ✓ handles cross-schema queries safely
110
+ ✓ performs bulk operations across multiple schemas
111
+ schema cleanup with dependencies
112
+ ✓ cleans up multiple schemas with foreign key relationships
113
+
114
+ Multiple Schemas Integration Tests
115
+ multiple tenant schemas
116
+ ✓ creates multiple schemas successfully
117
+ ✓ switches between multiple schemas
118
+ ✓ maintains data isolation between schemas
119
+ ✓ handles concurrent schema access safely
120
+ ✓ drops multiple schemas cleanly
121
+ schema search path with multiple schemas
122
+ ✓ handles complex search paths
123
+ schema migration scenarios
124
+ ✓ handles schema creation with existing objects
125
+ error scenarios with multiple schemas
126
+ ✓ handles dropping non-existent schemas gracefully
127
+ ✓ handles schema dependencies correctly
128
+
129
+ Finished in 0.67914 seconds
130
+ 21 examples, 0 failures
131
+ ```
132
+
133
+ ## 📋 **Detailed Test Scenarios**
134
+
135
+ ### **PostgreSQL Integration Tests**
136
+
137
+ #### **Schema Creation Tests**
138
+ ```ruby
139
+ describe "schema creation" do
140
+ it "actually creates a schema in PostgreSQL" do
141
+ schema_name = "test_schema"
142
+
143
+ # Create schema using gem
144
+ PgMultitenantSchemas::SchemaSwitcher.create_schema(schema_name)
145
+
146
+ # Verify schema exists in PostgreSQL
147
+ result = conn.exec("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '#{schema_name}';")
148
+ expect(result.ntuples).to eq(1)
149
+ expect(result.getvalue(0, 0)).to eq(schema_name)
150
+ end
151
+ end
152
+ ```
153
+
154
+ #### **Schema Switching Tests**
155
+ ```ruby
156
+ describe "schema switching" do
157
+ it "actually switches the search_path" do
158
+ schema_name = "test_schema"
159
+ PgMultitenantSchemas::SchemaSwitcher.create_schema(schema_name)
160
+
161
+ # Switch to schema
162
+ PgMultitenantSchemas::SchemaSwitcher.switch_schema(schema_name)
163
+
164
+ # Verify search_path changed
165
+ result = conn.exec("SELECT current_schema();")
166
+ expect(result.getvalue(0, 0)).to eq(schema_name)
167
+ end
168
+ end
169
+ ```
170
+
171
+ ### **Multiple Schemas Integration Tests**
172
+
173
+ #### **Data Isolation Tests**
174
+ ```ruby
175
+ describe "multiple tenant schemas" do
176
+ it "maintains data isolation between schemas" do
177
+ tenants = ["tenant_a", "tenant_b", "tenant_c"]
178
+
179
+ # Create schemas and insert tenant-specific data
180
+ tenants.each do |tenant|
181
+ PgMultitenantSchemas::SchemaSwitcher.create_schema(tenant)
182
+ PgMultitenantSchemas::SchemaSwitcher.switch_schema(tenant)
183
+
184
+ conn.exec("CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(50));")
185
+ conn.exec("INSERT INTO users (name) VALUES ('User from #{tenant}');")
186
+ end
187
+
188
+ # Verify each schema contains only its own data
189
+ tenants.each do |tenant|
190
+ PgMultitenantSchemas::SchemaSwitcher.switch_schema(tenant)
191
+ result = conn.exec("SELECT name FROM users;")
192
+ expect(result.getvalue(0, 0)).to eq("User from #{tenant}")
193
+ end
194
+ end
195
+ end
196
+ ```
197
+
198
+ #### **Concurrent Access Tests**
199
+ ```ruby
200
+ describe "concurrent schema access" do
201
+ it "handles concurrent schema access safely" do
202
+ tenants = ["tenant_a", "tenant_b", "tenant_c"]
203
+
204
+ # Create multiple database connections
205
+ connections = tenants.map { |_| PG.connect(db_config) }
206
+
207
+ begin
208
+ # Each connection works in different schema
209
+ connections.each_with_index do |connection, index|
210
+ tenant = tenants[index]
211
+
212
+ # Set search path for this specific connection
213
+ connection.exec("SET search_path TO #{connection.escape_identifier(tenant)}")
214
+
215
+ # Clean up from previous runs
216
+ connection.exec("DROP TABLE IF EXISTS orders CASCADE")
217
+
218
+ # Create table and insert data
219
+ connection.exec("CREATE TABLE orders (id SERIAL PRIMARY KEY, tenant_name VARCHAR(50));")
220
+ connection.exec("INSERT INTO orders (tenant_name) VALUES ('#{tenant}');")
221
+
222
+ # Verify isolation
223
+ result = connection.exec("SELECT tenant_name FROM orders;")
224
+ expect(result.getvalue(0, 0)).to eq(tenant)
225
+ end
226
+ ensure
227
+ connections.each(&:close)
228
+ end
229
+ end
230
+ end
231
+ ```
232
+
233
+ ### **Database Operations Tests**
234
+
235
+ #### **Cross-Schema Query Tests**
236
+ ```ruby
237
+ describe "cross-schema queries" do
238
+ it "handles cross-schema queries safely" do
239
+ schemas = ["schema_with_users", "schema_with_products"]
240
+
241
+ schemas.each do |schema|
242
+ PgMultitenantSchemas::SchemaSwitcher.create_schema(schema)
243
+ PgMultitenantSchemas::SchemaSwitcher.switch_schema(schema)
244
+
245
+ if schema.include?("users")
246
+ conn.exec("CREATE TABLE customers (id SERIAL PRIMARY KEY, name VARCHAR(50));")
247
+ conn.exec("INSERT INTO customers (name) VALUES ('John Doe');")
248
+ else
249
+ conn.exec("CREATE TABLE customers (id SERIAL PRIMARY KEY, product VARCHAR(50));")
250
+ conn.exec("INSERT INTO customers (product) VALUES ('Widget');")
251
+ end
252
+ end
253
+
254
+ # Verify each schema maintains its own table structure
255
+ PgMultitenantSchemas::SchemaSwitcher.switch_schema("schema_with_users")
256
+ result = conn.exec("SELECT column_name FROM information_schema.columns WHERE table_name = 'customers' AND table_schema = 'schema_with_users' ORDER BY ordinal_position;")
257
+ user_columns = result.map { |row| row["column_name"] }
258
+ expect(user_columns).to include("name")
259
+ expect(user_columns).not_to include("product")
260
+ end
261
+ end
262
+ ```
263
+
264
+ #### **Bulk Operations Tests**
265
+ ```ruby
266
+ describe "bulk operations" do
267
+ it "performs bulk operations across multiple schemas" do
268
+ schemas = ["company_a", "company_b"]
269
+
270
+ # Set up multiple schemas with data
271
+ schemas.each do |schema|
272
+ PgMultitenantSchemas::SchemaSwitcher.create_schema(schema)
273
+ PgMultitenantSchemas::SchemaSwitcher.switch_schema(schema)
274
+
275
+ conn.exec("CREATE TABLE analytics (id SERIAL PRIMARY KEY, metric_value INTEGER);")
276
+ conn.exec("INSERT INTO analytics (metric_value) VALUES (100), (200), (300);")
277
+ end
278
+
279
+ # Perform bulk analysis across schemas
280
+ total_metrics = 0
281
+ schemas.each do |schema|
282
+ PgMultitenantSchemas::SchemaSwitcher.switch_schema(schema)
283
+ result = conn.exec("SELECT SUM(metric_value) as total FROM analytics;")
284
+ total_metrics += result.getvalue(0, 0).to_i
285
+ end
286
+
287
+ expect(total_metrics).to eq(1200) # (100+200+300) * 2 schemas
288
+ end
289
+ end
290
+ ```
291
+
292
+ ## 🧹 **Test Cleanup and Safety**
293
+
294
+ ### **Automatic Cleanup**
295
+
296
+ Every integration test includes comprehensive cleanup:
297
+
298
+ ```ruby
299
+ after do
300
+ # Clean up test schemas created during tests
301
+ ["tenant_a", "tenant_b", "tenant_c", "public_test", "test_schema",
302
+ "another_test_schema", "company_a", "company_b", "company_c",
303
+ "schema_with_users", "schema_with_products", "sub_tenant",
304
+ "dependency_test"].each do |schema|
305
+ begin
306
+ PgMultitenantSchemas::SchemaSwitcher.drop_schema(schema, cascade: true)
307
+ rescue => e
308
+ # Log but don't fail tests on cleanup errors
309
+ puts "Cleanup warning: #{e.message}" if ENV['DEBUG_TESTS']
310
+ end
311
+ end
312
+
313
+ # Always reset to public schema
314
+ PgMultitenantSchemas::SchemaSwitcher.reset_to_public_schema
315
+
316
+ # Close connections
317
+ conn&.close
318
+ end
319
+ ```
320
+
321
+ ### **Schema Naming Strategy**
322
+
323
+ Integration tests use predictable schema names for easy cleanup:
324
+
325
+ - **Tenant Schemas**: `tenant_a`, `tenant_b`, `tenant_c`
326
+ - **Company Schemas**: `company_a`, `company_b`, `company_c`
327
+ - **Feature Schemas**: `schema_with_users`, `schema_with_products`
328
+ - **Test Schemas**: `test_schema`, `another_test_schema`
329
+ - **Special Cases**: `public_test`, `sub_tenant`, `dependency_test`
330
+
331
+ ### **Cascade Deletion**
332
+
333
+ Tests use `CASCADE` option to handle schema dependencies:
334
+
335
+ ```ruby
336
+ # Drop schema with all dependent objects
337
+ PgMultitenantSchemas::SchemaSwitcher.drop_schema(schema_name, cascade: true)
338
+ ```
339
+
340
+ ## 🚨 **Troubleshooting Integration Tests**
341
+
342
+ ### **Common Issues and Solutions**
343
+
344
+ #### **PostgreSQL Connection Failures**
345
+ ```bash
346
+ # Error: PostgreSQL not available
347
+ # Solution: Check PostgreSQL service
348
+ brew services start postgresql # macOS
349
+ sudo service postgresql start # Linux
350
+
351
+ # Verify connection
352
+ pg_isready -h localhost -p 5432
353
+ ```
354
+
355
+ #### **Permission Errors**
356
+ ```bash
357
+ # Error: permission denied to create schema
358
+ # Solution: Grant schema creation permissions
359
+ psql -d pg_multitenant_test -c "GRANT CREATE ON DATABASE pg_multitenant_test TO postgres;"
360
+ ```
361
+
362
+ #### **Schema Already Exists**
363
+ ```bash
364
+ # Error: schema "tenant_a" already exists
365
+ # Solution: Manual cleanup
366
+ psql -d pg_multitenant_test -c "DROP SCHEMA IF EXISTS tenant_a CASCADE;"
367
+ ```
368
+
369
+ #### **Table Already Exists**
370
+ ```ruby
371
+ # Error: relation "orders" already exists
372
+ # Fix: Add proper cleanup in test
373
+ before do
374
+ conn.exec("DROP TABLE IF EXISTS orders CASCADE")
375
+ end
376
+ ```
377
+
378
+ ### **Debug Mode**
379
+
380
+ Enable debug output for troubleshooting:
381
+
382
+ ```bash
383
+ # Run with debug information
384
+ DEBUG_TESTS=1 bundle exec rspec --tag integration
385
+
386
+ # Run specific failing test with verbose output
387
+ bundle exec rspec ./spec/multiple_schemas_integration_spec.rb:90 --format documentation --backtrace
388
+ ```
389
+
390
+ ### **Manual Database Inspection**
391
+
392
+ ```bash
393
+ # Connect to test database
394
+ psql -d pg_multitenant_test
395
+
396
+ # List all schemas
397
+ \dn
398
+
399
+ # Check current schema
400
+ SELECT current_schema();
401
+
402
+ # List tables in specific schema
403
+ \dt tenant_a.*
404
+
405
+ # Clean up manually if needed
406
+ DROP SCHEMA IF EXISTS tenant_a CASCADE;
407
+ ```
408
+
409
+ ## 📊 **Integration Test Metrics**
410
+
411
+ ### **Performance Benchmarks**
412
+
413
+ - **Total Integration Tests**: 21 examples
414
+ - **Average Execution Time**: ~0.67 seconds
415
+ - **Database Operations**: ~50+ schema create/drop operations
416
+ - **Concurrent Connections**: Up to 3 simultaneous connections
417
+ - **Data Isolation**: 100% between tenant schemas
418
+
419
+ ### **Coverage Areas**
420
+
421
+ ✅ **Schema Lifecycle**: Create, switch, drop operations
422
+ ✅ **Data Isolation**: Complete tenant separation
423
+ ✅ **Concurrent Access**: Multi-connection safety
424
+ ✅ **Error Handling**: Graceful failure scenarios
425
+ ✅ **Complex Queries**: Cross-schema operations
426
+ ✅ **Bulk Operations**: Multi-tenant data processing
427
+ ✅ **Dependencies**: Foreign key and cascade handling
428
+ ✅ **Search Path**: PostgreSQL search_path management
429
+
430
+ ## 🎯 **Best Practices for Integration Testing**
431
+
432
+ ### **Test Development Guidelines**
433
+
434
+ 1. **Real Database Operations**: Always test against actual PostgreSQL
435
+ 2. **Clean Isolation**: Each test creates and destroys its own schemas
436
+ 3. **Comprehensive Cleanup**: Always clean up after tests, even on failure
437
+ 4. **Connection Management**: Properly close database connections
438
+ 5. **Error Scenarios**: Test both success and failure paths
439
+ 6. **Performance Awareness**: Monitor test execution time
440
+ 7. **Thread Safety**: Test concurrent access patterns
441
+ 8. **Documentation**: Document complex test scenarios
442
+
443
+ ### **Integration Test Checklist**
444
+
445
+ - [ ] Database connection established
446
+ - [ ] Schemas created with unique names
447
+ - [ ] Operations tested with real PostgreSQL
448
+ - [ ] Data isolation verified
449
+ - [ ] Cleanup performed in after hooks
450
+ - [ ] Error scenarios tested
451
+ - [ ] Thread safety validated
452
+ - [ ] Performance characteristics measured
453
+
454
+ This comprehensive integration testing approach ensures the PgMultitenantSchemas gem works reliably in real-world PostgreSQL environments with multiple tenants and complex schema operations.