Test coverage for the counter #54
Labels
No labels
bug
duplicate
enhancement
good first issue
help wanted
question
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lvl0/incr#54
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Restores the safety net.
MilestoneTest.phpis the repo's only test and #49 deletes it, so the suite is empty from that point until this lands.Why this is its own ticket
Ideally this comes first, TDD-style. It can't: the thing under test (
trackers.count,POST /increment) does not exist until #50, and the component under test changes shape again at #52.The pragmatic sequencing is to accept a temporary gap and verify #47–#51 manually via the UI, then land this immediately after #50 so #52 has real regression cover for the stack swap. That is the point where tests earn the most — a Livewire rewrite with no tests is the risky move in this milestone.
Scope
Feature tests (post-#50, pre-#52):
POST /incrementincrements by exactly 1PATCH /countsets an absolute valuePATCH /countrejects negatives, non-integers, missing valuesLivewire tests (post-#52):
Livewire::test(Counter::class)->call('increment')updates the rendered valueAlso: delete
tests/Unit/ExampleTest.php(framework placeholder).Acceptance criteria
Concrete test cases (from the #50 review)
The
code-reviewerpass on #50 produced a specific list. Recording it here so this ticket has a checklist rather than a sketch.Migration backfill (
2026_08_15_000002)countstays at the default 0entriesnever existed (Schema::hasTable('entries')false) → migration does not errorMigration
down()up()thendown()→entriesexists again with correct columns and nullabilitycount = 0→ produces no entry rowcount > 0→ produces exactly one row withquantity = countPOST /incrementPATCH /count"abc",1.5) → 422countkey → 422unsignedIntegerceiling (4294967295) → 422, not a DB exception. (Amax:4294967295rule was added during #50; this test guards it.)Route contract — high value
POST /incrementandPATCH /countreturnContent-Type: application/json, not an Inertia redirectThis last one is the regression test that matters most. The bug it guards against —
EntryController::storereturningback(), sofetch()followed the redirect and reported success on validation failure — is exactly what #50 removed.Model
Tracker::countcasts toint, not string, after a freshfind()Done —
3a28988From one placeholder test to 25 tests, 62 assertions.
tests/Feature/CounterTest.php— 13 tests: increment adds exactly 1, increments accumulate, 404 without a tracker, absolute set, set to zero,#[DataProvider]over invalid values (negative, non-numeric, fractional, above the unsigned ceiling), missing value, JSON-not-redirect contract, integer casttests/Feature/TrackerTest.php— 5 tests: fresh install reports no tracker, tracker returned once created, creation without label/unit applies defaults, a new counter starts at 0, second creation 409s without writing a rowtests/Feature/CountBackfillMigrationTest.php— 7 tests against the #50 migrationdatabase/factories/TrackerFactory.php— new;TrackergainedHasFactoryExampleTest.php; addedtests/Unit/.gitkeep(samephpunit.xmldirectory trap as #49)These tests were verified to fail
A green suite proves nothing on its own, so both critical paths were mutation-checked:
increment('count', 2)count => 7vs6)(int) floor($total)formax(0, (int) round($total))2vs3; clamping surfacedSQLSTATE[22003] Out of rangeThe second is worth recording: without the clamp a negative ledger total crashes the migration on the unsigned column rather than storing a wrong number.
Test isolation — verified, not assumed
Review raised a critical concern: MySQL DDL implicitly commits, which can break
RefreshDatabase's transaction and let migration tests pass on leaked state.Checked with a temporary probe ordered to run last, after all DDL. It confirmed
trackers.countstill present,entriesabsent, zero leaked rows. Isolation holds. The mechanism is real, but Laravel re-migrates when the transaction is compromised — the cost is speed (~29s of the 35s runtime), not correctness. Probe removed afterwards.From review
Applied: comment explaining the repeated
requireof an anonymous-class migration; note on why the twodown()tests skiprevertToLedger();TrackerFactorydefaultsuser_idtoUser::default()->idso an omitted override cannot silently create a second user with a second tracker.Declined: a concurrent-increment race test (single-user app, and
increment()is atomicSET count = count + 1), and reworking the cast test.Confirmed deliberate:
PATCH /trackeris uncovered because #48/#53 delete that code.Known coupling
Every test resolves fixtures through
User::default(), mirroring how the controllers work. #53 will require touching all of these — expected, not accidental.Livewire tests remain outstanding and belong to #52.