package console import ( "context" "encoding/json" "net/http" "testing" "github.com/carlosframework/platform/internal/release" ) func metaPartSwap(t *testing.T, version, swap string) shipPart { t.Helper() raw, err := json.Marshal(map[string]string{"version": version, "kind": "binary", "swap": swap}) if err != nil { t.Fatal(err) } return shipPart{name: "meta", body: raw} } func TestAPIShipStoresTheSwapDemand(t *testing.T) { fx := newConsoleFixture(t) liftShipBudget(fx) token := cliToken(t, fx, "paul@keymail.dev", "code-ship-swap") u := fx.ts.URL + "/api/cli/apps/" + fx.sqid + "/kass/releases" art := shipPart{name: "linux-arm64", body: randomBytes(t, 64)} code, out := postShip(t, u, token, []shipPart{metaPartSwap(t, "v9", "hold"), art}) if code != http.StatusCreated || out["swap"] != "hold" { t.Fatalf("ship = %d %v, want 201 echoing swap hold", code, out) } m, err := release.GetManifest(context.Background(), fx.rel, fx.sqid, "kass", "v9") if err != nil || m.Swap != release.DemandHold { t.Fatalf("stored manifest swap = %q, %v", m.Swap, err) } if code, out = postShip(t, u, token, []shipPart{metaPartSwap(t, "v9", "hold"), art}); code != http.StatusOK || out["swap"] != "hold" { t.Fatalf("identical re-ship = %d %v", code, out) } if code, out = postShip(t, u, token, []shipPart{metaPart(t, "v9", "binary"), art}); code != http.StatusConflict { t.Fatalf("re-ship without the demand = %d %v, want 409", code, out) } } func TestAPIShipRefusesAnyOtherDemand(t *testing.T) { fx := newConsoleFixture(t) liftShipBudget(fx) token := cliToken(t, fx, "paul@keymail.dev", "code-ship-swap-bad") u := fx.ts.URL + "/api/cli/apps/" + fx.sqid + "/kass/releases" for _, bad := range []string{"overlap", "fast", "HOLD"} { code, out := postShip(t, u, token, []shipPart{metaPartSwap(t, "v10", bad), {name: "linux-arm64", body: randomBytes(t, 8)}}) if code != http.StatusBadRequest { t.Errorf("swap %q = %d %v, want 400", bad, code, out) } } if _, err := release.GetManifest(context.Background(), fx.rel, fx.sqid, "kass", "v10"); err == nil { t.Fatal("a refused ship stored a manifest") } } func TestAPIShipWithoutSwapAnswersAsBefore(t *testing.T) { fx := newConsoleFixture(t) liftShipBudget(fx) token := cliToken(t, fx, "paul@keymail.dev", "code-ship-noswap") u := fx.ts.URL + "/api/cli/apps/" + fx.sqid + "/kass/releases" code, out := postShip(t, u, token, []shipPart{metaPart(t, "v11", "binary"), {name: "linux-arm64", body: randomBytes(t, 8)}}) if code != http.StatusCreated { t.Fatalf("ship = %d %v", code, out) } if _, present := out["swap"]; present { t.Fatalf("a ship with no demand echoes swap: %v", out) } } func TestAPIShipRefusesAddingTheDemandOnAReship(t *testing.T) { fx := newConsoleFixture(t) liftShipBudget(fx) token := cliToken(t, fx, "paul@keymail.dev", "code-ship-swap-add") u := fx.ts.URL + "/api/cli/apps/" + fx.sqid + "/kass/releases" art := shipPart{name: "linux-arm64", body: randomBytes(t, 64)} if code, out := postShip(t, u, token, []shipPart{metaPart(t, "v12", "binary"), art}); code != http.StatusCreated { t.Fatalf("ship = %d %v", code, out) } code, out := postShip(t, u, token, []shipPart{metaPart(t, "v12", "binary"), art}) if code != http.StatusOK { t.Fatalf("identical no-demand re-ship = %d %v, want 200", code, out) } if _, present := out["swap"]; present { t.Fatalf("a re-ship with no demand echoes swap: %v", out) } if code, out = postShip(t, u, token, []shipPart{metaPartSwap(t, "v12", "hold"), art}); code != http.StatusConflict { t.Fatalf("re-ship adding the demand = %d %v, want 409", code, out) } m, err := release.GetManifest(context.Background(), fx.rel, fx.sqid, "kass", "v12") if err != nil || m.Swap != "" { t.Fatalf("stored manifest swap = %q, %v, want it unchanged", m.Swap, err) } }