| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/json" |
| 6 | "io" |
| 7 | "net/http" |
| 8 | "net/http/httptest" |
| 9 | "strings" |
| 10 | "testing" |
| 11 | ) |
| 12 | |
| 13 | // The three requests `--private-storage` has to keep apart on the wire |
| 14 | // (issue #412). Absent must be BYTE-IDENTICAL to what an enable sent before |
| 15 | // the flag existed: a `carlos instances enable -channel beta` that quietly |
| 16 | // unset a tenant's storage isolation is the failure this pins. |
| 17 | func TestInstancesEnableSendsPrivateStorageOnlyWhenGiven(t *testing.T) { |
| 18 | var gotBody string |
| 19 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 20 | b, _ := io.ReadAll(r.Body) |
| 21 | gotBody = string(b) |
| 22 | json.NewEncoder(w).Encode(map[string]any{ |
| 23 | "app": "jam", "domain": "jam.example.com", "private_storage": true, |
| 24 | }) |
| 25 | })) |
| 26 | defer ts.Close() |
| 27 | s := newTestSession(ts.URL) |
| 28 | yes, no := true, false |
| 29 | |
| 30 | m, err := s.instancesEnable(context.Background(), apiAcct{Seg: "bah"}, "jam", nil, nil, &yes) |
| 31 | if err != nil { |
| 32 | t.Fatalf("instancesEnable: %v", err) |
| 33 | } |
| 34 | if !strings.Contains(gotBody, `"private_storage":true`) { |
| 35 | t.Errorf("request body = %s, want it to ask for private storage", gotBody) |
| 36 | } |
| 37 | // The ECHO is what the CLI prints, not the flag that was typed: an |
| 38 | // enable that never mentioned storage still has to report what the app |
| 39 | // does, because that is the fact the next create acts on. |
| 40 | if !m.PrivateStorage { |
| 41 | t.Error("decoded manifest lost the policy the door echoed") |
| 42 | } |
| 43 | |
| 44 | gotBody = "" |
| 45 | if _, err := s.instancesEnable(context.Background(), apiAcct{Seg: "bah"}, "jam", nil, nil, &no); err != nil { |
| 46 | t.Fatalf("instancesEnable (off): %v", err) |
| 47 | } |
| 48 | if !strings.Contains(gotBody, `"private_storage":false`) { |
| 49 | t.Errorf("request body = %s, want an explicit false", gotBody) |
| 50 | } |
| 51 | |
| 52 | gotBody = "" |
| 53 | if _, err := s.instancesEnable(context.Background(), apiAcct{Seg: "bah"}, "jam", nil, nil, nil); err != nil { |
| 54 | t.Fatalf("instancesEnable (absent): %v", err) |
| 55 | } |
| 56 | if strings.Contains(gotBody, "private_storage") { |
| 57 | t.Errorf("request body = %s, want no key at all when the flag was not given", gotBody) |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | // The create response's bucket is decoded, because the line it prints is the |
| 62 | // only place a member ever learns the name of the bucket their signup path |
| 63 | // just minted. |
| 64 | func TestInstanceCreateDecodesThePrivateStoreBucket(t *testing.T) { |
| 65 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 66 | w.WriteHeader(http.StatusCreated) |
| 67 | json.NewEncoder(w).Encode(map[string]any{ |
| 68 | "host": "acme.jam.example.com", "channel": "stable", |
| 69 | "environment": "acme", "private_store_bucket": "carlos-bah-jam-acme", |
| 70 | }) |
| 71 | })) |
| 72 | defer ts.Close() |
| 73 | s := newTestSession(ts.URL) |
| 74 | |
| 75 | rec, err := s.instanceCreate(context.Background(), apiAcct{Seg: "bah"}, "jam", |
| 76 | "acme.jam.example.com", "", "", "", "", nil, nil, nil) |
| 77 | if err != nil { |
| 78 | t.Fatalf("instanceCreate: %v", err) |
| 79 | } |
| 80 | if rec.PrivateStoreBucket != "carlos-bah-jam-acme" { |
| 81 | t.Errorf("decoded bucket = %q, want carlos-bah-jam-acme", rec.PrivateStoreBucket) |
| 82 | } |
| 83 | } |
| 84 | |