test(uploads): validation policy -- video, no-audio, too-long, unreadable (red)
Probe-based, not extension-based: a Teams voice-only export is typically an .mp4 with no video stream, indistinguishable from video by name (ADR-033). Red: package uploads does not exist. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package uploads_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"git.d-ma.be/mathias/tapir/internal/uploads"
|
||||
)
|
||||
|
||||
var testLimits = uploads.Limits{MaxBytes: 200 << 20, MaxDuration: 90 * time.Minute}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
probe uploads.Probe
|
||||
wantReason uploads.Reason // "" = accepted
|
||||
wantInMsg []string
|
||||
}{
|
||||
{
|
||||
name: "audio-only file within the duration limit is accepted",
|
||||
probe: uploads.Probe{Duration: 45 * time.Minute, HasAudio: true},
|
||||
},
|
||||
{
|
||||
name: "a recording exactly at the limit is accepted",
|
||||
probe: uploads.Probe{Duration: 90 * time.Minute, HasAudio: true},
|
||||
},
|
||||
{
|
||||
name: "a file with a video stream is rejected and names issue 28",
|
||||
probe: uploads.Probe{Duration: 4 * time.Minute, HasAudio: true, HasVideo: true},
|
||||
wantReason: uploads.ReasonVideo,
|
||||
wantInMsg: []string{"video", "#28"},
|
||||
},
|
||||
{
|
||||
name: "video with no audio is still rejected as video, not as missing audio",
|
||||
probe: uploads.Probe{Duration: 4 * time.Minute, HasVideo: true},
|
||||
wantReason: uploads.ReasonVideo,
|
||||
wantInMsg: []string{"#28"},
|
||||
},
|
||||
{
|
||||
name: "a file with no audio stream is rejected",
|
||||
probe: uploads.Probe{Duration: 4 * time.Minute},
|
||||
wantReason: uploads.ReasonNoAudio,
|
||||
wantInMsg: []string{"no audio"},
|
||||
},
|
||||
{
|
||||
name: "a recording over the limit is rejected, stating both lengths",
|
||||
probe: uploads.Probe{Duration: 102 * time.Minute, HasAudio: true},
|
||||
wantReason: uploads.ReasonTooLong,
|
||||
wantInMsg: []string{"1 h 42 min", "1 h 30 min"},
|
||||
},
|
||||
{
|
||||
name: "zero duration is unreadable, not accepted as an empty recording",
|
||||
probe: uploads.Probe{HasAudio: true},
|
||||
wantReason: uploads.ReasonUnreadable,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := uploads.Validate(tt.probe, testLimits)
|
||||
|
||||
if tt.wantReason == "" {
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
var rej *uploads.RejectError
|
||||
require.True(t, errors.As(err, &rej), "want a *RejectError, got %v", err)
|
||||
assert.Equal(t, tt.wantReason, rej.Reason)
|
||||
for _, s := range tt.wantInMsg {
|
||||
assert.Contains(t, rej.Message, s)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatDuration(t *testing.T) {
|
||||
tests := []struct {
|
||||
d time.Duration
|
||||
want string
|
||||
}{
|
||||
{90 * time.Minute, "1 h 30 min"},
|
||||
{102 * time.Minute, "1 h 42 min"},
|
||||
{45 * time.Minute, "45 min"},
|
||||
{2 * time.Hour, "2 h"},
|
||||
{30 * time.Second, "30 s"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, uploads.FormatDuration(tt.d))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user