package handler import ( "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "codeberg.org/VARASYS/ZDDC/zddc/internal/config" ) func TestIsProjectRootURL(t *testing.T) { cases := map[string]bool{ "/Project-1": true, "/Project_2": true, "/Project-1/": false, // trailing slash "/Project-1/x": false, // deeper "/": false, // deployment root "": false, } for path, want := range cases { if got := IsProjectRootURL(path); got != want { t.Errorf("IsProjectRootURL(%q) = %v, want %v", path, got, want) } } } func TestServeProjectLanding(t *testing.T) { root := t.TempDir() // Need .zddc on disk for the resolver to be happy at the root. if err := os.WriteFile(filepath.Join(root, ".zddc"), []byte("acl:\n permissions:\n \"*\": rwcda\n"), 0o644); err != nil { t.Fatal(err) } // Project with two parties under archive/ (PascalCase to exercise // the case-insensitive archive resolver) and one orphaned dot-prefixed // dir that should be filtered out of the party list. for _, sub := range []string{ "Project-1/Archive/PartyA", "Project-1/Archive/PartyB", "Project-1/Archive/.hidden", } { if err := os.MkdirAll(filepath.Join(root, sub), 0o755); err != nil { t.Fatal(err) } } cfg := config.Config{Root: root} t.Run("renders project name + stage cards", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/Project-1", nil) rec := httptest.NewRecorder() ServeProjectLanding(cfg, rec, req, "Project-1") if rec.Code != http.StatusOK { t.Fatalf("status=%d", rec.Code) } body := rec.Body.String() // Page identifies the project and includes the four stages. for _, want := range []string{ "Project-1", "

Archive

", "

Working

", "

Staging

", "

Reviewing

", "Master Deliverables List", `href="/Project-1/working"`, `href="/Project-1/archive/"`, // Logo wraps to the deployment root — same convention as // shared/logo.js applies in tools (which would route here // to /Project-1, the project landing). On the project // landing itself, "next up" is the deployment root. `