package console import ( "net/http" "net/url" "reflect" "strings" "testing" "time" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/status" ) func TestInstanceGroupingPaginatesWithoutLoss(t *testing.T) { rows := []InstanceRow{ {Host: "a.example.com", Channel: "stable", HealthWord: "Running"}, {Host: "b.example.com", Channel: "edge", HealthWord: "Asleep"}, {Host: "c.example.com", Channel: "stable", HealthWord: "Running"}, {Host: "d.example.com", Channel: "edge", HealthWord: "Running"}, } q := instanceQuery{Group: true} var got []string for i := 0; i < 4; i++ { p := pageInstanceRows(rows, q, 1) for _, r := range p.Rows { got = append(got, r.Host) } if p.NextCursor == "" { break } q.Cursor = p.NextCursor u, err := url.Parse(p.NextURL) if err != nil { t.Fatal(err) } if u.Query().Get("group") != "channel" { t.Fatal(p.NextURL) } } want := []string{"b.example.com", "d.example.com", "a.example.com", "c.example.com"} if !reflect.DeepEqual(got, want) { t.Fatalf("got %v want %v", got, want) } p := pageInstanceRows(rows, instanceQuery{Cursor: q.Cursor}, 50) if len(p.Rows) != 4 { t.Fatal("changing grouping must reset a cursor from the other order") } } func TestInstanceRowsExcludePlatformAliases(t *testing.T) { routes := []RouteView{ {Host: "app.example", Account: "ours", Kind: "instance"}, {Host: "static.example", Account: "ours", Kind: "static"}, {Host: "alias.example", Account: "ours", Kind: "static", Platform: true}, {Host: "foreign.example", Account: "theirs", Kind: "instance"}, } owned := ownedInstanceRoutes(routes, "ours") if len(owned) != 2 || owned[0].Host != "app.example" || owned[1].Host != "static.example" { t.Fatalf("owned instance routes = %+v", owned) } rows := assembleInstanceRows(&AppView{Routes: routes}, &Account{Sqid: "ours"}, nil) if len(rows) != 2 || rows[0].Host != "app.example" || rows[1].Host != "static.example" { t.Fatalf("instance rows = %+v", rows) } } func TestInstanceChannelFilterAndLinks(t *testing.T) { rows := []InstanceRow{{Host: "a.example.com", Channel: "canary/a+b", HealthWord: "Running"}, {Host: "b.example.com", Channel: "edge", HealthWord: "Asleep"}} q := instanceQuery{Channel: "canary/a+b", Group: true} p := pageInstanceRows(rows, q, 50) if p.Total != 1 || p.Rows[0].Host != "a.example.com" { t.Fatalf("%+v", p) } for _, l := range p.Links { u, e := url.Parse(l.URL) if e != nil { t.Fatal(e) } if u.Query().Get("channel") != q.Channel || u.Query().Get("group") != "channel" { t.Fatal(l.URL) } } q.Channel = "missing" if p := pageInstanceRows(rows, q, 50); p.Total != 0 { t.Fatalf("unmatched channel widened: %+v", p) } } func TestInstanceGroupsKeepTargetSeparate(t *testing.T) { rows := []InstanceRow{{Host: "a.example.com", Channel: "edge", Version: "old"}, {Host: "b.example.com", Channel: "edge", Version: "new"}} groups := groupInstanceRows(rows[:1], []ChannelView{{Name: "edge", Version: "new"}}, rows) if len(groups) != 1 || groups[0].Total != 2 || groups[0].TargetVersion != "new" || groups[0].Rows[0].Version != "old" { t.Fatalf("%+v", groups) } } func TestInstanceGroupCountsRespectActiveFilters(t *testing.T) { rows := []InstanceRow{ {Host: "a.example.com", Channel: "edge", HealthWord: "Running"}, {Host: "b.example.com", Channel: "edge", HealthWord: "Asleep"}, {Host: "c.example.com", Channel: "stable", HealthWord: "Running"}, } p := pageInstanceRows(rows, instanceQuery{Group: true, Channel: "edge", Health: "Running"}, 1) groups := groupInstanceRows(p.Rows, nil, p.Matching) if len(groups) != 1 || groups[0].Channel != "edge" || groups[0].Total != 1 || len(groups[0].Rows) != 1 { t.Fatalf("groups = %+v; page = %+v", groups, p) } if p.Counts["Running"] != 2 || p.Counts["Asleep"] != 1 { t.Fatalf("global health counts changed with filters: %+v", p.Counts) } } func TestOwnedInstanceRoutesDoesNotPassOtherAccountsToSelfReport(t *testing.T) { routes := []RouteView{{Host: "mine.example.com", Account: "bab"}, {Host: "theirs.example.com", Account: "bac"}} got := ownedInstanceRoutes(routes, "bab") if len(got) != 1 || got[0].Host != "mine.example.com" { t.Fatalf("%+v", got) } } func TestInstanceDetailOwnsRouteAndAliases(t *testing.T) { fx := newConsoleFixture(t) c := signInPaul(t, fx) reg, err := registry.Open(fx.regPath) if err != nil { t.Fatal(err) } t.Cleanup(func() { reg.Close() }) if err := reg.Upsert(registry.Route{Host: "foreign.example.com", App: "kass", Account: "bac", Kind: "instance", Version: "foreign-version"}); err != nil { t.Fatal(err) } if err := reg.UpsertAlias(registry.RouteAlias{Alias: "alias.example.com", Host: "kass.carlosapp.dev", Account: fx.sqid, App: "kass"}); err != nil { t.Fatal(err) } if err := reg.UpsertAlias(registry.RouteAlias{Alias: "foreign-alias.example.com", Host: "kass.carlosapp.dev", Account: "bac", App: "kass"}); err != nil { t.Fatal(err) } status, _, body := get(t, c, fx.u("/apps/kass/instances/kass.carlosapp.dev?channel=stable&group=channel")) if status != http.StatusOK { t.Fatalf("%d: %s", status, body) } for _, want := range []string{"kass.carlosapp.dev", "alias.example.com", `/instances?channel=stable&group=channel`, `href="https://kass.carlosapp.dev"`} { if !strings.Contains(body, want) { t.Errorf("missing %q", want) } } for _, bad := range []string{"foreign-version", "foreign-alias.example.com", "stamp its unit"} { if strings.Contains(body, bad) { t.Errorf("leaked %q", bad) } } for _, host := range []string{"foreign.example.com", "otherapp.carlosapp.dev", "missing.example.com"} { status, _, body := get(t, c, fx.u("/apps/kass/instances/"+host)) if status != http.StatusNotFound || body != "404 page not found\n" { t.Fatalf("%s = %d %q", host, status, body) } } } func TestInstanceDetailRejectsPlatformAlias(t *testing.T) { fx := newConsoleFixture(t) c := signInPaul(t, fx) fx.addRoute(t, registry.Route{Host: "platform-alias.example.com", App: "kass", Account: fx.sqid, Kind: "static", Platform: true}) status, _, body := get(t, c, fx.u("/apps/kass/instances/platform-alias.example.com")) if status != http.StatusNotFound || body != "404 page not found\n" { t.Fatalf("platform alias detail = %d %q", status, body) } } func TestInstanceListLinksToDetailAndKeepsHealthShort(t *testing.T) { fx := newConsoleFixture(t) c := signInPaul(t, fx) status, _, body := get(t, c, fx.u("/apps/kass/instances?group=channel&channel=stable")) if status != http.StatusOK { t.Fatalf("%d %s", status, body) } if !strings.Contains(body, `/instances/kass.carlosapp.dev?channel=stable&group=channel`) { t.Fatal("missing filtered detail link") } if !strings.Contains(body, `href="https://kass.carlosapp.dev"`) { t.Fatal("missing separate external link") } if strings.Contains(body, "stamp its unit") { t.Fatal("health cell still carries infrastructure instructions") } if !strings.Contains(body, `class="instance-group"`) { t.Fatal("missing grouped table") } } func TestInstanceListKeepsUnknownSelectedChannelVisible(t *testing.T) { fx := newConsoleFixture(t) c := signInPaul(t, fx) status, _, body := get(t, c, fx.u("/apps/kass/instances?channel=retired")) if status != http.StatusOK { t.Fatalf("%d %s", status, body) } if !strings.Contains(body, ``) { t.Fatalf("selected unknown channel is invisible: %s", body) } if strings.Contains(body, `