package httpapi import ( "errors" "html/template" "io" "log" "net/http" "net/http/httptest" "os" "path/filepath" "runtime" "testing" "scratchbox/internal/config" "scratchbox/internal/storage" ) func TestMultipartFileCount(t *testing.T) { t.Parallel() req := httptest.NewRequest(http.MethodPost, "/api/scratch", nil) if got := multipartFileCount(req); got != 0 { t.Fatalf("multipartFileCount() = %d, want 0", got) } } func TestWriteCreateErrorBranches(t *testing.T) { t.Parallel() s := &Server{} rec1 := httptest.NewRecorder() s.writeCreateError(rec1, &http.MaxBytesError{Limit: 5}) if rec1.Code != http.StatusRequestEntityTooLarge { t.Fatalf("max bytes error status = %d, want %d", rec1.Code, http.StatusRequestEntityTooLarge) } rec2 := httptest.NewRecorder() s.writeCreateError(rec2, errors.New("request body too large")) if rec2.Code != http.StatusRequestEntityTooLarge { t.Fatalf("body too large string status = %d, want %d", rec2.Code, http.StatusRequestEntityTooLarge) } rec3 := httptest.NewRecorder() s.writeCreateError(rec3, errors.New("other parse failure")) if rec3.Code != http.StatusBadRequest { t.Fatalf("generic error status = %d, want %d", rec3.Code, http.StatusBadRequest) } } func TestIsLikelyText(t *testing.T) { t.Parallel() if !isLikelyText("text/plain; charset=utf-8", []byte{0x00}) { t.Fatalf("text/plain should be treated as text") } if !isLikelyText("application/json", []byte{0x00}) { t.Fatalf("application/json should be treated as text") } if isLikelyText("application/octet-stream", []byte{0xff, 0xfe, 0xfd}) { t.Fatalf("binary octet-stream should not be treated as text") } if !isLikelyText("", []byte("hello")) { t.Fatalf("utf8 payload should be treated as text") } if !isLikelyText("", nil) { t.Fatalf("empty payload should be treated as text") } } func TestNewServerLoadsTemplates(t *testing.T) { tmp := t.TempDir() store, err := storage.NewFilesystemStore(filepath.Join(tmp, "data")) if err != nil { t.Fatalf("NewFilesystemStore() error = %v", err) } cfg := config.Config{ Limits: config.LimitsConfig{MaxUploadSizeBytes: 1024}, Security: config.SecurityConfig{ RateLimit: config.RateLimitConfig{Enabled: false}, }, } _, file, _, ok := runtime.Caller(0) if !ok { t.Fatal("runtime.Caller failed") } repoRoot := filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..")) wd, err := os.Getwd() if err != nil { t.Fatalf("Getwd() error = %v", err) } if err := os.Chdir(repoRoot); err != nil { t.Fatalf("Chdir() error = %v", err) } defer func() { _ = os.Chdir(wd) }() srv, err := NewServer(cfg, store, log.New(io.Discard, "", 0)) if err != nil { t.Fatalf("NewServer() error = %v", err) } if srv == nil || srv.templates == nil { t.Fatalf("expected initialized server with templates") } } func TestNewServerTemplateErrorAndIndexError(t *testing.T) { tmp := t.TempDir() store, err := storage.NewFilesystemStore(filepath.Join(tmp, "data")) if err != nil { t.Fatalf("NewFilesystemStore() error = %v", err) } cfg := config.Config{ Limits: config.LimitsConfig{MaxUploadSizeBytes: 1024}, Security: config.SecurityConfig{ RateLimit: config.RateLimitConfig{Enabled: false}, }, } wd, err := os.Getwd() if err != nil { t.Fatalf("Getwd() error = %v", err) } if err := os.Chdir(tmp); err != nil { t.Fatalf("Chdir() error = %v", err) } defer func() { _ = os.Chdir(wd) }() if _, err := NewServer(cfg, store, log.New(io.Discard, "", 0)); err == nil { t.Fatalf("expected NewServer() template parse error") } s := &Server{} func() { defer func() { if recover() == nil { t.Fatalf("expected panic when templates are nil") } }() rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/", nil) s.index(rec, req) }() } func TestIndexExecuteTemplateError(t *testing.T) { t.Parallel() s := &Server{ templates: template.New("empty"), } rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/", nil) s.index(rec, req) if rec.Code != http.StatusInternalServerError { t.Fatalf("index status = %d, want %d", rec.Code, http.StatusInternalServerError) } }