forked from cleveragents/cleveragents-core
ec52acffb7
Implements all missing CRUD and checkpoint methods on DatabaseResourceHandler per issue #1241 (Epic #825 ResourceHandler Protocol Completion): - read(): SQLite queries sqlite_master for schema; remote returns connection-info - write(): SQLite executes SQL statements; remote returns not-supported result - delete(): SQLite executes DROP TABLE IF EXISTS; remote returns not-supported - list_children(): SQLite lists tables/views from sqlite_master; remote returns [] - diff(): compares schemas via content_hash on both sides - create_checkpoint(): SQLite creates SAVEPOINT on open connection; remote uses content-hash fallback - rollback_to(): SQLite executes ROLLBACK TO SAVEPOINT; remote returns not-supported All methods handle errors gracefully (no unhandled exceptions). Adds comprehensive Behave BDD tests covering all methods for both SQLite and remote database types. ISSUES CLOSED: #1241 Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me> Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
220 lines
10 KiB
Gherkin
220 lines
10 KiB
Gherkin
Feature: DatabaseResourceHandler CRUD and checkpoint methods
|
|
As a CleverAgents developer
|
|
I want DatabaseResourceHandler to implement full CRUD and checkpoint methods
|
|
So that SQLite databases can be read, written, deleted, listed, diffed, and checkpointed
|
|
|
|
Issue #1241: DatabaseResourceHandler CRUD and checkpoint methods
|
|
|
|
# ============================================================
|
|
# read() — SQLite schema query
|
|
# ============================================================
|
|
|
|
Scenario: read() returns schema for SQLite database with tables
|
|
Given a SQLite database with tables "users" and "orders"
|
|
When I read from the database handler
|
|
Then the db read content should contain "users"
|
|
And the db read content should contain "orders"
|
|
And the db read content encoding should be "utf-8"
|
|
And the db read content hash should not be empty
|
|
|
|
Scenario: read() returns empty schema for empty SQLite database
|
|
Given an empty SQLite database file
|
|
When I read from the database handler
|
|
Then the db read content encoding should be "utf-8"
|
|
And the db read content data should be empty or whitespace
|
|
|
|
Scenario: read() returns connection-info summary for remote database
|
|
Given a remote postgres database resource with location "postgresql://localhost/mydb"
|
|
When I read from the database handler
|
|
Then the db read content should contain "postgres"
|
|
And the db read content should contain "mydb"
|
|
And the db read content encoding should be "utf-8"
|
|
|
|
Scenario: read() returns empty content for resource with no location
|
|
Given a database resource with no location
|
|
When I read from the database handler
|
|
Then the db read content data should be empty or whitespace
|
|
|
|
Scenario: read() handles SQLite error gracefully
|
|
Given a database resource pointing to a non-existent SQLite path
|
|
When I read from the database handler
|
|
Then no db exceptions should have been raised
|
|
|
|
# ============================================================
|
|
# write() — SQLite SQL execution
|
|
# ============================================================
|
|
|
|
Scenario: write() executes SQL on SQLite database
|
|
Given a SQLite database with table "items"
|
|
When I write SQL "INSERT INTO items (id, name) VALUES (1, 'apple')" to the database handler
|
|
Then the db write result should be successful
|
|
And the SQLite table "items" should contain 1 row
|
|
|
|
Scenario: write() creates a new table via SQL
|
|
Given an empty SQLite database file
|
|
When I write SQL "CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT)" to the database handler
|
|
Then the db write result should be successful
|
|
And the SQLite database should have table "products"
|
|
|
|
Scenario: write() returns not-supported for remote postgres database
|
|
Given a remote postgres database resource with location "postgresql://localhost/mydb"
|
|
When I write SQL "SELECT 1" to the database handler
|
|
Then the db write result should not be successful
|
|
And the db write result message should mention "not supported"
|
|
|
|
Scenario: write() returns not-supported for remote mysql database
|
|
Given a remote mysql database resource with location "mysql://localhost/mydb"
|
|
When I write SQL "SELECT 1" to the database handler
|
|
Then the db write result should not be successful
|
|
And the db write result message should mention "not supported"
|
|
|
|
Scenario: write() returns failure for resource with no location
|
|
Given a database resource with no location
|
|
When I write SQL "SELECT 1" to the database handler
|
|
Then the db write result should not be successful
|
|
|
|
Scenario: write() returns failure for invalid SQL
|
|
Given an empty SQLite database file
|
|
When I write SQL "THIS IS NOT SQL" to the database handler
|
|
Then the db write result should not be successful
|
|
And the db write result message should mention "error"
|
|
|
|
# ============================================================
|
|
# delete() — SQLite DROP TABLE
|
|
# ============================================================
|
|
|
|
Scenario: delete() drops a table from SQLite database
|
|
Given a SQLite database with table "temp_data"
|
|
When I delete table "temp_data" from the database handler
|
|
Then the db delete result should be successful
|
|
And the SQLite database should not have table "temp_data"
|
|
|
|
Scenario: delete() with non-existent table succeeds (DROP TABLE IF EXISTS)
|
|
Given an empty SQLite database file
|
|
When I delete table "ghost_table" from the database handler
|
|
Then the db delete result should be successful
|
|
|
|
Scenario: delete() with empty path returns failure
|
|
Given an empty SQLite database file
|
|
When I delete with empty path from the database handler
|
|
Then the db delete result should not be successful
|
|
And the db delete result message should mention "table name"
|
|
|
|
Scenario: delete() returns not-supported for remote postgres database
|
|
Given a remote postgres database resource with location "postgresql://localhost/mydb"
|
|
When I delete table "users" from the database handler
|
|
Then the db delete result should not be successful
|
|
And the db delete result message should mention "not supported"
|
|
|
|
Scenario: delete() returns failure for resource with no location
|
|
Given a database resource with no location
|
|
When I delete table "users" from the database handler
|
|
Then the db delete result should not be successful
|
|
|
|
# ============================================================
|
|
# list_children() — SQLite tables/views
|
|
# ============================================================
|
|
|
|
Scenario: list_children() returns table names for SQLite database
|
|
Given a SQLite database with tables "alpha" and "beta"
|
|
When I list children from the database handler
|
|
Then the db children list should contain "alpha"
|
|
And the db children list should contain "beta"
|
|
And the db children list should be sorted
|
|
|
|
Scenario: list_children() returns empty list for empty SQLite database
|
|
Given an empty SQLite database file
|
|
When I list children from the database handler
|
|
Then the db children list should be empty
|
|
|
|
Scenario: list_children() returns empty list for remote database
|
|
Given a remote postgres database resource with location "postgresql://localhost/mydb"
|
|
When I list children from the database handler
|
|
Then the db children list should be empty
|
|
|
|
Scenario: list_children() returns empty list for resource with no location
|
|
Given a database resource with no location
|
|
When I list children from the database handler
|
|
Then the db children list should be empty
|
|
|
|
# ============================================================
|
|
# diff() — schema hash comparison
|
|
# ============================================================
|
|
|
|
Scenario: diff() detects no changes between identical SQLite databases
|
|
Given a SQLite database with table "same_table"
|
|
And a second SQLite database with the same schema
|
|
When I diff the database handler against the second database
|
|
Then the db diff result should have no changes
|
|
|
|
Scenario: diff() detects changes between different SQLite databases
|
|
Given a SQLite database with table "table_a"
|
|
And a second SQLite database with table "table_b"
|
|
When I diff the database handler against the second database
|
|
Then the db diff result should have changes
|
|
|
|
Scenario: diff() compares remote database by identity hash
|
|
Given a remote postgres database resource with location "postgresql://localhost/db1"
|
|
And a second remote database location "postgresql://localhost/db2"
|
|
When I diff the database handler against the second remote location
|
|
Then the db diff result should have changes
|
|
|
|
# ============================================================
|
|
# create_checkpoint() — SQLite SAVEPOINT
|
|
# ============================================================
|
|
|
|
Scenario: create_checkpoint() creates a SAVEPOINT on SQLite database
|
|
Given a SQLite database with table "checkpoint_test"
|
|
And a mock sandbox manager
|
|
When I create a checkpoint with plan "PLAN-CKPT-001" on the database handler
|
|
Then the db checkpoint result should have a checkpoint_id
|
|
And the db checkpoint result plan_id should be "PLAN-CKPT-001"
|
|
And the db checkpoint result message should mention "SAVEPOINT"
|
|
|
|
Scenario: create_checkpoint() returns content-hash checkpoint for remote database
|
|
Given a remote postgres database resource with location "postgresql://localhost/mydb"
|
|
And a mock sandbox manager
|
|
When I create a checkpoint with plan "PLAN-CKPT-002" on the database handler
|
|
Then the db checkpoint result should have a checkpoint_id
|
|
And the db checkpoint result message should mention "hash"
|
|
|
|
# ============================================================
|
|
# rollback_to() — SQLite ROLLBACK TO SAVEPOINT
|
|
# ============================================================
|
|
|
|
Scenario: rollback_to() rolls back SQLite changes to SAVEPOINT
|
|
Given a SQLite database with table "rollback_test"
|
|
And a mock sandbox manager
|
|
When I create a checkpoint with plan "PLAN-RB-001" on the database handler
|
|
And I insert a row into "rollback_test" via the database handler
|
|
And I rollback to the last checkpoint on the database handler
|
|
Then the db rollback result should be successful
|
|
And the SQLite table "rollback_test" should contain 0 rows
|
|
|
|
Scenario: rollback_to() returns failure for unknown checkpoint_id
|
|
Given a SQLite database with table "rollback_test2"
|
|
And a mock sandbox manager
|
|
When I rollback to checkpoint "nonexistent-ckpt" on the database handler
|
|
Then the db rollback result should not be successful
|
|
And the db rollback result message should mention "not found"
|
|
|
|
Scenario: rollback_to() returns not-supported for remote database
|
|
Given a remote postgres database resource with location "postgresql://localhost/mydb"
|
|
And a mock sandbox manager
|
|
When I rollback to checkpoint "some-ckpt" on the database handler
|
|
Then the db rollback result should not be successful
|
|
And the db rollback result message should mention "not supported"
|
|
|
|
# ============================================================
|
|
# Error handling
|
|
# ============================================================
|
|
|
|
Scenario: All methods handle missing location gracefully
|
|
Given a database resource with no location
|
|
And a mock sandbox manager
|
|
When I read from the database handler
|
|
And I write SQL "SELECT 1" to the database handler
|
|
And I delete with empty path from the database handler
|
|
And I list children from the database handler
|
|
Then no db exceptions should have been raised
|