mirror of
https://github.com/miniflux/v2.git
synced 2025-08-06 17:41:00 +00:00
refactor(model): add test coverage and simplify ProxifyEnclosureURL
This commit is contained in:
parent
7107ff985f
commit
63891501e5
7 changed files with 746 additions and 46 deletions
|
@ -7,6 +7,7 @@ import (
|
||||||
json_parser "encoding/json"
|
json_parser "encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"miniflux.app/v2/internal/config"
|
||||||
"miniflux.app/v2/internal/http/request"
|
"miniflux.app/v2/internal/http/request"
|
||||||
"miniflux.app/v2/internal/http/response/json"
|
"miniflux.app/v2/internal/http/response/json"
|
||||||
"miniflux.app/v2/internal/model"
|
"miniflux.app/v2/internal/model"
|
||||||
|
@ -33,7 +34,7 @@ func (h *handler) getEnclosureByID(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
enclosure.ProxifyEnclosureURL(h.router)
|
enclosure.ProxifyEnclosureURL(h.router, config.Opts.MediaProxyMode(), config.Opts.MediaProxyResourceTypes())
|
||||||
|
|
||||||
json.OK(w, r, enclosure)
|
json.OK(w, r, enclosure)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"miniflux.app/v2/internal/config"
|
||||||
"miniflux.app/v2/internal/http/request"
|
"miniflux.app/v2/internal/http/request"
|
||||||
"miniflux.app/v2/internal/http/response/json"
|
"miniflux.app/v2/internal/http/response/json"
|
||||||
"miniflux.app/v2/internal/integration"
|
"miniflux.app/v2/internal/integration"
|
||||||
|
@ -34,8 +35,7 @@ func (h *handler) getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content)
|
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content)
|
||||||
|
entry.Enclosures.ProxifyEnclosureURL(h.router, config.Opts.MediaProxyMode(), config.Opts.MediaProxyResourceTypes())
|
||||||
entry.Enclosures.ProxifyEnclosureURL(h.router)
|
|
||||||
|
|
||||||
json.OK(w, r, entry)
|
json.OK(w, r, entry)
|
||||||
}
|
}
|
||||||
|
|
|
@ -763,8 +763,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content)
|
entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content)
|
||||||
|
entry.Enclosures.ProxifyEnclosureURL(h.router, config.Opts.MediaProxyMode(), config.Opts.MediaProxyResourceTypes())
|
||||||
entry.Enclosures.ProxifyEnclosureURL(h.router)
|
|
||||||
|
|
||||||
contentItems[i] = contentItem{
|
contentItems[i] = contentItem{
|
||||||
ID: convertEntryIDToLongFormItemID(entry.ID),
|
ID: convertEntryIDToLongFormItemID(entry.ID),
|
||||||
|
|
|
@ -589,3 +589,145 @@ func TestProxyFilterVideoPosterOnce(t *testing.T) {
|
||||||
t.Errorf(`Not expected output: got %s`, output)
|
t.Errorf(`Not expected output: got %s`, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShouldProxifyURLWithMimeType(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
mediaURL string
|
||||||
|
mediaMimeType string
|
||||||
|
mediaProxyOption string
|
||||||
|
mediaProxyResourceTypes []string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Empty URL should not be proxified",
|
||||||
|
mediaURL: "",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Data URL should not be proxified",
|
||||||
|
mediaURL: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
|
||||||
|
mediaMimeType: "image/png",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTP URL with all mode and matching MIME type should be proxified",
|
||||||
|
mediaURL: "http://example.com/image.jpg",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTPS URL with all mode and matching MIME type should be proxified",
|
||||||
|
mediaURL: "https://example.com/image.jpg",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTP URL with http-only mode and matching MIME type should be proxified",
|
||||||
|
mediaURL: "http://example.com/image.jpg",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "http-only",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTPS URL with http-only mode should not be proxified",
|
||||||
|
mediaURL: "https://example.com/image.jpg",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "http-only",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with none mode should not be proxified",
|
||||||
|
mediaURL: "http://example.com/image.jpg",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "none",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with matching MIME type should be proxified",
|
||||||
|
mediaURL: "http://example.com/video.mp4",
|
||||||
|
mediaMimeType: "video/mp4",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"video"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with non-matching MIME type should not be proxified",
|
||||||
|
mediaURL: "http://example.com/video.mp4",
|
||||||
|
mediaMimeType: "video/mp4",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with multiple resource types and matching MIME type should be proxified",
|
||||||
|
mediaURL: "http://example.com/audio.mp3",
|
||||||
|
mediaMimeType: "audio/mp3",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image", "audio", "video"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with multiple resource types but non-matching MIME type should not be proxified",
|
||||||
|
mediaURL: "http://example.com/document.pdf",
|
||||||
|
mediaMimeType: "application/pdf",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image", "audio", "video"},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with empty resource types should not be proxified",
|
||||||
|
mediaURL: "http://example.com/image.jpg",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with partial MIME type match should be proxified",
|
||||||
|
mediaURL: "http://example.com/image.jpg",
|
||||||
|
mediaMimeType: "image/jpeg",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"image"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with audio MIME type and audio resource type should be proxified",
|
||||||
|
mediaURL: "http://example.com/song.ogg",
|
||||||
|
mediaMimeType: "audio/ogg",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"audio"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "URL with video MIME type and video resource type should be proxified",
|
||||||
|
mediaURL: "http://example.com/movie.webm",
|
||||||
|
mediaMimeType: "video/webm",
|
||||||
|
mediaProxyOption: "all",
|
||||||
|
mediaProxyResourceTypes: []string{"video"},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := ShouldProxifyURLWithMimeType(tc.mediaURL, tc.mediaMimeType, tc.mediaProxyOption, tc.mediaProxyResourceTypes)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("Expected %v, got %v for URL: %s, MIME type: %s, proxy option: %s, resource types: %v",
|
||||||
|
tc.expected, result, tc.mediaURL, tc.mediaMimeType, tc.mediaProxyOption, tc.mediaProxyResourceTypes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter,
|
||||||
case "image":
|
case "image":
|
||||||
doc.Find("img, picture source").Each(func(i int, img *goquery.Selection) {
|
doc.Find("img, picture source").Each(func(i int, img *goquery.Selection) {
|
||||||
if srcAttrValue, ok := img.Attr("src"); ok {
|
if srcAttrValue, ok := img.Attr("src"); ok {
|
||||||
if shouldProxy(srcAttrValue, proxyOption) {
|
if shouldProxifyURL(srcAttrValue, proxyOption) {
|
||||||
img.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
img.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter,
|
||||||
if !slices.Contains(config.Opts.MediaProxyResourceTypes(), "video") {
|
if !slices.Contains(config.Opts.MediaProxyResourceTypes(), "video") {
|
||||||
doc.Find("video").Each(func(i int, video *goquery.Selection) {
|
doc.Find("video").Each(func(i int, video *goquery.Selection) {
|
||||||
if posterAttrValue, ok := video.Attr("poster"); ok {
|
if posterAttrValue, ok := video.Attr("poster"); ok {
|
||||||
if shouldProxy(posterAttrValue, proxyOption) {
|
if shouldProxifyURL(posterAttrValue, proxyOption) {
|
||||||
video.SetAttr("poster", proxifyFunction(router, posterAttrValue))
|
video.SetAttr("poster", proxifyFunction(router, posterAttrValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter,
|
||||||
case "audio":
|
case "audio":
|
||||||
doc.Find("audio, audio source").Each(func(i int, audio *goquery.Selection) {
|
doc.Find("audio, audio source").Each(func(i int, audio *goquery.Selection) {
|
||||||
if srcAttrValue, ok := audio.Attr("src"); ok {
|
if srcAttrValue, ok := audio.Attr("src"); ok {
|
||||||
if shouldProxy(srcAttrValue, proxyOption) {
|
if shouldProxifyURL(srcAttrValue, proxyOption) {
|
||||||
audio.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
audio.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,13 @@ func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter,
|
||||||
case "video":
|
case "video":
|
||||||
doc.Find("video, video source").Each(func(i int, video *goquery.Selection) {
|
doc.Find("video, video source").Each(func(i int, video *goquery.Selection) {
|
||||||
if srcAttrValue, ok := video.Attr("src"); ok {
|
if srcAttrValue, ok := video.Attr("src"); ok {
|
||||||
if shouldProxy(srcAttrValue, proxyOption) {
|
if shouldProxifyURL(srcAttrValue, proxyOption) {
|
||||||
video.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
video.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if posterAttrValue, ok := video.Attr("poster"); ok {
|
if posterAttrValue, ok := video.Attr("poster"); ok {
|
||||||
if shouldProxy(posterAttrValue, proxyOption) {
|
if shouldProxifyURL(posterAttrValue, proxyOption) {
|
||||||
video.SetAttr("poster", proxifyFunction(router, posterAttrValue))
|
video.SetAttr("poster", proxifyFunction(router, posterAttrValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ func proxifySourceSet(element *goquery.Selection, router *mux.Router, proxifyFun
|
||||||
imageCandidates := sanitizer.ParseSrcSetAttribute(srcsetAttrValue)
|
imageCandidates := sanitizer.ParseSrcSetAttribute(srcsetAttrValue)
|
||||||
|
|
||||||
for _, imageCandidate := range imageCandidates {
|
for _, imageCandidate := range imageCandidates {
|
||||||
if shouldProxy(imageCandidate.ImageURL, proxyOption) {
|
if shouldProxifyURL(imageCandidate.ImageURL, proxyOption) {
|
||||||
imageCandidate.ImageURL = proxifyFunction(router, imageCandidate.ImageURL)
|
imageCandidate.ImageURL = proxifyFunction(router, imageCandidate.ImageURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,15 +107,33 @@ func proxifySourceSet(element *goquery.Selection, router *mux.Router, proxifyFun
|
||||||
element.SetAttr("srcset", imageCandidates.String())
|
element.SetAttr("srcset", imageCandidates.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldProxy(attrValue, proxyOption string) bool {
|
// shouldProxifyURL checks if the media URL should be proxified based on the media proxy option and URL scheme.
|
||||||
if strings.HasPrefix(attrValue, "data:") {
|
func shouldProxifyURL(mediaURL, mediaProxyOption string) bool {
|
||||||
|
switch {
|
||||||
|
case mediaURL == "":
|
||||||
|
return false
|
||||||
|
case strings.HasPrefix(mediaURL, "data:"):
|
||||||
|
return false
|
||||||
|
case mediaProxyOption == "all":
|
||||||
|
return true
|
||||||
|
case mediaProxyOption != "none" && !urllib.IsHTTPS(mediaURL):
|
||||||
|
return true
|
||||||
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if proxyOption == "all" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if !urllib.IsHTTPS(attrValue) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldProxifyURLWithMimeType checks if the media URL should be proxified based on the media proxy option, URL scheme, and MIME type.
|
||||||
|
func ShouldProxifyURLWithMimeType(mediaURL, mediaMimeType, mediaProxyOption string, mediaProxyResourceTypes []string) bool {
|
||||||
|
if !shouldProxifyURL(mediaURL, mediaProxyOption) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mediaType := range mediaProxyResourceTypes {
|
||||||
|
if strings.HasPrefix(mediaMimeType, mediaType+"/") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"miniflux.app/v2/internal/config"
|
|
||||||
"miniflux.app/v2/internal/mediaproxy"
|
"miniflux.app/v2/internal/mediaproxy"
|
||||||
"miniflux.app/v2/internal/urllib"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enclosure represents an attachment.
|
// Enclosure represents an attachment.
|
||||||
|
@ -52,6 +51,13 @@ func (e *Enclosure) IsImage() bool {
|
||||||
return strings.HasSuffix(mediaURL, ".jpg") || strings.HasSuffix(mediaURL, ".jpeg") || strings.HasSuffix(mediaURL, ".png") || strings.HasSuffix(mediaURL, ".gif")
|
return strings.HasSuffix(mediaURL, ".jpg") || strings.HasSuffix(mediaURL, ".jpeg") || strings.HasSuffix(mediaURL, ".png") || strings.HasSuffix(mediaURL, ".gif")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxifyEnclosureURL modifies the enclosure URL to use the media proxy if necessary.
|
||||||
|
func (e *Enclosure) ProxifyEnclosureURL(router *mux.Router, mediaProxyOption string, mediaProxyResourceTypes []string) {
|
||||||
|
if mediaproxy.ShouldProxifyURLWithMimeType(e.URL, e.MimeType, mediaProxyOption, mediaProxyResourceTypes) {
|
||||||
|
e.URL = mediaproxy.ProxifyAbsoluteURL(router, e.URL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// EnclosureList represents a list of attachments.
|
// EnclosureList represents a list of attachments.
|
||||||
type EnclosureList []*Enclosure
|
type EnclosureList []*Enclosure
|
||||||
|
|
||||||
|
@ -77,31 +83,8 @@ func (el EnclosureList) ContainsAudioOrVideo() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el EnclosureList) ProxifyEnclosureURL(router *mux.Router) {
|
func (el EnclosureList) ProxifyEnclosureURL(router *mux.Router, mediaProxyOption string, mediaProxyResourceTypes []string) {
|
||||||
proxyOption := config.Opts.MediaProxyMode()
|
for _, enclosure := range el {
|
||||||
|
enclosure.ProxifyEnclosureURL(router, mediaProxyOption, mediaProxyResourceTypes)
|
||||||
if proxyOption != "none" {
|
|
||||||
for i := range el {
|
|
||||||
if urllib.IsHTTPS(el[i].URL) {
|
|
||||||
proxifyAbsoluteURLIfMimeType(el[i], router)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Enclosure) ProxifyEnclosureURL(router *mux.Router) {
|
|
||||||
proxyOption := config.Opts.MediaProxyMode()
|
|
||||||
|
|
||||||
if proxyOption == "all" || proxyOption != "none" && !urllib.IsHTTPS(e.URL) {
|
|
||||||
proxifyAbsoluteURLIfMimeType(e, router)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func proxifyAbsoluteURLIfMimeType(e *Enclosure, router *mux.Router) {
|
|
||||||
for _, mediaType := range config.Opts.MediaProxyResourceTypes() {
|
|
||||||
if strings.HasPrefix(e.MimeType, mediaType+"/") {
|
|
||||||
e.URL = mediaproxy.ProxifyAbsoluteURL(router, e.URL)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,12 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"miniflux.app/v2/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEnclosure_Html5MimeTypeGivesOriginalMimeType(t *testing.T) {
|
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
|
// tested at the time of this commit (06/2023) on latest Firefox & Vivaldi on this feed
|
||||||
// https://www.florenceporcel.com/podcast/lfhdu.xml
|
// https://www.florenceporcel.com/podcast/lfhdu.xml
|
||||||
t.Fatalf(
|
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(),
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue