|
|
@@ -4,7 +4,12 @@
|
|
|
package model
|
|
|
|
|
|
import (
|
|
|
+ "net/http"
|
|
|
+ "os"
|
|
|
"testing"
|
|
|
+
|
|
|
+ "github.com/gorilla/mux"
|
|
|
+ "miniflux.app/v2/internal/config"
|
|
|
)
|
|
|
|
|
|
func TestEnclosure_Html5MimeTypeGivesOriginalMimeType(t *testing.T) {
|
|
|
@@ -26,8 +31,560 @@ func TestEnclosure_Html5MimeTypeReplaceStandardM4vByAppleSpecificMimeType(t *tes
|
|
|
// tested at the time of this commit (06/2023) on latest Firefox & Vivaldi on this feed
|
|
|
// https://www.florenceporcel.com/podcast/lfhdu.xml
|
|
|
t.Fatalf(
|
|
|
- "HTML5 MimeType must be replaced by 'video/x-m4v' when originally video/m4v to ensure playbacks in brownser. Got '%s'",
|
|
|
+ "HTML5 MimeType must be replaced by 'video/x-m4v' when originally video/m4v to ensure playbacks in browsers. Got '%s'",
|
|
|
enclosure.Html5MimeType(),
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+func TestEnclosure_IsAudio(t *testing.T) {
|
|
|
+ testCases := []struct {
|
|
|
+ name string
|
|
|
+ mimeType string
|
|
|
+ expected bool
|
|
|
+ }{
|
|
|
+ {"MP3 audio", "audio/mpeg", true},
|
|
|
+ {"WAV audio", "audio/wav", true},
|
|
|
+ {"OGG audio", "audio/ogg", true},
|
|
|
+ {"Mixed case audio", "Audio/MP3", true},
|
|
|
+ {"Video file", "video/mp4", false},
|
|
|
+ {"Image file", "image/jpeg", false},
|
|
|
+ {"Text file", "text/plain", false},
|
|
|
+ {"Empty mime type", "", false},
|
|
|
+ {"Audio with extra info", "audio/mpeg; charset=utf-8", true},
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range testCases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ enclosure := &Enclosure{MimeType: tc.mimeType}
|
|
|
+ if got := enclosure.IsAudio(); got != tc.expected {
|
|
|
+ t.Errorf("IsAudio() = %v, want %v for mime type %s", got, tc.expected, tc.mimeType)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestEnclosure_IsVideo(t *testing.T) {
|
|
|
+ testCases := []struct {
|
|
|
+ name string
|
|
|
+ mimeType string
|
|
|
+ expected bool
|
|
|
+ }{
|
|
|
+ {"MP4 video", "video/mp4", true},
|
|
|
+ {"AVI video", "video/avi", true},
|
|
|
+ {"WebM video", "video/webm", true},
|
|
|
+ {"M4V video", "video/m4v", true},
|
|
|
+ {"Mixed case video", "Video/MP4", true},
|
|
|
+ {"Audio file", "audio/mpeg", false},
|
|
|
+ {"Image file", "image/jpeg", false},
|
|
|
+ {"Text file", "text/plain", false},
|
|
|
+ {"Empty mime type", "", false},
|
|
|
+ {"Video with extra info", "video/mp4; codecs=\"avc1.42E01E\"", true},
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range testCases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ enclosure := &Enclosure{MimeType: tc.mimeType}
|
|
|
+ if got := enclosure.IsVideo(); got != tc.expected {
|
|
|
+ t.Errorf("IsVideo() = %v, want %v for mime type %s", got, tc.expected, tc.mimeType)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestEnclosure_IsImage(t *testing.T) {
|
|
|
+ testCases := []struct {
|
|
|
+ name string
|
|
|
+ mimeType string
|
|
|
+ url string
|
|
|
+ expected bool
|
|
|
+ }{
|
|
|
+ {"JPEG image by mime", "image/jpeg", "http://example.com/file", true},
|
|
|
+ {"PNG image by mime", "image/png", "http://example.com/file", true},
|
|
|
+ {"GIF image by mime", "image/gif", "http://example.com/file", true},
|
|
|
+ {"Mixed case image mime", "Image/JPEG", "http://example.com/file", true},
|
|
|
+ {"JPG file extension", "application/octet-stream", "http://example.com/photo.jpg", true},
|
|
|
+ {"JPEG file extension", "text/plain", "http://example.com/photo.jpeg", true},
|
|
|
+ {"PNG file extension", "unknown/type", "http://example.com/photo.png", true},
|
|
|
+ {"GIF file extension", "binary/data", "http://example.com/photo.gif", true},
|
|
|
+ {"Mixed case extension", "text/plain", "http://example.com/photo.JPG", true},
|
|
|
+ {"Image mime and extension", "image/jpeg", "http://example.com/photo.jpg", true},
|
|
|
+ {"Video file", "video/mp4", "http://example.com/video.mp4", false},
|
|
|
+ {"Audio file", "audio/mpeg", "http://example.com/audio.mp3", false},
|
|
|
+ {"Text file", "text/plain", "http://example.com/file.txt", false},
|
|
|
+ {"No extension", "text/plain", "http://example.com/file", false},
|
|
|
+ {"Other extension", "text/plain", "http://example.com/file.pdf", false},
|
|
|
+ {"Empty values", "", "", false},
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range testCases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ enclosure := &Enclosure{MimeType: tc.mimeType, URL: tc.url}
|
|
|
+ if got := enclosure.IsImage(); got != tc.expected {
|
|
|
+ t.Errorf("IsImage() = %v, want %v for mime type %s and URL %s", got, tc.expected, tc.mimeType, tc.url)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestEnclosureList_FindMediaPlayerEnclosure(t *testing.T) {
|
|
|
+ testCases := []struct {
|
|
|
+ name string
|
|
|
+ enclosures EnclosureList
|
|
|
+ expectedNil bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "Returns first audio enclosure",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/video.mp4", MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ expectedNil: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Returns first video enclosure",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/video.mp4", MimeType: "video/mp4"},
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ },
|
|
|
+ expectedNil: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Skips image enclosure and returns audio",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/image.jpg", MimeType: "image/jpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ },
|
|
|
+ expectedNil: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Skips enclosure with empty URL",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ },
|
|
|
+ expectedNil: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Returns nil for no media enclosures",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/image.jpg", MimeType: "image/jpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/doc.pdf", MimeType: "application/pdf"},
|
|
|
+ },
|
|
|
+ expectedNil: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Returns nil for empty list",
|
|
|
+ enclosures: EnclosureList{},
|
|
|
+ expectedNil: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Returns nil for all empty URLs",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "", MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ expectedNil: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range testCases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ result := tc.enclosures.FindMediaPlayerEnclosure()
|
|
|
+ if tc.expectedNil {
|
|
|
+ if result != nil {
|
|
|
+ t.Errorf("FindMediaPlayerEnclosure() = %v, want nil", result)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if result == nil {
|
|
|
+ t.Errorf("FindMediaPlayerEnclosure() = nil, want non-nil")
|
|
|
+ } else if !result.IsAudio() && !result.IsVideo() {
|
|
|
+ t.Errorf("FindMediaPlayerEnclosure() returned non-media enclosure: %s", result.MimeType)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestEnclosureList_ContainsAudioOrVideo(t *testing.T) {
|
|
|
+ testCases := []struct {
|
|
|
+ name string
|
|
|
+ enclosures EnclosureList
|
|
|
+ expected bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "Contains audio",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{MimeType: "image/jpeg"},
|
|
|
+ },
|
|
|
+ expected: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Contains video",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{MimeType: "image/jpeg"},
|
|
|
+ &Enclosure{MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ expected: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Contains both audio and video",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ expected: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Contains only images",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{MimeType: "image/jpeg"},
|
|
|
+ &Enclosure{MimeType: "image/png"},
|
|
|
+ },
|
|
|
+ expected: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Contains only documents",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{MimeType: "application/pdf"},
|
|
|
+ &Enclosure{MimeType: "text/plain"},
|
|
|
+ },
|
|
|
+ expected: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty list",
|
|
|
+ enclosures: EnclosureList{},
|
|
|
+ expected: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Single audio enclosure",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{MimeType: "audio/wav"},
|
|
|
+ },
|
|
|
+ expected: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Single video enclosure",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{MimeType: "video/webm"},
|
|
|
+ },
|
|
|
+ expected: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range testCases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ result := tc.enclosures.ContainsAudioOrVideo()
|
|
|
+ if result != tc.expected {
|
|
|
+ t.Errorf("ContainsAudioOrVideo() = %v, want %v", result, tc.expected)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestEnclosure_ProxifyEnclosureURL(t *testing.T) {
|
|
|
+ // Initialize config for testing
|
|
|
+ os.Clearenv()
|
|
|
+ os.Setenv("BASE_URL", "http://localhost")
|
|
|
+ os.Setenv("MEDIA_PROXY_PRIVATE_KEY", "test-private-key")
|
|
|
+
|
|
|
+ var err error
|
|
|
+ parser := config.NewParser()
|
|
|
+ config.Opts, err = parser.ParseEnvironmentVariables()
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf(`Config parsing failure: %v`, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ router := mux.NewRouter()
|
|
|
+ router.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
|
|
+
|
|
|
+ testCases := []struct {
|
|
|
+ name string
|
|
|
+ url string
|
|
|
+ mimeType string
|
|
|
+ mediaProxyOption string
|
|
|
+ mediaProxyResourceTypes []string
|
|
|
+ expectedURLChanged bool
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "HTTP URL with audio type - proxy mode all",
|
|
|
+ url: "http://example.com/audio.mp3",
|
|
|
+ mimeType: "audio/mpeg",
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "HTTPS URL with video type - proxy mode all",
|
|
|
+ url: "https://example.com/video.mp4",
|
|
|
+ mimeType: "video/mp4",
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "HTTP URL with video type - proxy mode http-only",
|
|
|
+ url: "http://example.com/video.mp4",
|
|
|
+ mimeType: "video/mp4",
|
|
|
+ mediaProxyOption: "http-only",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "HTTPS URL with video type - proxy mode http-only",
|
|
|
+ url: "https://example.com/video.mp4",
|
|
|
+ mimeType: "video/mp4",
|
|
|
+ mediaProxyOption: "http-only",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "HTTP URL with image type - not in resource types",
|
|
|
+ url: "http://example.com/image.jpg",
|
|
|
+ mimeType: "image/jpeg",
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "HTTP URL with image type - in resource types",
|
|
|
+ url: "http://example.com/image.jpg",
|
|
|
+ mimeType: "image/jpeg",
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video", "image"},
|
|
|
+ expectedURLChanged: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "HTTP URL - proxy mode none",
|
|
|
+ url: "http://example.com/audio.mp3",
|
|
|
+ mimeType: "audio/mpeg",
|
|
|
+ mediaProxyOption: "none",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty URL",
|
|
|
+ url: "",
|
|
|
+ mimeType: "audio/mpeg",
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Non-media MIME type",
|
|
|
+ url: "http://example.com/doc.pdf",
|
|
|
+ mimeType: "application/pdf",
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedURLChanged: false,
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range testCases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ enclosure := &Enclosure{
|
|
|
+ URL: tc.url,
|
|
|
+ MimeType: tc.mimeType,
|
|
|
+ }
|
|
|
+
|
|
|
+ originalURL := enclosure.URL
|
|
|
+
|
|
|
+ // Call the method
|
|
|
+ enclosure.ProxifyEnclosureURL(router, tc.mediaProxyOption, tc.mediaProxyResourceTypes)
|
|
|
+
|
|
|
+ // Check if URL changed as expected
|
|
|
+ urlChanged := enclosure.URL != originalURL
|
|
|
+ if urlChanged != tc.expectedURLChanged {
|
|
|
+ t.Errorf("ProxifyEnclosureURL() URL changed = %v, want %v. Original: %s, New: %s",
|
|
|
+ urlChanged, tc.expectedURLChanged, originalURL, enclosure.URL)
|
|
|
+ }
|
|
|
+
|
|
|
+ // If URL should have changed, verify it's not empty
|
|
|
+ if tc.expectedURLChanged && enclosure.URL == "" {
|
|
|
+ t.Error("ProxifyEnclosureURL() resulted in empty URL when proxification was expected")
|
|
|
+ }
|
|
|
+
|
|
|
+ // If URL shouldn't have changed, verify it's identical
|
|
|
+ if !tc.expectedURLChanged && enclosure.URL != originalURL {
|
|
|
+ t.Errorf("ProxifyEnclosureURL() URL changed unexpectedly from %s to %s", originalURL, enclosure.URL)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestEnclosureList_ProxifyEnclosureURL(t *testing.T) {
|
|
|
+ // Initialize config for testing
|
|
|
+ os.Clearenv()
|
|
|
+ os.Setenv("BASE_URL", "http://localhost")
|
|
|
+ os.Setenv("MEDIA_PROXY_PRIVATE_KEY", "test-private-key")
|
|
|
+
|
|
|
+ var err error
|
|
|
+ parser := config.NewParser()
|
|
|
+ config.Opts, err = parser.ParseEnvironmentVariables()
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf(`Config parsing failure: %v`, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ router := mux.NewRouter()
|
|
|
+ router.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
|
|
+
|
|
|
+ testCases := []struct {
|
|
|
+ name string
|
|
|
+ enclosures EnclosureList
|
|
|
+ mediaProxyOption string
|
|
|
+ mediaProxyResourceTypes []string
|
|
|
+ expectedChangedCount int
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ name: "Mixed enclosures with all proxy mode",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "https://example.com/video.mp4", MimeType: "video/mp4"},
|
|
|
+ &Enclosure{URL: "http://example.com/image.jpg", MimeType: "image/jpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/doc.pdf", MimeType: "application/pdf"},
|
|
|
+ },
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedChangedCount: 2, // audio and video should be proxified
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Mixed enclosures with http-only proxy mode",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "https://example.com/video.mp4", MimeType: "video/mp4"},
|
|
|
+ &Enclosure{URL: "http://example.com/video2.mp4", MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ mediaProxyOption: "http-only",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedChangedCount: 2, // only HTTP URLs should be proxified
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "No media types in resource list",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/video.mp4", MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"image"},
|
|
|
+ expectedChangedCount: 0, // no matching resource types
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Proxy mode none",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "http://example.com/audio.mp3", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/video.mp4", MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ mediaProxyOption: "none",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedChangedCount: 0,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Empty enclosure list",
|
|
|
+ enclosures: EnclosureList{},
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedChangedCount: 0,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Enclosures with empty URLs",
|
|
|
+ enclosures: EnclosureList{
|
|
|
+ &Enclosure{URL: "", MimeType: "audio/mpeg"},
|
|
|
+ &Enclosure{URL: "http://example.com/video.mp4", MimeType: "video/mp4"},
|
|
|
+ },
|
|
|
+ mediaProxyOption: "all",
|
|
|
+ mediaProxyResourceTypes: []string{"audio", "video"},
|
|
|
+ expectedChangedCount: 1, // only the non-empty URL should be processed
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range testCases {
|
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
|
+ // Store original URLs
|
|
|
+ originalURLs := make([]string, len(tc.enclosures))
|
|
|
+ for i, enclosure := range tc.enclosures {
|
|
|
+ originalURLs[i] = enclosure.URL
|
|
|
+ }
|
|
|
+
|
|
|
+ // Call the method
|
|
|
+ tc.enclosures.ProxifyEnclosureURL(router, tc.mediaProxyOption, tc.mediaProxyResourceTypes)
|
|
|
+
|
|
|
+ // Count how many URLs actually changed
|
|
|
+ changedCount := 0
|
|
|
+ for i, enclosure := range tc.enclosures {
|
|
|
+ if enclosure.URL != originalURLs[i] {
|
|
|
+ changedCount++
|
|
|
+ // Verify that changed URLs are not empty (unless they were empty originally)
|
|
|
+ if originalURLs[i] != "" && enclosure.URL == "" {
|
|
|
+ t.Errorf("Enclosure %d: ProxifyEnclosureURL resulted in empty URL", i)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if changedCount != tc.expectedChangedCount {
|
|
|
+ t.Errorf("ProxifyEnclosureURL() changed %d URLs, want %d", changedCount, tc.expectedChangedCount)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestEnclosure_ProxifyEnclosureURL_EdgeCases(t *testing.T) {
|
|
|
+ // Initialize config for testing
|
|
|
+ os.Clearenv()
|
|
|
+ os.Setenv("BASE_URL", "http://localhost")
|
|
|
+ os.Setenv("MEDIA_PROXY_PRIVATE_KEY", "test-private-key")
|
|
|
+
|
|
|
+ var err error
|
|
|
+ parser := config.NewParser()
|
|
|
+ config.Opts, err = parser.ParseEnvironmentVariables()
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf(`Config parsing failure: %v`, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ router := mux.NewRouter()
|
|
|
+ router.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
|
|
+ t.Run("Empty resource types slice", func(t *testing.T) {
|
|
|
+ enclosure := &Enclosure{
|
|
|
+ URL: "http://example.com/audio.mp3",
|
|
|
+ MimeType: "audio/mpeg",
|
|
|
+ }
|
|
|
+
|
|
|
+ originalURL := enclosure.URL
|
|
|
+ enclosure.ProxifyEnclosureURL(router, "all", []string{})
|
|
|
+
|
|
|
+ // With empty resource types, URL should not change
|
|
|
+ if enclosure.URL != originalURL {
|
|
|
+ t.Errorf("URL should not change with empty resource types. Original: %s, New: %s", originalURL, enclosure.URL)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ t.Run("Nil resource types slice", func(t *testing.T) {
|
|
|
+ enclosure := &Enclosure{
|
|
|
+ URL: "http://example.com/audio.mp3",
|
|
|
+ MimeType: "audio/mpeg",
|
|
|
+ }
|
|
|
+
|
|
|
+ originalURL := enclosure.URL
|
|
|
+ enclosure.ProxifyEnclosureURL(router, "all", nil)
|
|
|
+
|
|
|
+ // With nil resource types, URL should not change
|
|
|
+ if enclosure.URL != originalURL {
|
|
|
+ t.Errorf("URL should not change with nil resource types. Original: %s, New: %s", originalURL, enclosure.URL)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ t.Run("Invalid proxy mode", func(t *testing.T) {
|
|
|
+ enclosure := &Enclosure{
|
|
|
+ URL: "http://example.com/audio.mp3",
|
|
|
+ MimeType: "audio/mpeg",
|
|
|
+ }
|
|
|
+
|
|
|
+ originalURL := enclosure.URL
|
|
|
+ enclosure.ProxifyEnclosureURL(router, "invalid-mode", []string{"audio"})
|
|
|
+
|
|
|
+ // With invalid proxy mode, the function still proxifies non-HTTPS URLs
|
|
|
+ // because shouldProxifyURL defaults to checking URL scheme
|
|
|
+ if enclosure.URL == originalURL {
|
|
|
+ t.Errorf("URL should change for HTTP URL even with invalid proxy mode. Original: %s, New: %s", originalURL, enclosure.URL)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|