package web import ( "context" "io" "log/slog" "net/http" "strings" "testing" "time" "gitea.mynodes.xyz/ilker/llmgw/internal/store" ) // fakeOrgs: Acme (managed by u1) and Scania. type fakeOrgs struct{ managers map[string]string } // org -> manager user id func (f fakeOrgs) ManagesOrg(_ context.Context, user, org string) (bool, error) { return f.managers[org] == user, nil } func (f fakeOrgs) ManagedOrgs(_ context.Context, user string) ([]store.Org, error) { var out []store.Org for _, o := range []store.Org{{ID: "org-a", Name: "Acme"}, {ID: "org-s", Name: "Scania"}} { if f.managers[o.ID] == user { out = append(out, o) } } return out, nil } func (f fakeOrgs) AllOrgs(context.Context) ([]store.Org, error) { return []store.Org{{ID: "org-a", Name: "Acme"}, {ID: "org-s", Name: "Scania"}}, nil } func (f fakeOrgs) Tree(context.Context) ([]store.TreeOrg, error) { return []store.TreeOrg{ {ID: "org-a", Name: "Acme", Teams: []store.TreeTeam{{ID: "t1", Name: "ml", Projects: []store.TreeProject{{ID: "p1", Name: "training"}}}}}, {ID: "org-s", Name: "Scania"}, }, nil } func (f fakeOrgs) AllMemberships(context.Context) ([]store.Membership, error) { return []store.Membership{ {UserID: "u1", Scope: store.ScopeOrg, ScopeID: "org-a", Role: store.RoleManager}, {UserID: "u2", Scope: store.ScopeProject, ScopeID: "p1", Role: store.RoleMember}, {UserID: "u3", Scope: store.ScopeOrg, ScopeID: "org-s", Role: store.RoleMember}, }, nil } func (f fakeOrgs) Users(context.Context, store.UserState) ([]store.User, error) { return []store.User{{ID: "u1", Email: "boss@acme"}, {ID: "u2", Email: "dev@acme"}, {ID: "u3", Email: "lars@scania"}}, nil } func (f fakeOrgs) AllLimits(context.Context) (map[string]store.Limits, error) { return map[string]store.Limits{}, nil } func (f fakeOrgs) UsageBucketsIn(context.Context, store.Grouping, string, time.Time, time.Duration) ([]store.UsageSeries, error) { return nil, nil } func (f fakeOrgs) UsageByScope(context.Context, store.Scope, time.Time) ([]store.ScopeUsage, error) { return nil, nil } func orgPage(t *testing.T, user store.User) *http.ServeMux { t.Helper() p := NewAccountPages(fakeAccounts{user: user}, nil, nil, "v0", slog.New(slog.NewTextHandler(io.Discard, nil))) mux := http.NewServeMux() p.RegisterOrgPages(mux, fakeOrgs{managers: map[string]string{"org-a": "u1"}}, nil) return mux } func TestAnOrganizationsManagerSeesItAndNothingElse(t *testing.T) { mux := orgPage(t, store.User{ID: "u1", Email: "boss@acme"}) w := mirrorGet(t, mux, "/org/org-a") if w.Code != http.StatusOK { t.Fatalf("the manager was refused their own organization: %d", w.Code) } body := w.Body.String() for _, want := range []string{"

Acme

", "dev@acme", "training", `href="/org"`} { if !strings.Contains(body, want) { t.Errorf("the page lacks %q", want) } } if strings.Contains(body, "lars@scania") { t.Error("another organization's member is on the page") } if w := mirrorGet(t, mux, "/org/org-s"); w.Code != http.StatusNotFound { t.Errorf("a manager of Acme could open Scania: %d", w.Code) } if w := mirrorGet(t, mux, "/org"); w.Code != http.StatusSeeOther || w.Header().Get("Location") != "/org/org-a" { t.Errorf("/org should lead to the organization they manage: %d %q", w.Code, w.Header().Get("Location")) } } func TestSomebodyWhoManagesNothingGetsNoOrganizationPage(t *testing.T) { mux := orgPage(t, store.User{ID: "u2", Email: "dev@acme"}) for _, path := range []string{"/org", "/org/org-a"} { if w := mirrorGet(t, mux, path); w.Code != http.StatusNotFound { t.Errorf("%s: %d, want 404", path, w.Code) } } } func TestAnAdministratorSeesEveryOrganization(t *testing.T) { mux := orgPage(t, store.User{ID: "admin", Email: "a@x", IsAdmin: true}) w := mirrorGet(t, mux, "/org/org-s") if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), "lars@scania") { t.Errorf("an administrator could not see Scania: %d", w.Code) } } type noOrgs struct{ fakeOrgs } func (noOrgs) AllOrgs(context.Context) ([]store.Org, error) { return nil, nil } func (noOrgs) ManagedOrgs(context.Context, string) ([]store.Org, error) { return nil, nil } func TestWithNoOrganizationsAnAdministratorIsToldSo(t *testing.T) { p := NewAccountPages(fakeAccounts{user: store.User{ID: "a", IsAdmin: true}}, nil, nil, "v0", slog.New(slog.NewTextHandler(io.Discard, nil))) mux := http.NewServeMux() p.RegisterOrgPages(mux, noOrgs{}, nil) w := mirrorGet(t, mux, "/org") if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), "There are none") { t.Errorf("an empty cluster answered %d: %s", w.Code, w.Body.String()) } // And the menu does not offer a page of nothing. if strings.Contains(w.Body.String(), `href="/org"`) { t.Error("the menu links to organizations when there are none") } } func TestAnAdministratorIsSentToThePeoplePageForAnOrganization(t *testing.T) { // Its members, limits and use are a panel there, beside the whole tree // and changeable; /org stays for the managers who cannot see that page. p := NewAccountPages(fakeAccounts{user: store.User{ID: "a", IsAdmin: true}}, nil, nil, "v0", slog.New(slog.NewTextHandler(io.Discard, nil))) mux := http.NewServeMux() p.RegisterAdminUsers(mux, &fakeAdmin{}) p.RegisterOrgPages(mux, fakeOrgs{}, nil) for path, to := range map[string]string{"/org": "/admin/users", "/org/org-s": "/admin/users?sel=org:org-s"} { w := mirrorGet(t, mux, path) if w.Code != http.StatusSeeOther || w.Header().Get("Location") != to { t.Errorf("%s: %d to %q, want %s", path, w.Code, w.Header().Get("Location"), to) } } if menu := menuRe.FindString(mirrorGet(t, mux, "/admin/users").Body.String()); strings.Contains(menu, `href="/org"`) { t.Error("the administrator's menu still offers /org beside People") } }