package api import ( "crypto/sha256" "encoding/hex" "io" "log/slog" "net/http" "net/http/httptest" "net/url" "os" "path/filepath" "strings" "sync/atomic" "testing" "time" "github.com/ponzischeme89/memby/server/internal/appupdate" "github.com/ponzischeme89/memby/server/internal/config" "github.com/ponzischeme89/memby/server/internal/emby" ) func TestReleasePublishAuth(t *testing.T) { handler := func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) } t.Run("disabled is hidden", func(t *testing.T) { s := &Server{cfg: config.Config{}} rec := httptest.NewRecorder() s.releasePublishAuth(handler).ServeHTTP( rec, httptest.NewRequest(http.MethodPost, "/admin/api/release", nil), ) if rec.Code != http.StatusNotFound { t.Fatalf("got %d, want 404", rec.Code) } }) t.Run("valid bearer token is accepted", func(t *testing.T) { s := &Server{cfg: config.Config{ReleasePublishToken: "release-secret"}} rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodPost, "/admin/api/release", nil) req.Header.Set("Authorization", "Bearer release-secret") s.releasePublishAuth(handler).ServeHTTP(rec, req) if rec.Code != http.StatusNoContent { t.Fatalf("got %d, want 204", rec.Code) } }) } func TestReleaseDownloadOnlyServesVersionedAPKs(t *testing.T) { dir := t.TempDir() payload := []byte("PK signed apk") if err := os.WriteFile(filepath.Join(dir, "memby-0.1.54.apk"), payload, 0o600); err != nil { t.Fatal(err) } s := &Server{cfg: config.Config{ ReleaseDir: dir, ReleasePublishToken: "test-release-secret", }} rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, s.signedReleasePath("memby-0.1.54.apk"), nil) req.SetPathValue("filename", "memby-0.1.54.apk") s.handleReleaseDownload(rec, req) if rec.Code != http.StatusOK || rec.Body.String() != string(payload) { t.Fatalf("valid release response = %d %q", rec.Code, rec.Body.String()) } if disposition := rec.Header().Get("Content-Disposition"); disposition != `attachment; filename="memby-0.1.54.apk"` { t.Fatalf("Content-Disposition = %q", disposition) } if !strings.Contains(rec.Header().Get("X-Robots-Tag"), "noindex") { t.Fatalf("APK crawler policy = %q", rec.Header().Get("X-Robots-Tag")) } rec = httptest.NewRecorder() req = httptest.NewRequest(http.MethodGet, "/updates/../secrets", nil) req.SetPathValue("filename", "../secrets") s.handleReleaseDownload(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("invalid filename got %d, want 404", rec.Code) } } func TestInstallPageAndLatestDownloadUsePublishedRelease(t *testing.T) { dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "memby-0.1.72.apk"), []byte("apk"), 0o600); err != nil { t.Fatal(err) } s := &Server{cfg: config.Config{ ReleaseDir: dir, ReleasePublishToken: "test-release-secret", }} s.updatePolicy.set(appupdate.Policy{ LatestVersion: "0.1.72", Notes: ``, }) rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/install", nil) addInstallerSession(t, s, req) s.handleInstallPage(rec, req) if rec.Code != http.StatusOK { t.Fatalf("install page status = %d", rec.Code) } body := rec.Body.String() if !strings.Contains(body, "Download Memby 0.1.72") || !strings.Contains(body, `href="/updates/latest.apk"`) { t.Fatalf("install page is missing current release details: %s", body) } if strings.Contains(body, `