Files
llmgw/internal/web/org_page_test.go
T
Ilker ManapandClaude Opus 5.5 d8cefd8b21 Say there are no organizations rather than 404
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>
2026-09-28 20:23:19 +02:00

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")
}
}