Add exact-image compatibility policy, experimental admission, paginated build history and shared reauthentication checks. Preserve uncertain Docker creation records, bound frontend resource waits and renew runtime sessions proactively. Lazy-load and split Monaco, add regression and opt-in live Docker fault tests, and record full race and real-browser acceptance results.
167 lines
5.4 KiB
Go
167 lines
5.4 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"git.misaka.ren/m1saka/dsh-web/internal/store"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestIntentsReserveCancel(t *testing.T) {
|
|
a, sid := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) { t.Error("reservation contacted Docker") })
|
|
if err := a.initAuth(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for n := 0; n < 3; n++ {
|
|
w := httptest.NewRecorder()
|
|
a.intents(w, runtimeRequest("POST", "/api/intents", `{"name":"one","version":"0.2.0-rc.1"}`, sid))
|
|
want := 202
|
|
if n == 2 {
|
|
want = 409
|
|
}
|
|
if w.Code != want {
|
|
t.Fatalf("%d: %d %s", n, w.Code, w.Body.String())
|
|
}
|
|
}
|
|
list, err := a.s.Intents("owner")
|
|
if err != nil || len(list) != 2 {
|
|
t.Fatalf("%v %v", list, err)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
a.intent(w, runtimeRequest("DELETE", "/api/intents/"+list[0].ID, "", sid))
|
|
if w.Code != 200 {
|
|
t.Fatal(w.Code)
|
|
}
|
|
if err := a.s.ReserveIntent(store.Intent{ID: store.ID(), UID: "owner"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestIntentBuildSuccessUsesRealRuntime(t *testing.T) {
|
|
if os.Geteuid() != 0 {
|
|
t.Skip("runtime filesystem ownership requires root")
|
|
}
|
|
image := "sha256:" + strings.Repeat("a", 64)
|
|
creates := 0
|
|
a, _ := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case strings.Contains(r.URL.Path, "/images/"):
|
|
writeJSON(w, map[string]any{"Id": image, "Os": "linux", "Architecture": "amd64", "Config": map[string]any{"Labels": map[string]string{"dsh-mgr.role": "runtime", "dsh-mgr.version": "0.2.0-rc.1", "dsh-mgr.recipe": imageRecipeRev}}})
|
|
case strings.HasSuffix(r.URL.Path, "/containers/create"):
|
|
creates++
|
|
writeJSON(w, map[string]string{"Id": "runtime-created"})
|
|
default:
|
|
w.WriteHeader(204)
|
|
}
|
|
})
|
|
seedVerifiedImage(t, a, image, "0.2.0-rc.1")
|
|
if err := a.s.EnsureIntents(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var build string
|
|
if err := a.s.DB.QueryRow("SELECT id FROM image_builds WHERE version='0.2.0-rc.1'").Scan(&build); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
in := store.Intent{ID: store.ID(), UID: "owner", Name: "one", Version: "0.2.0-rc.1", Host: "panel.test"}
|
|
if err := a.s.ReserveIntent(in); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := a.s.DB.Exec("UPDATE creation_intents SET build_id=? WHERE id=?", build, in.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a.processIntents(context.Background())
|
|
a.processIntents(context.Background())
|
|
list, err := a.s.Intents("owner")
|
|
if err != nil || len(list) != 1 || list[0].Status != "success" || creates != 1 {
|
|
t.Fatalf("%+v err=%v creates=%d", list, err, creates)
|
|
}
|
|
instances, err := a.s.Instances("owner")
|
|
if err != nil || len(instances) != 1 || instances[0].ImageID != image || instances[0].Version != "0.2.0-rc.1" {
|
|
t.Fatalf("%+v %v", instances, err)
|
|
}
|
|
}
|
|
|
|
func TestIntentFailedBuildReleasesAndCancelPreservesBuild(t *testing.T) {
|
|
a, _ := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) { t.Error("failed intent contacted Docker") })
|
|
seedVerifiedImage(t, a, "sha256:"+strings.Repeat("b", 64), "0.2.0-rc.1")
|
|
if err := a.s.EnsureIntents(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var build string
|
|
a.s.DB.QueryRow("SELECT id FROM image_builds LIMIT 1").Scan(&build)
|
|
for _, id := range []string{"cancel", "fail"} {
|
|
if err := a.s.ReserveIntent(store.Intent{ID: id, UID: "owner", Version: "0.2.0-rc.1"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a.s.DB.Exec("UPDATE creation_intents SET build_id=? WHERE id=?", build, id)
|
|
}
|
|
if err := a.s.CancelIntent("cancel", "owner"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var status string
|
|
a.s.DB.QueryRow("SELECT status FROM image_builds WHERE id=?", build).Scan(&status)
|
|
if status != "success" {
|
|
t.Fatal(status)
|
|
}
|
|
a.s.DB.Exec("UPDATE image_builds SET status='failed' WHERE id=?", build)
|
|
a.processIntents(context.Background())
|
|
for n := 0; n < 2; n++ {
|
|
if err := a.s.ReserveIntent(store.Intent{ID: store.ID(), UID: "owner"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIntentRestartCleansInterruptedCreationAndRetainsWaiting(t *testing.T) {
|
|
deletes := 0
|
|
a, _ := runtimeTestAPI(t, func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == "GET" && r.URL.Path == "/v1.47/networks/dsh-inst-reserved" {
|
|
w.Write([]byte(`{"Id":"network","Name":"dsh-inst-reserved","Driver":"bridge","Labels":{"dsh-mgr.instance":"reserved"}}`))
|
|
return
|
|
}
|
|
if r.Method == "GET" && r.URL.Path == "/v1.47/containers/dsh-inst-reserved/json" {
|
|
w.Write([]byte(`{"Id":"recovered","Name":"/dsh-inst-reserved","Config":{"Labels":{"dsh-mgr.instance":"reserved","dsh-mgr.owner":"owner","dsh-mgr.role":"dsh"}}}`))
|
|
return
|
|
}
|
|
if r.Method == "DELETE" {
|
|
deletes++
|
|
}
|
|
w.WriteHeader(204)
|
|
})
|
|
if err := a.s.EnsureIntents(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, id := range []string{"waiting", "creating"} {
|
|
if err := a.s.ReserveIntent(store.Intent{ID: id, UID: "owner", Version: "0.2.0-rc.1"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := a.s.ClaimIntent("creating", store.Instance{ID: "reserved", UID: "owner", Version: "0.2.0-rc.1", Status: "creating"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := a.recoverIntents(context.Background()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if deletes != 2 {
|
|
t.Fatalf("cleanup calls=%d", deletes)
|
|
}
|
|
list, err := a.s.Intents("owner")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, in := range list {
|
|
if in.ID == "waiting" && in.Status != "waiting" {
|
|
t.Fatal(in)
|
|
}
|
|
if in.ID == "creating" && in.Status != "failed" {
|
|
t.Fatal(in)
|
|
}
|
|
}
|
|
if err := a.s.ReserveIntent(store.Intent{ID: store.ID(), UID: "owner"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|