gates / gates (push) Successful in 44s
The hub holds the new package -> success; another package -> 502 'previous code still works' (now true); cannot be asked -> 504, no promise. Tests TestR1254_* red before the fix. Commit only; the agent release needs the vouch.
99 lines
4.5 KiB
Go
99 lines
4.5 KiB
Go
package localapi
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// R-1254 (the ceremony half, review round 2 HUNT-3 H2): the upload's answer can be lost or late (30 s) AFTER the hub
|
|
// stored the new package. The household was then told „the previous code still works" — false: the hub now holds the
|
|
// package the NEW code opens. The agent asks the hub which package it holds before it words the result:
|
|
// - the hub holds the new package → success (the code is handed out / the confirm completes);
|
|
// - the hub holds another package → the old sentence, now true;
|
|
// - the hub cannot be asked → 504 and a sentence that does NOT promise the previous code; the wait goes on.
|
|
|
|
// RED-PROOF: remove the hub question from the claim's failure branch → 502 „the previous code still works" while the
|
|
// hub holds the new package → FAILS.
|
|
func TestR1254_ALostAnswerAtTheClaimIsASuccessWhenTheHubHoldsTheNewPackage(t *testing.T) {
|
|
hub := &fakeEscrowHub{lost: true}
|
|
srv, _ := newEscrowTestServerHub(t, sequenceRunner(firstR), hub)
|
|
h := srv.Handler()
|
|
startAndWait(t, srv, h)
|
|
code, body := claimCode(t, h)
|
|
if !hub.opensWith(firstR) {
|
|
t.Fatal("fixture: the hub must hold the new package (the answer was lost after the store)")
|
|
}
|
|
if code != http.StatusOK || !strings.Contains(body, firstR) {
|
|
t.Fatalf("claim after a lost answer, the hub holding the new package: %d %s — want 200 with the code", code, body)
|
|
}
|
|
if strings.Contains(body, "previous code still works") {
|
|
t.Fatalf("the household was told the previous code still works: %s", body)
|
|
}
|
|
if st := do(t, h, "GET", "/escrow/ceremony/status", "A", "").Body.String(); !strings.Contains(st, `"uploaded":true`) {
|
|
t.Fatalf("the job must read uploaded: %s", st)
|
|
}
|
|
}
|
|
|
|
// The hub cannot be asked: no promise about the previous code, 504, the code stays claimable (a retry decides).
|
|
// RED-PROOF: as above → 502 with „the previous code still works" → FAILS.
|
|
func TestR1254_AnUnknownOutcomeAtTheClaimPromisesNothing(t *testing.T) {
|
|
hub := &fakeEscrowHub{lost: true, holdsErr: errors.New("hub unreachable")}
|
|
srv, _ := newEscrowTestServerHub(t, sequenceRunner(firstR), hub)
|
|
h := srv.Handler()
|
|
startAndWait(t, srv, h)
|
|
code, body := claimCode(t, h)
|
|
if code != http.StatusGatewayTimeout {
|
|
t.Fatalf("claim with an unknown outcome: %d %s — want 504", code, body)
|
|
}
|
|
if strings.Contains(body, "previous code still works") {
|
|
t.Fatalf("an unknown outcome promised the previous code: %s", body)
|
|
}
|
|
assertNoRIn(t, "unknown-outcome claim payload", body, firstR)
|
|
if st := do(t, h, "GET", "/escrow/ceremony/status", "A", "").Body.String(); !strings.Contains(st, `"claimable":true`) {
|
|
t.Fatalf("the code must stay claimable: %s", st)
|
|
}
|
|
// The hub answers again: the retry finds the new package already stored and hands the code out.
|
|
hub.mu.Lock()
|
|
hub.holdsErr = nil
|
|
hub.mu.Unlock()
|
|
if code, body := claimCode(t, h); code != http.StatusOK || !strings.Contains(body, firstR) {
|
|
t.Fatalf("retry: %d %s", code, body)
|
|
}
|
|
}
|
|
|
|
// The hub holds ANOTHER package (the upload really failed): the old sentence stays, and is now true.
|
|
func TestR1254_AFailedUploadTheHubDidNotStoreKeepsTheOldSentence(t *testing.T) {
|
|
hub := &fakeEscrowHub{err: errors.New("hub returned HTTP 502")}
|
|
srv, _ := newEscrowTestServerHub(t, sequenceRunner(firstR), hub)
|
|
h := srv.Handler()
|
|
startAndWait(t, srv, h)
|
|
if code, body := claimCode(t, h); code != http.StatusBadGateway || !strings.Contains(body, "previous code still works") {
|
|
t.Fatalf("a failed upload: %d %s — want 502 and the previous code named", code, body)
|
|
}
|
|
}
|
|
|
|
// The same at the typed-back confirm (R-1151).
|
|
// RED-PROOF: remove the hub question from the confirm's failure branch → 502 → FAILS.
|
|
func TestR1254_ALostAnswerAtTheConfirmIsASuccessWhenTheHubHoldsTheNewPackage(t *testing.T) {
|
|
hub := &fakeEscrowHub{lost: true}
|
|
srv, _ := newEscrowTestServerHub(t, sequenceRunner(secondR), hub)
|
|
h := srv.Handler()
|
|
startAndWait(t, srv, h)
|
|
c := claimDeferred(t, h)
|
|
if code := confirm(t, h, c.typedBack(t)); code != http.StatusOK || !hub.opensWith(secondR) {
|
|
t.Fatalf("confirm after a lost answer: %d (opens: %v) — want 200", code, hub.opensWith(secondR))
|
|
}
|
|
hub.mu.Lock()
|
|
hub.lost, hub.holdsErr = true, errors.New("hub unreachable")
|
|
hub.mu.Unlock()
|
|
srv2, _ := newEscrowTestServerHub(t, sequenceRunner(firstR), hub)
|
|
h2 := srv2.Handler()
|
|
startAndWait(t, srv2, h2)
|
|
c2 := claimDeferred(t, h2)
|
|
if code := confirm(t, h2, c2.typedBack(t)); code != http.StatusGatewayTimeout {
|
|
t.Fatalf("confirm with an unknown outcome: %d — want 504", code)
|
|
}
|
|
}
|