package main import ( "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "testing/fstest" ) func newTestPublicApp(t *testing.T, downloads string) (*publicApp, http.Handler) { t.Helper() web := fstest.MapFS{ "app.html": &fstest.MapFile{Data: []byte("public app")}, "app.js": &fstest.MapFile{Data: []byte("js")}, "styles.css": &fstest.MapFile{Data: []byte("css")}, } app, err := newPublicApp("https://lrc.flechazo.xin", downloads, web, fakeQQ{}) if err != nil { t.Fatal(err) } return app, app.routes() } func publicRequest(method, path, body string) *http.Request { request := httptest.NewRequest(method, "https://lrc.flechazo.xin"+path, strings.NewReader(body)) request.Header.Set("Origin", "https://lrc.flechazo.xin") request.Header.Set("Content-Type", "application/json") request.Header.Set("X-Real-IP", "203.0.113.7") return request } func TestPublicPageNeedsNoSession(t *testing.T) { _, handler := newTestPublicApp(t, "") w := httptest.NewRecorder() handler.ServeHTTP(w, publicRequest(http.MethodGet, "/", "")) if w.Code != http.StatusOK || w.Body.String() != "public app" { t.Fatalf("status=%d body=%q", w.Code, w.Body.String()) } if w.Header().Get("Cache-Control") != "no-store" || w.Header().Get("X-Frame-Options") != "DENY" { t.Fatal("public application safety headers are missing") } } func TestPublicAPIRejectsWrongOrigin(t *testing.T) { _, handler := newTestPublicApp(t, "") request := publicRequest(http.MethodPost, "/api/search", `{"keyword":"test","limit":2}`) request.Header.Set("Origin", "https://attacker.example") w := httptest.NewRecorder() handler.ServeHTTP(w, request) if w.Code != http.StatusForbidden { t.Fatalf("expected 403, got %d", w.Code) } } func TestPublicSearchWorksWithoutCookie(t *testing.T) { _, handler := newTestPublicApp(t, "") w := httptest.NewRecorder() handler.ServeHTTP(w, publicRequest(http.MethodPost, "/api/search", `{"keyword":"测试","limit":2}`)) if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), "测试") { t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) } } func TestPublicPerClientRateLimit(t *testing.T) { _, handler := newTestPublicApp(t, "") for attempt := 1; attempt <= publicClientLimit+1; attempt++ { w := httptest.NewRecorder() handler.ServeHTTP(w, publicRequest(http.MethodPost, "/api/search", `{"keyword":"test","limit":1}`)) if attempt <= publicClientLimit && w.Code != http.StatusOK { t.Fatalf("attempt %d unexpectedly returned %d", attempt, w.Code) } if attempt == publicClientLimit+1 && w.Code != http.StatusTooManyRequests { t.Fatalf("expected final attempt to return 429, got %d", w.Code) } } } func TestPublicDownloadWhitelistAndRange(t *testing.T) { directory := t.TempDir() name := "lrc-local-windows-amd64.exe" if err := os.WriteFile(filepath.Join(directory, name), []byte("0123456789"), 0o600); err != nil { t.Fatal(err) } _, handler := newTestPublicApp(t, directory) request := publicRequest(http.MethodGet, "/download/"+name, "") request.Header.Set("Range", "bytes=2-5") w := httptest.NewRecorder() handler.ServeHTTP(w, request) if w.Code != http.StatusPartialContent || w.Body.String() != "2345" { t.Fatalf("status=%d body=%q", w.Code, w.Body.String()) } if !strings.Contains(w.Header().Get("Content-Disposition"), name) { t.Fatal("download disposition is missing") } w = httptest.NewRecorder() handler.ServeHTTP(w, publicRequest(http.MethodGet, "/download/not-allowed", "")) if w.Code != http.StatusNotFound { t.Fatalf("unexpected non-whitelist status %d", w.Code) } } func TestPublicOriginConfigurationMustUseHTTPS(t *testing.T) { _, err := newPublicApp("http://lrc.flechazo.xin", "", fstest.MapFS{}, fakeQQ{}) if err == nil { t.Fatal("expected insecure public origin to be rejected") } }