| 1 | package suite |
| 2 | |
| 3 | import ( |
| 4 | "errors" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | // S3 is the ingest call: (id, revision), no person, team-visible only. |
| 9 | func TestS3ReturnsBody(t *testing.T) { |
| 10 | got, err := DecodeS3(200, []byte("the body")) |
| 11 | if err != nil { |
| 12 | t.Fatalf("unexpected error: %v", err) |
| 13 | } |
| 14 | |
| 15 | if string(got) != "the body" { |
| 16 | t.Errorf("got %q, want %q", got, "the body") |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | // 404 is the whole vocabulary of refusal. Deleted, never team-visible, or |
| 21 | // departed from team-visibility all answer the same way, so a consumer cannot |
| 22 | // learn from a refusal that the item exists (§4.2). |
| 23 | func TestS3NotFoundIsIndistinguishable(t *testing.T) { |
| 24 | _, err := DecodeS3(404, nil) |
| 25 | |
| 26 | if !errors.Is(err, ErrNotAvailable) { |
| 27 | t.Errorf("got %v, want ErrNotAvailable", err) |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | // A 403 breaks the contract, and the client says so rather than folding it in |
| 32 | // with 404. "404, never 403" is the rule (§4.2, §6) because 403 means "this |
| 33 | // exists and you may not have it" — which is precisely the fact custody hides. |
| 34 | // A sibling answering 403 has a bug that leaks, and quietly treating it as a |
| 35 | // refusal would keep that bug invisible. |
| 36 | func TestS3ForbiddenIsAContractBreach(t *testing.T) { |
| 37 | _, err := DecodeS3(403, nil) |
| 38 | |
| 39 | // Positively, not just "some error": mutating this branch to a plain |
| 40 | // fmt.Errorf passed the suite, which lost exactly the breach-versus-outage |
| 41 | // distinction the sentinel exists to carry. |
| 42 | if !errors.Is(err, ErrContractBreach) { |
| 43 | t.Errorf("got %v, want ErrContractBreach", err) |
| 44 | } |
| 45 | if errors.Is(err, ErrNotAvailable) { |
| 46 | t.Error("403 was folded into ErrNotAvailable; it must be distinguishable") |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | // Any other non-2xx is a failed call, not a refusal. |
| 51 | func TestS3ServerErrorIsNotARefusal(t *testing.T) { |
| 52 | _, err := DecodeS3(503, nil) |
| 53 | |
| 54 | if err == nil { |
| 55 | t.Fatal("got nil error for a 503, want a failure") |
| 56 | } |
| 57 | if errors.Is(err, ErrNotAvailable) { |
| 58 | t.Error("503 was reported as a refusal; an outage is not an answer") |
| 59 | } |
| 60 | if errors.Is(err, ErrContractBreach) { |
| 61 | t.Error("503 was reported as a contract breach; a sibling being down is not a sibling misbehaving") |
| 62 | } |
| 63 | } |
| 64 | |