Problem/Motivation

no test coverage.

Proposed resolution

Test Layer: Unit Tests

Unit tests mock all dependencies (database, config, logger, etc.) and test pure logic.

1. DeadLetterDatabaseQueueTest (the core of the module)

- claimItem() increments tries counter on each claim
- claimItem() returns item normally when tries < max_tries
- claimItem() triggers dead letter handling when tries >= max_tries
- claimItem() calls handleDeadLetter() on workers implementing DeadLetterQueueWorkerInterface
- claimItem() does NOT call handleDeadLetter() on workers that don't implement the interface
- claimItem() deletes item when handleDeadLetter() throws DiscardDeadLetterException
- claimItem() resets tries to 0 when handleDeadLetter() throws RestoreDeadLetterException
- claimItem() logs dead letter events
- numberOfItems() excludes items where tries >= max_tries
- releaseItem() decrements the tries counter
- resetItemTries() sets tries back to 0 for a given item ID
- getMaxTries() returns queue-specific config value when set
- getMaxTries() falls back to worker annotation cron['max_tries'] when no config
- getMaxTries() defaults to 10 when neither config nor annotation exists
- schemaDefinition() includes the 'tries' field

2. DeadLetterQueueDatabaseFactoryTest

- get() returns a DeadLetterDatabaseQueue instance
- Dependencies (database, worker manager, config, logger) are passed through correctly

3. UniqueDeadLetterDatabaseQueueTest

- doCreateItem() inserts items with SHA512 hash
- doCreateItem() returns FALSE on duplicate items (hash collision)
- hash() generates consistent hashes for the same input
- hash() generates different hashes for different queue names with same data
- schemaDefinition() includes hash field and unique key constraint
- Dead letter behavior inherited from parent class works correctly

4. UniqueDeadLetterQueueDatabaseFactoryTest

- get() returns a UniqueDeadLetterDatabaseQueue instance

5. Exception tests (lightweight, may not be worth separate test classes)

- DiscardDeadLetterException extends RuntimeException
- RestoreDeadLetterException extends RuntimeException

Test Layer: Kernel Tests

Kernel tests boot a real Drupal kernel with a test database. These are the most valuable tests for this module since the core logic is database
interaction.

6. DeadLetterDatabaseQueueKernelTest

This is the most important test class. It exercises the real queue table with the tries column.

- Create an item, claim it, verify tries = 1
- Claim and release repeatedly, verify tries increments and decrements correctly
- After max_tries claims, item is no longer returned by claimItem()
- numberOfItems() returns correct count excluding dead letters
- resetItemTries() makes a dead letter claimable again
- Full lifecycle: create -> claim repeatedly -> dead letter -> reset -> claim again
- Multiple items: only the item exceeding max_tries becomes a dead letter, others still process
- Verify the install hook adds the tries column correctly (via module install during test setup)

7. DeadLetterQueueConfigKernelTest

- Per-queue max_tries from config overrides annotation default
- Changing config at runtime affects getMaxTries() return value
- Config schema validation (queues array structure)

8. UniqueDeadLetterDatabaseQueueKernelTest

- Inserting duplicate data returns FALSE
- Inserting same data to different queue names succeeds (different hashes)
- Dead letter behavior works end-to-end with unique queue table
- Verify unique queue install hook adds tries column to queue_unique table

Test Layer: UI Submodule (Kernel + Unit)

9. DeadLetterDatabaseQueueUiTest (Kernel)

- getItems() excludes dead letters
- getDeadLetters() returns only items where tries >= maxTries
- getNumberOfDeadLetters() returns correct count
- getOperations() includes "Reset tries" operation
- loadItem() returns item with tries field

10. DeadLettersFormTest (Unit or Kernel)

- Form builds table with correct columns (ID, expires, created, tries, operations)
- Pagination works (test with >25 items)
- Operations dropbutton includes View, Release, Delete, Reset tries

11. ConfirmItemResetTriesFormTest (Kernel)

- Validates queue implements DeadLetterQueueInterface
- Submission calls resetItemTries() and shows success message
- Redirects to queue inspection view after submission

12. Overview form alter tests (Kernel)

- Dead letter count column added to overview
- Max tries input field present and editable
- Submit handler saves max_tries to config correctly

Priority Order

If we're doing this incrementally:

1. DeadLetterDatabaseQueueKernelTest — highest value, tests the core claim/tries/dead-letter lifecycle against a real database
2. DeadLetterDatabaseQueueTest (unit) — covers the branching logic in claimItem() (exception handling, worker interface checks)
3. UniqueDeadLetterDatabaseQueueKernelTest — covers the unique variant
4. DeadLetterQueueConfigKernelTest — covers config resolution
5. UI tests — lower priority, more surface area but less critical logic

Estimated Scope

Roughly 12 test classes, ~60-70 test methods. The kernel tests for the core queue class and the unit tests for claimItem() branching logic cover the vast
majority of risk. The UI tests are nice-to-have but the module's value is in the queue behavior, not the admin screens.

Remaining tasks

write the tests and make an MR.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

benstallings created an issue. See original summary.

benstallings’s picture

Assigned: benstallings » Unassigned
Status: Active » Needs review

dieterholvoet’s picture

Status: Needs review » Fixed

Not an expert on tests so I'll trust you on this, thanks for the effort!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.