package handler import ( "context" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "codeberg.org/VARASYS/ZDDC/zddc/internal/config" "codeberg.org/VARASYS/ZDDC/zddc/internal/zddc" ) func TestIsZddcFileRequest(t *testing.T) { cases := []struct { url string want bool }{ {"/.zddc", true}, {"/.zddc/", true}, {"/Project/.zddc", true}, {"/Project/archive/PartyA/mdl/.zddc", true}, {"/.zddc.html", false}, // editor leaf, handled separately {"/Project/.zddc.html", false}, {"/Project/.zddc/", true}, {"/Project/", false}, {"/", false}, } for _, tc := range cases { if got := IsZddcFileRequest(tc.url); got != tc.want { t.Errorf("IsZddcFileRequest(%q) = %v, want %v", tc.url, got, tc.want) } } } func TestServeZddcFile_ExistingFile(t *testing.T) { root := t.TempDir() mustWrite(t, filepath.Join(root, ".zddc"), "title: root\nacl:\n permissions:\n \"*\": rwcda\n") subDir := filepath.Join(root, "Project") if err := os.Mkdir(subDir, 0o755); err != nil { t.Fatal(err) } mustWrite(t, filepath.Join(subDir, ".zddc"), "title: project-level\n") zddc.InvalidateCache(root) cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"} req := httptest.NewRequest(http.MethodGet, "/Project/.zddc", nil) req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com")) rec := httptest.NewRecorder() ServeZddcFile(cfg, rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, body = %s", rec.Code, rec.Body.String()) } if !strings.Contains(rec.Body.String(), "title: project-level") { t.Errorf("body missing on-disk content: %q", rec.Body.String()) } if got := rec.Header().Get("X-ZDDC-Source"); !strings.HasPrefix(got, "file:") { t.Errorf("X-ZDDC-Source = %q, want file:* prefix", got) } if got := rec.Header().Get("Content-Type"); !strings.HasPrefix(got, "application/yaml") { t.Errorf("Content-Type = %q, want application/yaml*", got) } } func TestServeZddcFile_VirtualDefault(t *testing.T) { root := t.TempDir() mustWrite(t, filepath.Join(root, ".zddc"), "title: bootstrap\nacl:\n permissions:\n \"*\": rwcda\n") // Directory exists but has no .zddc. subDir := filepath.Join(root, "Project") if err := os.Mkdir(subDir, 0o755); err != nil { t.Fatal(err) } zddc.InvalidateCache(root) cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"} req := httptest.NewRequest(http.MethodGet, "/Project/.zddc", nil) req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com")) rec := httptest.NewRecorder() ServeZddcFile(cfg, rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, body = %s", rec.Code, rec.Body.String()) } if got := rec.Header().Get("X-ZDDC-Source"); got != "virtual:zddc" { t.Errorf("X-ZDDC-Source = %q, want virtual:zddc", got) } body := rec.Body.String() if !strings.Contains(body, "Virtual .zddc") { t.Errorf("body missing virtual marker: %q", body) } // Should show the root's title from the cascade. if !strings.Contains(body, "bootstrap") { t.Errorf("body missing root cascade summary: %q", body) } // Should parse as valid YAML (empty document or {} at the end). if !strings.Contains(body, "{}") { t.Errorf("body missing placeholder body: %q", body) } } func TestServeZddcFile_NonGetRejected(t *testing.T) { root := t.TempDir() mustWrite(t, filepath.Join(root, ".zddc"), "acl:\n permissions:\n \"*\": rwcda\n") zddc.InvalidateCache(root) cfg := config.Config{Root: root, EmailHeader: "X-Auth-Request-Email"} req := httptest.NewRequest(http.MethodPut, "/.zddc", strings.NewReader("title: hacked\n")) req = req.WithContext(context.WithValue(req.Context(), EmailKey, "alice@example.com")) rec := httptest.NewRecorder() ServeZddcFile(cfg, rec, req) if rec.Code != http.StatusMethodNotAllowed { t.Errorf("status = %d, want 405", rec.Code) } }