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", } }