package handler import ( "context" "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "testing" "codeberg.org/VARASYS/ZDDC/zddc/internal/config" ) // TestServeProjectListFiltersHiddenAndScaffolding asserts the project list // excludes both '.'-prefixed entries (the long-standing rule, e.g. .devshell) // AND '_'-prefixed entries (operator scaffolding like install.zip's // _template/ that's reachable by direct URL but should not clutter the // project picker). func TestServeProjectListFiltersHiddenAndScaffolding(t *testing.T) { root := t.TempDir() for _, name := range []string{ "Project-A", "Project-B", ".devshell", // dot-prefixed dir — must be excluded "_template", // underscore scaffolding — must be excluded "_archive", } { if err := os.MkdirAll(filepath.Join(root, name), 0o755); err != nil { t.Fatalf("mkdir %s: %v", name, err) } } // A loose .zddc that allows everyone, so ACL doesn't interfere with // what we're actually testing (the prefix filter). if err := os.WriteFile(filepath.Join(root, ".zddc"), []byte("acl:\n allow: [\"*\"]\n"), 0o644); err != nil { t.Fatalf("write .zddc: %v", err) } cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"} req := httptest.NewRequest(http.MethodGet, "/", nil) req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com")) rec := httptest.NewRecorder() ServeProjectList(cfg, rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200; body = %s", rec.Code, rec.Body.String()) } var got []map[string]string if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil { t.Fatalf("invalid JSON: %v\n%s", err, rec.Body.String()) } want := map[string]bool{"Project-A": true, "Project-B": true} if len(got) != len(want) { t.Fatalf("got %d projects (%+v), want %d (%v)", len(got), got, len(want), want) } for _, p := range got { if !want[p["name"]] { t.Errorf("unexpected project in list: %q", p["name"]) } } } // TestServeProjectListIncludesTitleFromPerProjectZddc verifies a project's own // .zddc `title:` field surfaces in the JSON response; projects without it (or // without any .zddc) come back with an empty/absent title. func TestServeProjectListIncludesTitleFromPerProjectZddc(t *testing.T) { root := t.TempDir() for _, name := range []string{"176109", "197072"} { if err := os.MkdirAll(filepath.Join(root, name), 0o755); err != nil { t.Fatalf("mkdir %s: %v", name, err) } } if err := os.WriteFile(filepath.Join(root, ".zddc"), []byte("acl:\n allow: [\"*\"]\n"), 0o644); err != nil { t.Fatalf("write root .zddc: %v", err) } if err := os.WriteFile(filepath.Join(root, "176109", ".zddc"), []byte("title: \"Greenfield Substation\"\n"), 0o644); err != nil { t.Fatalf("write project .zddc: %v", err) } cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"} req := httptest.NewRequest(http.MethodGet, "/", nil) req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com")) rec := httptest.NewRecorder() ServeProjectList(cfg, rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want 200; body = %s", rec.Code, rec.Body.String()) } var got []ProjectInfo if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil { t.Fatalf("invalid JSON: %v\n%s", err, rec.Body.String()) } titles := map[string]string{} for _, p := range got { titles[p.Name] = p.Title } if titles["176109"] != "Greenfield Substation" { t.Errorf("176109 title = %q, want %q", titles["176109"], "Greenfield Substation") } if titles["197072"] != "" { t.Errorf("197072 title = %q, want empty", titles["197072"]) } }