Skip to content

fix: clear every actor-owned table in reset_actors! - #31

Merged
cardmagic merged 1 commit into
mainfrom
agent/test-helper-isolation
Aug 11, 2026
Merged

fix: clear every actor-owned table in reset_actors!#31
cardmagic merged 1 commit into
mainfrom
agent/test-helper-isolation

Conversation

@cardmagic

Copy link
Copy Markdown
Owner

Fixes the reported test-isolation gap. Confirmed, and it is wider than reminders.

What the helper did

def reset_actors!
  SolidObjects.reset_caller_process!
  Instance.delete_all
  Process.delete_all
end

Two tables deleted, seven left to the database cascade. Every actor-owned table has an on_delete: :cascade foreign key to solid_objects_instances, so on a database that enforces it the cascade removes the rest and the helper looks complete.

It is only complete where referential integrity is enforced. SQLite has to be asked for foreign keys, MySQL has to be on InnoDB, and a host application may have stripped the constraints out of the copied migration, which is standing policy in some shops. Where the cascade does not fire, rows survive a reset with an instance_id pointing at nothing, and the next test that reads them sees another test's data.

Reproduced

With PRAGMA foreign_keys = OFF, matching a host application whose database does not enforce the cascade:

after reset: instances=0 reminders=1
orphan instance_id: 1

That is exactly the described state: a reminder whose instance_id points at nothing.

It was not only reminders

Restoring the old two-line body with the new test in place shows what actually survived:

Expected ["solid_objects_dead_letters", "solid_objects_claimed_messages",
          "solid_objects_ready_messages", "solid_objects_broadcasts",
          "solid_objects_effects", "solid_objects_reminders",
          "solid_objects_messages"] to be empty.

Reminders are simply where it surfaces first, because they outlive the message that created them and a suite is likely to assert on them directly. Leftover dead letters or claimed mailbox rows would have been just as capable of failing a test by order.

The fix

reset_actors! deletes each actor-owned table itself, children before parents so the order is correct whether or not the cascade fires. A test helper has no reason to depend on referential integrity.

Tests

The existing test asserted only that instances and processes were empty, which is the same blind spot one level up: it could not have caught this. Two new tests:

  • clears actor-owned rows without the database cascade populates all eight tables, resets with foreign keys switched off so the cascade cannot cover for an incomplete list, and asserts every table is empty. Verified to fail against the old body, naming all seven surviving tables.
  • every actor-owned table is in the reset list compares the helper's list against the Solid Objects tables actually present in the schema, so a table added later cannot be omitted silently. On a database that enforces the cascade the omission would otherwise stay invisible.

Validation

Run Result
bundle exec rake (SQLite) 383 runs, 1282 assertions, 0 failures, 14 skips
Trilogy (docker MySQL 8) 383 runs, 1242 assertions, 0 failures, 23 skips
PostgreSQL 17.6 383 runs, 1259 assertions, 0 failures, 15 skips
Standard, RuboCop, RBS, Steep, Brakeman clean

The foreign-key test skips on MySQL and PostgreSQL, which is the extra skip in those columns. Disabling enforcement there needs either a session-wide switch or superuser rights, and the fix is adapter independent, so the SQLite run carries it.

No version bump. The changelog entry sits under Unreleased, matching the separate release-prep workflow. Note that PR #30 is also open with an Unreleased entry, so whichever merges second will want a trivial changelog merge.

The helper deleted instances and processes and left the other seven
tables to the database cascade. That cascade is not enforced everywhere:
SQLite has to be asked for foreign keys, MySQL has to be on InnoDB, and a
host application may have stripped the constraints out of the copied
migration. Where it does not fire, rows survive a reset with an
instance_id pointing at nothing and the next test reads them as its own.

Reported as reminders leaking, and reminders are where it surfaces first
because they outlive the message that created them, but the same reset
left messages, ready and claimed mailbox rows, effects, broadcasts, and
dead letters behind too.

Deleting each table explicitly costs nothing and removes the dependency
on referential integrity. Order is children before parents so it is
correct whether or not the cascade fires.

The existing test asserted only that instances and processes were empty,
which is the same blind spot one level up. It now populates every
actor-owned table, resets with foreign keys switched off so the cascade
cannot cover for an incomplete list, and separately asserts the list
covers every Solid Objects table so a table added later cannot be
omitted silently.
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Greptile Summary

The PR makes reset_actors! explicitly clear every actor-owned table instead of relying on database cascades, preventing test data from surviving when foreign-key enforcement is unavailable.

  • Adds a children-before-parents cleanup list covering all actor-owned models.
  • Adds regression and schema-completeness tests for cleanup without cascades.
  • Updates generated RBS, development documentation, and the unreleased changelog.

Confidence Score: 5/5

The PR appears safe to merge; the explicit cleanup order matches the current foreign-key relationships and covers every actor-owned table.

The reset helper deletes dependent rows before their parents, handles process rows after all restrictive references are removed, and includes regression coverage for databases where cascades are not enforced.

Important Files Changed

Filename Overview
lib/solid_objects/test_helper.rb Replaces cascade-dependent instance cleanup with a complete, foreign-key-safe deletion sequence.
test/integration/public_test_helper_test.rb Adds coverage proving cleanup without cascades and detecting future omissions from the reset list.
sig/generated/lib/solid_objects/test_helper.rbs Updates generated signatures for the new actor-owned model list.
docs/development.md Documents why test cleanup does not rely on database foreign-key enforcement.
CHANGELOG.md Records the test-isolation fix under Unreleased.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[reset_actors!] --> B[Reset caller process]
    B --> C[Delete dead letters]
    C --> D[Delete claimed and ready messages]
    D --> E[Delete broadcasts, effects, and reminders]
    E --> F[Delete messages]
    F --> G[Delete instances]
    G --> H[Delete processes]
Loading

Reviews (1): Last reviewed commit: "fix: clear every actor-owned table in re..." | Re-trigger Greptile

@cardmagic
cardmagic merged commit 075af9a into main Aug 11, 2026
29 checks passed
@cardmagic
cardmagic deleted the agent/test-helper-isolation branch August 11, 2026 00:28
@cardmagic cardmagic mentioned this pull request Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant