fix(14): WR-06 never file a second G15Office task on a job retry

A retry after a failed attachment or a failed CompleteJob created another
task for the same report. The task id is now stored as soon as the task
is created, a retry with a stored id only re-attempts the attachment, a
sent submission with a task id is skipped, and a CompleteJob failure is
logged instead of returned.
This commit is contained in:
Jakub Zych
2026-10-04 08:10:30 +02:00
parent 5a8b7f816f
commit e52b1ecd2c
4 changed files with 103 additions and 24 deletions

View File

@@ -274,17 +274,49 @@ func TestSyncG15Office(t *testing.T) {
failing(t, parityClient(), roundTrip(func(*http.Request) (*http.Response, error) { return nil, errors.New("dial failed") }), "g15office create task failed: connection error.")
})
t.Run("attach-failure", func(t *testing.T) {
calls := 0
// The task id is kept from the first attempt, so the retry only
// re-attempts the attachment and files no second task (WR-06).
tasks, attaches := 0, 0
attachStatus := 413
rt := roundTrip(func(r *http.Request) (*http.Response, error) {
calls++
if strings.HasSuffix(r.URL.Path, "/tasks") {
tasks++
return jsonAnswer(201, `{"data":{"id":"tskX"}}`)(r)
}
return jsonAnswer(413, `{"error":true,"message":"File too large"}`)(r)
attaches++
if !strings.Contains(r.URL.Path, "/tasks/tskX/") {
t.Errorf("attachment to %s", r.URL.Path)
}
if attachStatus != 201 {
return jsonAnswer(attachStatus, `{"error":true,"message":"File too large"}`)(r)
}
return jsonAnswer(201, `{"data":{"id":"att"}}`)(r)
})
failing(t, parityClient(), rt, "g15office attach file failed: File too large")
if calls != 2 {
t.Fatalf("%d requests", calls)
sub := recordedSubmission(t, app, gdb, bucket)
deps := classes.SyncDeps{DB: gdb, Bucket: bucket, Client: parityClient()}
ctx := fetchguard.WithTransport(t.Context(), rt)
const wantErr = "g15office attach file failed: File too large"
if err := classes.SyncG15Office(ctx, deps, classes.SyncG15OfficeArgs{SubmissionID: sub.ID}); err == nil || err.Error() != wantErr {
t.Fatalf("error %v, want %q", err, wantErr)
}
got := reload(t, gdb, sub.ID)
if got.Status != models.StatusFailed || got.G15OfficeError == nil || *got.G15OfficeError != wantErr || got.G15OfficeTaskID == nil || *got.G15OfficeTaskID != "tskX" {
t.Fatalf("submission %+v", got)
}
attachStatus = 201
if err := classes.SyncG15Office(ctx, deps, classes.SyncG15OfficeArgs{SubmissionID: sub.ID}); err != nil {
t.Fatal(err)
}
got = reload(t, gdb, sub.ID)
if got.Status != models.StatusSent || got.G15OfficeError != nil || got.G15OfficeTaskID == nil || *got.G15OfficeTaskID != "tskX" {
t.Fatalf("retried submission %+v", got)
}
// A sent submission with its task is never filed again.
if err := classes.SyncG15Office(ctx, deps, classes.SyncG15OfficeArgs{SubmissionID: sub.ID}); err != nil {
t.Fatal(err)
}
if tasks != 1 || attaches != 2 {
t.Fatalf("%d task and %d attachment requests", tasks, attaches)
}
})
t.Run("http-base-url-is-refused", func(t *testing.T) {