Files
cleveragents-core/features/postgresql_backend.feature
T
HAL9000 8dde6c81ef feat(server): implement PostgreSQL storage backend for server mode
Add PostgreSQL support as the server-mode storage backend alongside
existing SQLite for local mode. Verify all ORM models are dialect-
agnostic, configure connection pooling for multi-user access, add
Docker Compose for local PG development, and wire database URL
selection based on deployment mode.

Changes:
- Add psycopg2-binary dependency to pyproject.toml
- Add server_mode, db_pool_size, db_max_overflow, db_pool_recycle
  settings to Settings with environment variable support
- Add resolve_database_url() and is_postgresql() to Settings for
  mode-aware database URL resolution
- Configure UnitOfWork engine creation with pool_size, max_overflow,
  pool_recycle, and pool_pre_ping for PostgreSQL connections
- Update MigrationRunner to handle both SQLite and PostgreSQL backends
- Add compare_type=True to Alembic env.py for dialect-aware migrations
- Add docker-compose.yml with PostgreSQL 16-alpine for local development
- Add Behave BDD feature (14 scenarios) covering settings, pool config,
  engine creation, ORM dialect compatibility, and migration runner
- Add Robot Framework integration tests (12 test cases) for the
  abstraction layer with requires_postgresql tag for live PG tests

Fixes:
- Restore require_confirmation parameter to UnitOfWork.__init__
- Add argument validation to UnitOfWork.__init__ (fail-fast)
- Add explicit PostgreSQL isolation_level (READ COMMITTED)
- Add dispose() method and context manager support to UnitOfWork
- Fix MigrationRunner.get_current_revision() to dispose engine
- Fix Settings.is_postgresql() to handle ValueError gracefully

ISSUES CLOSED: #878
2026-05-29 01:04:59 -04:00

106 lines
4.5 KiB
Gherkin

@phase1 @database @postgresql @server_mode
Feature: PostgreSQL Storage Backend for Server Mode
As a platform operator
I want CleverAgents to support PostgreSQL as its storage backend
So that server mode can handle multi-user collaborative access
Background:
Given I have imported the database infrastructure modules
# ── Settings & URL Selection ──────────────────────────────────────
@settings @database_url
Scenario: Default database URL is SQLite in local mode
Given I create settings with server_mode disabled
Then the resolved database URL should start with "sqlite"
@settings @database_url
Scenario: Server mode with default URL raises ValueError
Given I create settings with server_mode enabled
And the database_url is the default SQLite value
Then resolving the database URL should raise a ValueError
@settings @database_url
Scenario: Server mode with explicit PostgreSQL URL uses that URL
Given I create settings with server_mode enabled
And the database_url is "postgresql://myhost/mydb"
Then the resolved database URL should be "postgresql://myhost/mydb"
@settings @database_url
Scenario: is_postgresql returns True for PostgreSQL URLs
Given I create settings with server_mode enabled
And the database_url is "postgresql://localhost/cleveragents"
Then is_postgresql should return True
@settings @database_url
Scenario: is_postgresql returns False for SQLite URLs
Given I create settings with server_mode disabled
Then is_postgresql should return False
# ── Pool Configuration ────────────────────────────────────────────
@settings @pool
Scenario: Default pool settings are appropriate for multi-user
Given I create settings with server_mode enabled
Then the db_pool_size should be 5
And the db_max_overflow should be 10
And the db_pool_recycle should be 1800
@settings @pool
Scenario: Pool settings can be overridden via environment
Given I create settings with custom pool configuration
| setting | value |
| db_pool_size | 20 |
| db_max_overflow | 30 |
| db_pool_recycle | 900 |
Then the db_pool_size should be 20
And the db_max_overflow should be 30
And the db_pool_recycle should be 900
# ── UnitOfWork Engine Creation ────────────────────────────────────
@engine @sqlite
Scenario: UnitOfWork creates SQLite engine for local mode
Given I create a UnitOfWork with URL "sqlite:///:memory:"
When I access the engine
Then the engine dialect should be "sqlite"
@engine @pool
Scenario: UnitOfWork passes pool parameters to non-SQLite engines
Given I create a UnitOfWork with URL "sqlite:///:memory:" and pool_size 8
Then the UnitOfWork pool_size should be 8
And the UnitOfWork max_overflow should be 10
And the UnitOfWork pool_recycle should be 1800
# ── ORM Model Dialect Compatibility ───────────────────────────────
@models @dialect
Scenario: ORM models use only dialect-agnostic column types
Given I inspect all ORM models from the Base metadata
Then no column should use a SQLite-specific type
And all columns should use standard SQLAlchemy types
@models @dialect
Scenario: ORM models define no raw SQL or sqlite3 imports
Given I read the models module source code
Then the source should not contain "sqlite3"
And the source should not contain "PRAGMA"
And the source should not contain "raw_sql"
@models @dialect
Scenario: All table names are valid PostgreSQL identifiers
Given I inspect all ORM models from the Base metadata
Then all table names should be valid SQL identifiers
# ── Migration Runner ──────────────────────────────────────────────
@migration
Scenario: Migration runner handles SQLite URLs
Given I create a MigrationRunner with URL "sqlite:///:memory:"
Then the migration runner database_url should start with "sqlite"
@migration
Scenario: Migration runner handles PostgreSQL URLs
Given I create a MigrationRunner with URL "postgresql://localhost/test"
Then the migration runner database_url should start with "postgresql"