Files
llmgw/internal/web/org_page_test.go
T
Ilker ManapandClaude Opus 5.5 febcf251e4
Release / build (push) Successful in 1m16s
Release / notes (push) Successful in 4s
CI / test (push) Successful in 3m56s
Send an administrator from /org to the people page's panel
An organization's members, limits and use are a panel of the people page,
where they can also be changed, so the administrator's menu loses its
Organizations entry and /org redirects there. Managers keep /org.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 19:15:25 +02:00

146 lines
5.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")
}
}
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")
}
}