This file covers worker-specific test execution. For the consolidated local dev and test guide (including all packages, integration tests, and troubleshooting), see docs/dev-test-guide.md.
go test ./internal/worker/... -v -coverExpected output:
=== RUN TestWorker_StartStop
--- PASS: TestWorker_StartStop (0.10s)
=== RUN TestWorker_ProcessPendingJob
--- PASS: TestWorker_ProcessPendingJob (0.20s)
=== RUN TestWorker_RetryOnFailure
--- PASS: TestWorker_RetryOnFailure (2.00s)
=== RUN TestWorker_DeadLetterAfterMaxAttempts
--- PASS: TestWorker_DeadLetterAfterMaxAttempts (3.00s)
=== RUN TestWorker_ConcurrentWorkers_NoDuplicateProcessing
--- PASS: TestWorker_ConcurrentWorkers_NoDuplicateProcessing (0.30s)
=== RUN TestWorker_SkipFutureJobs
--- PASS: TestWorker_SkipFutureJobs (0.20s)
=== RUN TestWorker_GracefulShutdown
--- PASS: TestWorker_GracefulShutdown (0.30s)
=== RUN TestWorker_ShutdownTimeout
--- PASS: TestWorker_ShutdownTimeout (0.20s)
=== RUN TestMemoryStore_CreateAndGet
--- PASS: TestMemoryStore_CreateAndGet (0.00s)
=== RUN TestMemoryStore_CreateWithoutID
--- PASS: TestMemoryStore_CreateWithoutID (0.00s)
=== RUN TestMemoryStore_GetNonExistent
--- PASS: TestMemoryStore_GetNonExistent (0.00s)
=== RUN TestMemoryStore_Update
--- PASS: TestMemoryStore_Update (0.00s)
=== RUN TestMemoryStore_UpdateNonExistent
--- PASS: TestMemoryStore_UpdateNonExistent (0.00s)
=== RUN TestMemoryStore_ListPending
--- PASS: TestMemoryStore_ListPending (0.00s)
=== RUN TestMemoryStore_ListPendingWithLimit
--- PASS: TestMemoryStore_ListPendingWithLimit (0.00s)
=== RUN TestMemoryStore_ListDeadLetter
--- PASS: TestMemoryStore_ListDeadLetter (0.00s)
=== RUN TestMemoryStore_AcquireLock
--- PASS: TestMemoryStore_AcquireLock (0.00s)
=== RUN TestMemoryStore_LockExpiration
--- PASS: TestMemoryStore_LockExpiration (0.15s)
=== RUN TestMemoryStore_ReleaseLock
--- PASS: TestMemoryStore_ReleaseLock (0.00s)
=== RUN TestMemoryStore_ReleaseLockNotHeld
--- PASS: TestMemoryStore_ReleaseLockNotHeld (0.00s)
=== RUN TestMemoryStore_ReleaseLockNonExistent
--- PASS: TestMemoryStore_ReleaseLockNonExistent (0.00s)
=== RUN TestBillingExecutor_ExecuteCharge
--- PASS: TestBillingExecutor_ExecuteCharge (0.10s)
=== RUN TestBillingExecutor_ExecuteInvoice
--- PASS: TestBillingExecutor_ExecuteInvoice (0.10s)
=== RUN TestBillingExecutor_ExecuteReminder
--- PASS: TestBillingExecutor_ExecuteReminder (0.10s)
=== RUN TestBillingExecutor_UnknownJobType
--- PASS: TestBillingExecutor_UnknownJobType (0.00s)
=== RUN TestBillingExecutor_ContextCancellation
--- PASS: TestBillingExecutor_ContextCancellation (0.00s)
=== RUN TestScheduler_ScheduleCharge
--- PASS: TestScheduler_ScheduleCharge (0.00s)
=== RUN TestScheduler_ScheduleInvoice
--- PASS: TestScheduler_ScheduleInvoice (0.00s)
=== RUN TestScheduler_ScheduleReminder
--- PASS: TestScheduler_ScheduleReminder (0.00s)
PASS
coverage: 96.5% of statements
ok stellarbill-backend/internal/worker 6.500s
# Worker tests
go test ./internal/worker/worker_test.go -v
# Store tests
go test ./internal/worker/store_memory_test.go -v
# Executor tests
go test ./internal/worker/executor_test.go -v
# Scheduler tests
go test ./internal/worker/scheduler_test.go -v# Generate coverage report
go test ./internal/worker/... -coverprofile=coverage.out
# View coverage in browser
go tool cover -html=coverage.out# Run tests with race detector
go test ./internal/worker/... -race -vExpected: No race conditions detected
# Run benchmarks (if added)
go test ./internal/worker/... -bench=. -benchmem- All tests pass
- Coverage >= 95%
- No race conditions
- No diagnostics/linting errors
- Code formatted with
go fmt
go fmt ./internal/worker/...
go vet ./internal/worker/...- Worker starts and stops cleanly
- Jobs execute successfully
- Retry logic works with exponential backoff
- Dead-letter queue captures persistent failures
- Concurrent workers don't duplicate processing
- Future jobs wait until scheduled time
- Graceful shutdown completes in-flight jobs
- Locks expire and are cleaned up
- Clock skew handled (past/future jobs)
- Worker restart recovers jobs
- Lock expiration allows job recovery
- Concurrent access is thread-safe
- Resource limits prevent exhaustion
- Context cancellation stops execution
- No sensitive data in logs
- Job isolation prevents interference
- Distributed locking prevents double-billing
- Error messages don't leak information
- Resource limits enforced
package main
import (
"log"
"time"
"stellarbill-backend/internal/worker"
)
func main() {
store := worker.NewMemoryStore()
executor := worker.NewBillingExecutor()
config := worker.DefaultConfig()
config.PollInterval = 2 * time.Second
w := worker.NewWorker(store, executor, config)
w.Start()
// Schedule test jobs
scheduler := worker.NewScheduler(store)
scheduler.ScheduleCharge("sub-1", time.Now(), 3, worker.PriorityHigh)
scheduler.ScheduleInvoice("sub-2", time.Now().Add(5*time.Second), 3, worker.PriorityNormal)
// Let it run
time.Sleep(30 * time.Second)
// Check metrics
metrics := w.GetMetrics()
log.Printf("Processed: %d, Succeeded: %d, Failed: %d",
metrics.JobsProcessed, metrics.JobsSucceeded, metrics.JobsFailed)
w.Stop()
}Run two instances simultaneously and verify no duplicate processing.
// Create executor that fails
type FailingExecutor struct{}
func (e *FailingExecutor) Execute(ctx context.Context, job *worker.Job) error {
return errors.New("simulated failure")
}
// Use with worker and verify retry + dead-letter behavior# Schedule 1000 jobs
for i in {1..1000}; do
# Schedule job via API or directly
done
# Monitor worker metrics
# Verify all jobs processed
# Check for memory leaks# Run 10 concurrent workers
# Schedule 10,000 jobs
# Monitor CPU, memory, database connections
# Verify no deadlocks or race conditions- Replace MemoryStore with PostgresStore
- Run migration to create jobs table
- Execute test suite
- Verify data persistence
- Test worker restart recovery
- Implement real payment gateway in executor
- Use test/sandbox credentials
- Schedule test charges
- Verify transactions created
- Test failure scenarios
name: Test Worker
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.22'
- name: Run tests
run: go test ./internal/worker/... -v -cover -race
- name: Check coverage
run: |
go test ./internal/worker/... -coverprofile=coverage.out
go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//' | awk '{if ($1 < 95) exit 1}'- Check for deadlocks in concurrent tests
- Verify context cancellation works
- Ensure timeouts are set appropriately
- Run with
-raceflag - Check for shared state without locks
- Verify job copies are immutable
- Add deterministic timing
- Use channels for synchronization
- Avoid sleep-based timing when possible
- Check for untested error paths
- Add edge case tests
- Test all job types and statuses