Implement resumable download queue (T-301)
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package downloader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTaskValidateAndDerivedPaths(t *testing.T) {
|
||||
task := validTaskForTest()
|
||||
if err := task.Validate(); err != nil {
|
||||
t.Fatalf("Validate() error = %v", err)
|
||||
}
|
||||
paths, err := DeriveTaskPaths(filepath.Join(t.TempDir(), "downloads"), task.RequestID)
|
||||
if err != nil {
|
||||
t.Fatalf("DeriveTaskPaths() error = %v", err)
|
||||
}
|
||||
for _, path := range []string{
|
||||
paths.Metadata,
|
||||
paths.Backup,
|
||||
paths.Part,
|
||||
paths.Completed,
|
||||
} {
|
||||
relative, err := filepath.Rel(paths.Root, path)
|
||||
if err != nil || relative == ".." {
|
||||
t.Fatalf("derived path escapes root: %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskRejectsInvalidFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mutate func(*Task)
|
||||
}{
|
||||
{name: "request id", mutate: func(task *Task) { task.RequestID = "../escape" }},
|
||||
{name: "app id", mutate: func(task *Task) { task.AppID = "Bad App" }},
|
||||
{name: "URL", mutate: func(task *Task) { task.URL = "http://example.invalid/app.zip" }},
|
||||
{name: "state", mutate: func(task *Task) { task.Status = "unknown" }},
|
||||
{name: "negative done", mutate: func(task *Task) { task.Done = -1 }},
|
||||
{name: "done exceeds total", mutate: func(task *Task) { task.Done = task.Total + 1 }},
|
||||
{name: "unknown total value", mutate: func(task *Task) {
|
||||
task.TotalKnown = false
|
||||
task.Total = 10
|
||||
}},
|
||||
{name: "weak etag", mutate: func(task *Task) {
|
||||
task.Validator = EntityValidator{ETag: `W/"weak"`}
|
||||
}},
|
||||
{name: "failed without code", mutate: func(task *Task) {
|
||||
task.Status = StatusFailed
|
||||
task.ErrorCode = ""
|
||||
}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
task := validTaskForTest()
|
||||
test.mutate(&task)
|
||||
if err := task.Validate(); !errors.Is(err, ErrInvalidTask) {
|
||||
t.Fatalf("Validate() error = %v, want %v", err, ErrInvalidTask)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatorsMatchRejectsDifferentKinds(t *testing.T) {
|
||||
expected := EntityValidator{ETag: `"v1"`}
|
||||
if validatorsMatch(expected, EntityValidator{LastModified: "Wed, 16 Jul 2026 10:00:00 GMT"}) {
|
||||
t.Fatal("ETag unexpectedly matched Last-Modified")
|
||||
}
|
||||
if validatorsMatch(expected, EntityValidator{ETag: `"v2"`}) {
|
||||
t.Fatal("different ETags matched")
|
||||
}
|
||||
if !validatorsMatch(expected, EntityValidator{}) {
|
||||
t.Fatal("absent response validator should not contradict If-Range")
|
||||
}
|
||||
}
|
||||
|
||||
func validTaskForTest() Task {
|
||||
return Task{
|
||||
SchemaVersion: TaskSchemaVersion,
|
||||
RequestID: "request-001",
|
||||
AppID: "json-parser",
|
||||
URL: "https://download.invalid/json-parser.zip",
|
||||
Status: StatusQueued,
|
||||
TotalKnown: true,
|
||||
Total: 42,
|
||||
CreatedAt: "2026-07-16T00:00:00Z",
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user