package main import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" ) // The three requests `--private-storage` has to keep apart on the wire // (issue #412). Absent must be BYTE-IDENTICAL to what an enable sent before // the flag existed: a `carlos instances enable -channel beta` that quietly // unset a tenant's storage isolation is the failure this pins. func TestInstancesEnableSendsPrivateStorageOnlyWhenGiven(t *testing.T) { var gotBody string ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { b, _ := io.ReadAll(r.Body) gotBody = string(b) json.NewEncoder(w).Encode(map[string]any{ "app": "jam", "domain": "jam.example.com", "private_storage": true, }) })) defer ts.Close() s := newTestSession(ts.URL) yes, no := true, false m, err := s.instancesEnable(context.Background(), apiAcct{Seg: "bah"}, "jam", nil, nil, &yes) if err != nil { t.Fatalf("instancesEnable: %v", err) } if !strings.Contains(gotBody, `"private_storage":true`) { t.Errorf("request body = %s, want it to ask for private storage", gotBody) } // The ECHO is what the CLI prints, not the flag that was typed: an // enable that never mentioned storage still has to report what the app // does, because that is the fact the next create acts on. if !m.PrivateStorage { t.Error("decoded manifest lost the policy the door echoed") } gotBody = "" if _, err := s.instancesEnable(context.Background(), apiAcct{Seg: "bah"}, "jam", nil, nil, &no); err != nil { t.Fatalf("instancesEnable (off): %v", err) } if !strings.Contains(gotBody, `"private_storage":false`) { t.Errorf("request body = %s, want an explicit false", gotBody) } gotBody = "" if _, err := s.instancesEnable(context.Background(), apiAcct{Seg: "bah"}, "jam", nil, nil, nil); err != nil { t.Fatalf("instancesEnable (absent): %v", err) } if strings.Contains(gotBody, "private_storage") { t.Errorf("request body = %s, want no key at all when the flag was not given", gotBody) } } // The create response's bucket is decoded, because the line it prints is the // only place a member ever learns the name of the bucket their signup path // just minted. func TestInstanceCreateDecodesThePrivateStoreBucket(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(map[string]any{ "host": "acme.jam.example.com", "channel": "stable", "environment": "acme", "private_store_bucket": "carlos-bah-jam-acme", }) })) defer ts.Close() s := newTestSession(ts.URL) rec, err := s.instanceCreate(context.Background(), apiAcct{Seg: "bah"}, "jam", "acme.jam.example.com", "", "", "", "", nil, nil, nil) if err != nil { t.Fatalf("instanceCreate: %v", err) } if rec.PrivateStoreBucket != "carlos-bah-jam-acme" { t.Errorf("decoded bucket = %q, want carlos-bah-jam-acme", rec.PrivateStoreBucket) } }