With every organization removed, /org answered an administrator with a 404 while the menu kept offering it. An administrator now gets a page saying there are none and where to make one, and the menu offers Organizations only when there is one to show (to a manager, one they manage). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
127 lines
4.7 KiB
Go
127 lines
4.7 KiB
Go
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{"<h1>Acme</h1>", "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")
|
|
}
|
|
}
|