Skip to content

oncetask: add Firestore integration tests and fix lifecycle bugs - #17

Open
anish749 wants to merge 2 commits into
mainfrom
worktree-firestore-integration-tests
Open

oncetask: add Firestore integration tests and fix lifecycle bugs#17
anish749 wants to merge 2 commits into
mainfrom
worktree-firestore-integration-tests

Conversation

@anish749

@anish749 anish749 commented May 9, 2026

Copy link
Copy Markdown
Owner

Problem

The library shipped with zero integration tests — every existing
*_test.go exercised in-memory logic only. Real Firestore behaviour
(lease acquisition, COUNT aggregation, BulkWriter, env isolation,
recurrence spawning, cancellation handlers, retry timing, etc.) was
untested end-to-end.

Writing those tests also surfaced three real bugs that affected
production semantics, not just test ergonomics — so they're fixed here
too.

Solution

Integration tests

  • 38 new integration tests under build tag integration, gated
    separately from unit tests. Run via go test -tags=integration ./....
  • Modeled on the testcontainers-go pattern from a sibling repo
    (shopkeeper): emulator boots once via testcontainers/modules/gcloud
    (cloud-sdk:506.0.0-emulators — needed for the multi-field
    inequality queries in firestore_queries.go), shared across all
    tests. Each test gets its own ONCE_TASK_ENV so data is isolated
    on the shared collection.
  • Table-driven where the assertions repeat (retry policies, RRule
    variants, cancellation entry points, delete operations).
  • Coverage: every Manager method, every HandlerOption, all three
    execution strategies (Concurrent, OnePerResourceKey, AllPerResourceKey),
    exponential / fixed / no-retry / custom policies, scheduled tasks,
    recurrence (DAILY / WEEKLY / COUNT / ExDate / MONTHLY / SECONDLY),
    cancellation with and without cleanup handler, reset, delete, env
    isolation (reads + handler claims + write rejection), panic recovery,
    and context helpers.

CI

New integration job in .github/workflows/ci.yml runs the full
suite with -race on Ubuntu (testcontainers uses the runner's Docker).

Bugs fixed in the same PR

  1. Cleanup didn't wait for worker goroutines.
    NewFirestoreOnceTaskManager returned a cleanup that just cancelled
    the context. Cancelling mid-transaction left the emulator holding
    row/index locks for several seconds; the next test's CreateTask
    tripped Transaction lock timeout. Fix: introduce a stop channel
    checked between iterations (so in-flight transactions complete and
    release locks naturally) plus a sync.WaitGroup that cleanup
    blocks on. Cancelling the user-provided ctx is still the abrupt
    path.

  2. CancelTasksByIds didn't notify workers.
    Cancelled tasks sat until the next 1-minute polling tick before the
    cancellation handler could run. Fix: track the affected task types
    and call evaluateNow for each.

  3. Retried tasks waited up to 60s for the next tick.
    Even with a 100 ms retry delay, the worker would sleep on its
    minute-long ticker after scheduling the retry. Fix: completeBatch
    now computes the earliest retry waitUntil and schedules a wakeup at
    that time via a goroutine that signals evaluateChan.

Context

All 38 integration tests pass under -race in ~30s on a fresh
emulator. Existing unit tests still pass. golangci-lint is clean
both with and without -tags=integration.

anish749 added 2 commits May 9, 2026 15:01
Add an integration test suite (build tag "integration", testcontainers-go
+ Firestore emulator) covering every public Manager method, handler
strategy, retry policy, recurrence variant, cancellation path, env
isolation guarantee, panic recovery, and context helper.

CI gains a separate job that runs `go test -tags=integration -race`
against the emulator started inside the test process.

Bugs found and fixed in the same change:

- cleanup function returned by NewFirestoreOnceTaskManager did not wait
  for worker goroutines to exit; tests racing the next manager would
  see "Transaction lock timeout" against the emulator. Cleanup now
  signals via a dedicated stop channel (so in-flight transactions
  finish naturally, releasing locks) and blocks on a WaitGroup.

- CancelTasksByIds did not signal the affected task type; cancelled
  tasks waited up to 60s for the next polling tick. Now it calls
  evaluateNow per affected type.

- Retried tasks waited up to 60s for the polling tick even when the
  retry delay was milliseconds. completeBatch now schedules a wakeup
  at the earliest retry waitUntil.
…orcing exit

Building on #18, cleanup() now signals workers via a dedicated stop
channel and waits up to 5s for them to exit between iterations
(letting any in-flight Firestore RPC complete naturally) before
falling back to context cancellation.

Cancelling a transaction mid-flight leaves the Firestore emulator
holding row/index locks for several seconds while it processes the
abort. With the integration test suite running tests back-to-back
against a shared emulator, the next test reliably tripped
"Transaction lock timeout" on its first CreateTask. The graceful path
sidesteps this; the 5s fallback ensures unreachable endpoints (used
by the existing cleanup_test.go) still terminate promptly.
@anish749
anish749 force-pushed the worktree-firestore-integration-tests branch from 94231dd to a680c7c Compare May 9, 2026 13:25
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