Files
cleveragents-core/features/database_handler_crud.feature
T
freemo 02250473ad fix(ci): restore all CI quality gates to passing on master
Fix all failing CI quality gates (lint, unit_tests, format) without
suppressing any quality enforcement.

Root causes and fixes:

1. Format: features/steps/plan_namespaced_name_tdd_steps.py had trailing
   whitespace; fixed by running ruff format.

2. Unit tests - A2A JSON-RPC 2.0 migration (commit 9c6d6915) renamed
   A2aRequest fields (operation→method, request_id→id, a2a_version→jsonrpc)
   and A2aResponse fields (status+data→result, request_id→id) but did not
   update all step files and feature files:
   - a2a_jsonrpc_wire_format_steps.py: added use_step_matcher('re') and
     reset to 'parse' at end to prevent parallel test interference
   - a2a_facade_wiring_steps.py: updated operation= to method=, .status/.data
     to .result
   - a2a_facade_steps.py: updated request_id→id, a2a_version→jsonrpc,
     A2aResponse(request_id=..., status=...) to new API
   - m6_facade_steps.py: updated all old API usage
   - devcontainer_cleanup_steps.py: updated A2aRequest(operation=...)
   - plan_prompt_command_steps.py: updated A2aRequest(operation=...)
   - wf03_plan_prompt_confidence_steps.py: updated A2aRequest(operation=...)
   - consolidated_misc.feature: updated old A2aRequest/A2aResponse scenarios

3. Unit tests - Session CLI output changed (commit 0d5d9cf0 and others):
   - 'Session Created' → 'Session created' (lowercase)
   - 'Session Details' → 'Session Summary'
   - 'Sessions (N total)' → 'Sessions'
   - session list JSON: top-level 'total' → nested 'summary.total'
   - Fixed in: session_cli.feature, session_cli_coverage_boost.feature,
     session_cli_uncovered_branches.feature, session_list_error.feature,
     tdd_session_create_persist_steps.py

4. Unit tests - Plan list output changed (commit 1a07a891):
   - 'V3 Lifecycle Plans' → 'Plans'
   - 'Lifecycle Plans' → 'Plans'
   - Name column removed (restored in source)
   - Invariants column removed (restored in source)
   - Project truncation removed (restored in source)
   - Fixed in: plan_cli_cancel_revert_coverage.feature,
     plan_lifecycle_cli_coverage.feature, plan_cli_coverage_boost_steps.py,
     plan.py (source code restored)

5. Unit tests - Plan apply command now requires ULID (commit 300a5d6d):
   - plan_cli_coverage_r3.feature: updated 'PLAN-001' to valid ULID
   - plan_cli_coverage_r3_steps.py: added --yes flag, added new step for
     no-eligible-plans path

6. Unit tests - Various source code bugs:
   - ThoughtBlock: converted from @dataclass to Pydantic BaseModel
     (architecture test requires all dataclasses to use Pydantic)
   - session.py: added DatabaseError handling to export, import, tell commands
   - database.py: fixed rollback_to() to reuse checkpoint connection for writes
   - database.py: added _get_checkpoint_conn() helper
   - check-tls-cert.py: fixed SSLCertVerificationError.reason AttributeError

7. Unit tests - Test step bugs:
   - error_recovery_coverage_boost_steps.py: fixed invalid ULID _PLAN_ID
   - session_service_coverage_steps.py: fixed 'sha256:' prefix bug in checksum
   - database_models_new_coverage_steps.py: added 'name' field to session mock
   - async_audit_recording_steps.py: fixed Settings(audit_async=False) via env var
   - coverage_threshold_config_steps.py: added --coverage-min pattern support
   - m5_acms_smoke_steps.py: updated usage hint text
   - actor_cli_yaml_steps.py: updated 'Removed actor' → 'Actor removed'
   - aimodelscredentials_steps.py: set context.imported_class in import step
   - domain_base_model.feature: added missing 'When I examine model_config' step
   - tui_first_run_steps.py: fixed module reload to restore cleveragents.tui.*
     modules after test (prevented patch interference in subsequent tests)
   - tui_first_run_steps.py: added set_search('') step for empty string
   - resource_handler_base_coverage_r3_steps.py: use _MinimalHandler instead
     of DatabaseResourceHandler for NotImplementedError tests
   - resource_handler_crud.feature: updated to test new DatabaseHandler behavior
   - resource_handler_sandbox.feature: updated to test new DatabaseHandler behavior
   - tdd_json_decode_crash_persistence.feature: fixed @tdd_bug → @tdd_issue tags

8. Parallel test interference:
   - All step files using use_step_matcher('re') now reset to 'parse' at end
     to prevent global matcher state leaking to subsequent step files
2026-04-04 20:38:16 +00:00

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 row
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