diplomail (Stage F): docs + edge-case tests + LibreTranslate recipe
Closes the documentation gaps from the freshly-audited diplomail implementation. FUNCTIONAL.md gains a §11 "Diplomatic mail" with the full user-facing story across all five stages, mirrored into FUNCTIONAL_ru.md as the project conventions require. A new backend/docs/diplomail-translator-setup.md captures the LibreTranslate operational recipe (Docker image, env wiring, manual smoke test, troubleshooting). The package README gains a "Multi-instance posture" note documenting the deliberate absence of FOR UPDATE in the worker pickup query — single-instance is safe today; multi-instance scaling will revisit the claim mechanism. Two small edge-case tests round things out: malformed LibreTranslate response bodies (single string, short array, empty array, missing field) must surface as errors so the worker falls back instead of crashing; and an empty translation queue must produce zero events on three consecutive Worker.Tick calls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -808,6 +808,36 @@ func TestDiplomailLifecycleMembershipKick(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestDiplomailWorkerTickOnEmptyQueueIsNoop confirms the async
|
||||
// worker tolerates an empty pending queue: no error, no panic, no
|
||||
// publisher events. Belt-and-suspenders for the case where backend
|
||||
// starts, mounts the worker as an `app.Component`, and ticks before
|
||||
// any user has sent mail.
|
||||
func TestDiplomailWorkerTickOnEmptyQueueIsNoop(t *testing.T) {
|
||||
db := startPostgres(t)
|
||||
ctx := context.Background()
|
||||
|
||||
publisher := &recordingPublisher{}
|
||||
svc := diplomail.NewService(diplomail.Deps{
|
||||
Store: diplomail.NewStore(db),
|
||||
Memberships: &staticMembershipLookup{},
|
||||
Notification: publisher,
|
||||
Config: config.DiplomailConfig{
|
||||
MaxBodyBytes: 4096,
|
||||
MaxSubjectBytes: 256,
|
||||
},
|
||||
})
|
||||
worker := diplomail.NewWorker(svc)
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := worker.Tick(ctx); err != nil {
|
||||
t.Fatalf("tick %d on empty queue: %v", i, err)
|
||||
}
|
||||
}
|
||||
if got := publisher.snapshot(); len(got) != 0 {
|
||||
t.Fatalf("publisher fired %d events on empty queue", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
// TestDiplomailAsyncTranslationDelivery covers the Stage E flow:
|
||||
// 1. SendPersonal where recipient.preferred_language != body_lang
|
||||
// materialises a recipient with `AvailableAt == nil`; the inbox
|
||||
|
||||
Reference in New Issue
Block a user