246 lines
6.0 KiB
Go
246 lines
6.0 KiB
Go
package media
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestFormatDuration(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
seconds int
|
|
expected string
|
|
}{
|
|
{"zero", 0, "00:00:00"},
|
|
{"one second", 1, "00:00:01"},
|
|
{"one minute", 60, "00:01:00"},
|
|
{"one hour", 3600, "01:00:00"},
|
|
{"mixed", 3661, "01:01:01"},
|
|
{"large", 86399, "23:59:59"},
|
|
{"negative", -5, "00:00:00"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := FormatDuration(tt.seconds); got != tt.expected {
|
|
t.Errorf("FormatDuration(%d) = %q, want %q", tt.seconds, got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsAllowedExtension(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
expected bool
|
|
}{
|
|
{"video.mp4", true},
|
|
{"video.mov", true},
|
|
{"video.mkv", true},
|
|
{"video.webm", true},
|
|
{"video.m4v", true},
|
|
{"video.ts", true},
|
|
{"video.MP4", true},
|
|
{"video.avi", false},
|
|
{"video.flv", false},
|
|
{"video.wmv", false},
|
|
{"video", false},
|
|
{"", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := IsAllowedExtension(tt.name); got != tt.expected {
|
|
t.Errorf("IsAllowedExtension(%q) = %v, want %v", tt.name, got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsAllowedMime(t *testing.T) {
|
|
tests := []struct {
|
|
mime string
|
|
expected bool
|
|
}{
|
|
{"video/mp4", true},
|
|
{"video/quicktime", true},
|
|
{"video/x-matroska", true},
|
|
{"video/webm", true},
|
|
{"application/octet-stream", true},
|
|
{"", true},
|
|
{"image/jpeg", false},
|
|
{"audio/mpeg", false},
|
|
{"text/plain", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.mime, func(t *testing.T) {
|
|
if got := IsAllowedMime(tt.mime); got != tt.expected {
|
|
t.Errorf("IsAllowedMime(%q) = %v, want %v", tt.mime, got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateUploadID(t *testing.T) {
|
|
tests := []struct {
|
|
id string
|
|
expected bool
|
|
}{
|
|
{"abcdef0123456789", true}, // 16 hex chars
|
|
{"abcdef0123456789abcdef0123456789", true}, // 32 hex chars
|
|
{"abc", false}, // too short
|
|
{"abcdef012345678", false}, // 15 chars (too short)
|
|
{"abcdef0123456789g", false}, // non-hex char
|
|
{"ABCDEF0123456789", false}, // uppercase (not allowed)
|
|
{"", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.id, func(t *testing.T) {
|
|
if got := ValidateUploadID(tt.id); got != tt.expected {
|
|
t.Errorf("ValidateUploadID(%q) = %v, want %v", tt.id, got, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParsePlaylist(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
content string
|
|
expected int
|
|
first string
|
|
}{
|
|
{
|
|
name: "simple",
|
|
content: "/videos/video1.mp4\n/videos/video2.mp4\n",
|
|
expected: 2,
|
|
first: "/videos/video1.mp4",
|
|
},
|
|
{
|
|
name: "with file prefix",
|
|
content: "file /videos/video1.mp4\nfile '/videos/video2.mp4'\n",
|
|
expected: 2,
|
|
first: "/videos/video1.mp4",
|
|
},
|
|
{
|
|
name: "with comments and blanks",
|
|
content: "# comment\n\n/videos/video1.mp4\n\n# another comment\n/videos/video2.mp4\n",
|
|
expected: 2,
|
|
first: "/videos/video1.mp4",
|
|
},
|
|
{
|
|
name: "empty",
|
|
content: "",
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "only comments",
|
|
content: "# comment\n# another\n",
|
|
expected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
entries := ParsePlaylist(tt.content)
|
|
if len(entries) != tt.expected {
|
|
t.Errorf("ParsePlaylist() len = %d, want %d", len(entries), tt.expected)
|
|
}
|
|
if tt.expected > 0 && entries[0].Path != tt.first {
|
|
t.Errorf("ParsePlaylist()[0].Path = %q, want %q", entries[0].Path, tt.first)
|
|
}
|
|
// Check indices
|
|
for i, e := range entries {
|
|
if e.Index != i+1 {
|
|
t.Errorf("ParsePlaylist()[%d].Index = %d, want %d", i, e.Index, i+1)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetMediaIDFromPath(t *testing.T) {
|
|
tests := []struct {
|
|
path string
|
|
prefix string
|
|
expected uint
|
|
expectErr bool
|
|
}{
|
|
{"/api/admin/media/123", "/api/admin/media/", 123, false},
|
|
{"/api/admin/media/1", "/api/admin/media/", 1, false},
|
|
{"/api/admin/media/", "/api/admin/media/", 0, true},
|
|
{"/api/admin/media/abc", "/api/admin/media/", 0, true},
|
|
{"/api/admin/media/0", "/api/admin/media/", 0, true},
|
|
{"/other/path", "/api/admin/media/", 0, true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.path, func(t *testing.T) {
|
|
id, err := GetMediaIDFromPath(tt.path, tt.prefix)
|
|
if tt.expectErr && err == nil {
|
|
t.Errorf("GetMediaIDFromPath(%q) expected error, got nil", tt.path)
|
|
}
|
|
if !tt.expectErr && err != nil {
|
|
t.Errorf("GetMediaIDFromPath(%q) error = %v", tt.path, err)
|
|
}
|
|
if id != tt.expected {
|
|
t.Errorf("GetMediaIDFromPath(%q) = %d, want %d", tt.path, id, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUniqueTargetPath(t *testing.T) {
|
|
// Test that the function returns a path with the mediaDir prefix
|
|
// and the correct filename. We can't easily test collision behavior
|
|
// without filesystem setup, but we can verify the basic path.
|
|
expected := filepath.Join("/tmp/media", "video.mp4")
|
|
path := UniqueTargetPath("/tmp/media", "video.mp4")
|
|
if path != expected {
|
|
t.Errorf("UniqueTargetPath() = %q, want %q", path, expected)
|
|
}
|
|
}
|
|
|
|
func TestNormalizationArgs(t *testing.T) {
|
|
args := normalizationArgs("/in/movie.mov", "/out/movie.normalized.mp4")
|
|
joined := strings.Join(args, " ")
|
|
for _, want := range []string{
|
|
"-map 0:v:0",
|
|
"-map 0:a:0?",
|
|
"-c:v libx264",
|
|
"-r 30",
|
|
"-g 60",
|
|
"-keyint_min 60",
|
|
"-pix_fmt yuv420p",
|
|
"-c:a aac",
|
|
"-ar 48000",
|
|
"-ac 2",
|
|
"-avoid_negative_ts make_zero",
|
|
"/out/movie.normalized.mp4",
|
|
} {
|
|
if !strings.Contains(joined, want) {
|
|
t.Errorf("normalization args missing %q: %v", want, args)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizeVideo_FFmpegError(t *testing.T) {
|
|
origRun := runCommand
|
|
t.Cleanup(func() { runCommand = origRun })
|
|
|
|
runCommand = func(ctx context.Context, name string, args ...string) error {
|
|
if name != "ffmpeg" {
|
|
t.Errorf("runCommand name = %q, want ffmpeg", name)
|
|
}
|
|
return fmt.Errorf("simulated ffmpeg failure")
|
|
}
|
|
|
|
if _, _, err := normalizeVideo("/in/a.mov", "/out/a.normalized.mp4"); err == nil {
|
|
t.Fatal("expected error when ffmpeg fails")
|
|
}
|
|
} |