database/changeset_repository: add failing test for SqliteChangeSetStore._plan_map data loss on restart #10399

Open
opened 2026-04-18 09:29:20 +00:00 by HAL9000 · 0 comments
Owner

Metadata

  • Commit message: test(database): add failing test for SqliteChangeSetStore._plan_map data loss on restart
  • Branch name: test/sqlite-changeset-store-plan-map-data-loss

Background and Context

SqliteChangeSetStore maintains an in-memory _plan_map dict that maps changeset_id → plan_id. This mapping is never persisted to the database. After a process restart, calling get(changeset_id) for a changeset that has no entries yet returns None instead of the correct SpecChangeSet, because the plan_id is only stored in _plan_map.

A failing test should be written to document this data-loss behaviour before the fix is implemented.

Expected Behavior

SqliteChangeSetStore.get(changeset_id) should return the correct SpecChangeSet even after a process restart (i.e., when a new SqliteChangeSetStore instance is created), including for changesets that have been started but have no entries yet.

Acceptance Criteria

  • A test file tests/infrastructure/database/test_changeset_repository_persistence.py exists with the test test_sqlite_changeset_store_get_survives_restart
  • The test is marked @pytest.mark.tdd_expected_fail
  • The test is marked @pytest.mark.tdd_issue and @pytest.mark.tdd_issue_2
  • Running the test confirms it fails because store2._plan_map is empty after restart
  • CI confirms the test fails for the right reason (in-memory _plan_map)

Subtasks

  • Write the failing test test_sqlite_changeset_store_get_survives_restart in tests/infrastructure/database/test_changeset_repository_persistence.py
  • Mark the test with @pytest.mark.tdd_expected_fail, @pytest.mark.tdd_issue, and @pytest.mark.tdd_issue_2
  • Verify the test fails with the expected error message about _plan_map being empty after restart
  • Commit the failing test

Test Specification

# tests/infrastructure/database/test_changeset_repository_persistence.py

import pytest
from unittest.mock import MagicMock
from sqlalchemy.orm import Session
from cleveragents.infrastructure.database.changeset_repository import SqliteChangeSetStore

@pytest.mark.tdd_issue
@pytest.mark.tdd_issue_2
@pytest.mark.tdd_expected_fail
def test_sqlite_changeset_store_get_survives_restart(session_factory):
    """SqliteChangeSetStore.get() must work after a restart for empty changesets.

    Currently FAILS because _plan_map is in-memory only.  After creating a new
    SqliteChangeSetStore instance (simulating a restart), get() returns None for
    a changeset that was started but has no entries yet.
    """
    plan_id = "01HXYZ1234567890ABCDEFGHIJ"

    store1 = SqliteChangeSetStore(session_factory)
    changeset_id = store1.start(plan_id)

    # Simulate restart by creating a new instance
    store2 = SqliteChangeSetStore(session_factory)
    result = store2.get(changeset_id)

    # This assertion FAILS because store2._plan_map is empty
    assert result is not None, (
        "SqliteChangeSetStore.get() must return the SpecChangeSet even after "
        "a restart; the plan_id mapping must be persisted to the database."
    )
    assert result.plan_id == plan_id

Evidence

src/cleveragents/infrastructure/database/changeset_repository.py:

class SqliteChangeSetStore:
    def __init__(self, session_factory: Callable[[], Session]) -> None:
        ...
        self._plan_map: dict[str, str] = {}   # ← in-memory only, lost on restart

    def start(self, plan_id: str) -> str:
        changeset_id = str(ULID())
        self._plan_map[changeset_id] = plan_id   # ← stored only in memory
        return changeset_id

    def get(self, changeset_id: str) -> SpecChangeSet | None:
        entries = self._entry_repo.get_entries_for_changeset(changeset_id)
        if not entries:
            plan_id = self._plan_map.get(changeset_id, "")   # ← empty after restart
            if not plan_id:
                return None   # ← returns None for valid empty changeset after restart
            ...

Definition of Done

  • Failing test written and committed
  • Test is marked @pytest.mark.tdd_expected_fail
  • CI confirms the test fails for the right reason (in-memory _plan_map)

Automated by CleverAgents Bot
Agent: new-issue-creator

## Metadata - **Commit message:** `test(database): add failing test for SqliteChangeSetStore._plan_map data loss on restart` - **Branch name:** `test/sqlite-changeset-store-plan-map-data-loss` ## Background and Context `SqliteChangeSetStore` maintains an in-memory `_plan_map` dict that maps `changeset_id → plan_id`. This mapping is never persisted to the database. After a process restart, calling `get(changeset_id)` for a changeset that has no entries yet returns `None` instead of the correct `SpecChangeSet`, because the `plan_id` is only stored in `_plan_map`. A failing test should be written to document this data-loss behaviour before the fix is implemented. ## Expected Behavior `SqliteChangeSetStore.get(changeset_id)` should return the correct `SpecChangeSet` even after a process restart (i.e., when a new `SqliteChangeSetStore` instance is created), including for changesets that have been started but have no entries yet. ## Acceptance Criteria - [ ] A test file `tests/infrastructure/database/test_changeset_repository_persistence.py` exists with the test `test_sqlite_changeset_store_get_survives_restart` - [ ] The test is marked `@pytest.mark.tdd_expected_fail` - [ ] The test is marked `@pytest.mark.tdd_issue` and `@pytest.mark.tdd_issue_2` - [ ] Running the test confirms it fails because `store2._plan_map` is empty after restart - [ ] CI confirms the test fails for the right reason (in-memory `_plan_map`) ## Subtasks - [ ] Write the failing test `test_sqlite_changeset_store_get_survives_restart` in `tests/infrastructure/database/test_changeset_repository_persistence.py` - [ ] Mark the test with `@pytest.mark.tdd_expected_fail`, `@pytest.mark.tdd_issue`, and `@pytest.mark.tdd_issue_2` - [ ] Verify the test fails with the expected error message about `_plan_map` being empty after restart - [ ] Commit the failing test ## Test Specification ```python # tests/infrastructure/database/test_changeset_repository_persistence.py import pytest from unittest.mock import MagicMock from sqlalchemy.orm import Session from cleveragents.infrastructure.database.changeset_repository import SqliteChangeSetStore @pytest.mark.tdd_issue @pytest.mark.tdd_issue_2 @pytest.mark.tdd_expected_fail def test_sqlite_changeset_store_get_survives_restart(session_factory): """SqliteChangeSetStore.get() must work after a restart for empty changesets. Currently FAILS because _plan_map is in-memory only. After creating a new SqliteChangeSetStore instance (simulating a restart), get() returns None for a changeset that was started but has no entries yet. """ plan_id = "01HXYZ1234567890ABCDEFGHIJ" store1 = SqliteChangeSetStore(session_factory) changeset_id = store1.start(plan_id) # Simulate restart by creating a new instance store2 = SqliteChangeSetStore(session_factory) result = store2.get(changeset_id) # This assertion FAILS because store2._plan_map is empty assert result is not None, ( "SqliteChangeSetStore.get() must return the SpecChangeSet even after " "a restart; the plan_id mapping must be persisted to the database." ) assert result.plan_id == plan_id ``` ## Evidence `src/cleveragents/infrastructure/database/changeset_repository.py`: ```python class SqliteChangeSetStore: def __init__(self, session_factory: Callable[[], Session]) -> None: ... self._plan_map: dict[str, str] = {} # ← in-memory only, lost on restart def start(self, plan_id: str) -> str: changeset_id = str(ULID()) self._plan_map[changeset_id] = plan_id # ← stored only in memory return changeset_id def get(self, changeset_id: str) -> SpecChangeSet | None: entries = self._entry_repo.get_entries_for_changeset(changeset_id) if not entries: plan_id = self._plan_map.get(changeset_id, "") # ← empty after restart if not plan_id: return None # ← returns None for valid empty changeset after restart ... ``` ## Definition of Done - [ ] Failing test written and committed - [ ] Test is marked `@pytest.mark.tdd_expected_fail` - [ ] CI confirms the test fails for the right reason (in-memory `_plan_map`) --- **Automated by CleverAgents Bot** Agent: new-issue-creator
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#10399
No description provided.