diff --git a/pkg/download/script.go b/pkg/download/script.go index a852b6db..7e0feaa3 100644 --- a/pkg/download/script.go +++ b/pkg/download/script.go @@ -27,6 +27,10 @@ type ScriptData struct { // ScriptPayload contains the task data type ScriptPayload struct { Task *Task + // TaskName is computed before Task is cloned. The clone has no fetcher + // manager, so for an unresolved task without Options.Name its Name() + // would return "unknown". + TaskName string } // getScriptPaths extracts script paths from config @@ -103,7 +107,7 @@ func (d *Downloader) executeScriptAtPath(scriptPath string, data *ScriptData) er cmd.Env = append(os.Environ(), fmt.Sprintf("GOPEED_EVENT=%s", data.Event), fmt.Sprintf("GOPEED_TASK_ID=%s", data.Payload.Task.ID), - fmt.Sprintf("GOPEED_TASK_NAME=%s", data.Payload.Task.Name()), + fmt.Sprintf("GOPEED_TASK_NAME=%s", data.Payload.TaskName), fmt.Sprintf("GOPEED_TASK_STATUS=%s", data.Payload.Task.Status), ) @@ -137,7 +141,8 @@ func (d *Downloader) triggerScripts(event ScriptEvent, task *Task, err error) { Event: event, Time: time.Now().UnixMilli(), Payload: &ScriptPayload{ - Task: task.clone(), + Task: task.clone(), + TaskName: task.Name(), }, } diff --git a/pkg/download/script_test.go b/pkg/download/script_test.go index c94b9ff1..826a3d95 100644 --- a/pkg/download/script_test.go +++ b/pkg/download/script_test.go @@ -3,6 +3,8 @@ package download import ( "os" "path/filepath" + "runtime" + "strings" "testing" "time" @@ -126,6 +128,46 @@ func TestScript_ExecuteScriptAtPath_NonExistentFile(t *testing.T) { }) } +func TestScript_TaskNameOfUnresolvedTask(t *testing.T) { + scriptPath := getTestScriptPath(t, envDumpScriptName()) + ensureScriptExecutable(t, scriptPath) + outputFile := filepath.Join(t.TempDir(), "env_output.txt") + + setupScriptTest(t, func(downloader *Downloader) { + cfg, _ := downloader.GetConfig() + cfg.Script = &base.ScriptConfig{ + Enable: true, + Paths: []string{scriptPath}, + } + downloader.PutConfig(cfg) + + // Without Options.Name and a resource, the name comes from the fetcher + // manager, which the cloned task in the script data does not have. + url := "http://127.0.0.1/error.bin" + fm, err := downloader.parseFm(url) + if err != nil { + t.Fatal(err) + } + task := NewTask() + task.Meta = &fetcher.FetcherMeta{ + Req: &base.Request{URL: url}, + Opts: &base.Options{Path: t.TempDir()}, + } + task.fetcherManager = fm + if name := task.Name(); name != "error.bin" { + t.Fatalf("Expected task name error.bin, got %q", name) + } + + t.Setenv("GOPEED_TEST_OUTPUT_FILE", outputFile) + downloader.triggerScripts(ScriptEventDownloadDone, task, nil) + + output := waitForFileContains(t, outputFile, "GOPEED_TASK_PATH=", 3*time.Second) + if !strings.Contains(output, "GOPEED_TASK_NAME=error.bin") { + t.Errorf("Expected GOPEED_TASK_NAME=error.bin in output, got: %s", output) + } + }) +} + func createDownloadDoneTask(t *testing.T, downloadDir, fileName string) (*Task, string) { t.Helper() content := []byte("downloaded file") @@ -171,6 +213,31 @@ func waitForFile(t *testing.T, path string, timeout time.Duration) { t.Fatalf("Timeout waiting for file: %s", path) } +// waitForFileContains waits until the file contains want and returns its content. +// Test scripts write the file line by line, so waiting for the file to exist is not enough. +func waitForFileContains(t *testing.T, path, want string, timeout time.Duration) string { + t.Helper() + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + if content, err := os.ReadFile(path); err == nil && strings.Contains(string(content), want) { + return string(content) + } + time.Sleep(50 * time.Millisecond) + } + content, _ := os.ReadFile(path) + t.Fatalf("Timeout waiting for %q in file %s, got: %q", want, path, content) + return "" +} + +// envDumpScriptName returns the test script that writes the GOPEED_* variables +// into the file named by GOPEED_TEST_OUTPUT_FILE. +func envDumpScriptName() string { + if runtime.GOOS == "windows" { + return "env_dump.bat" + } + return "env_dump.sh" +} + func getTestScriptPath(t *testing.T, name string) string { t.Helper() path := filepath.Join("testdata", "scripts", name) diff --git a/pkg/download/script_unix_test.go b/pkg/download/script_unix_test.go index 24ea62ef..39502c70 100644 --- a/pkg/download/script_unix_test.go +++ b/pkg/download/script_unix_test.go @@ -91,13 +91,8 @@ func TestScript_EnvironmentVariables(t *testing.T) { t.Setenv("GOPEED_TEST_OUTPUT_FILE", outputFile) downloader.triggerScripts(ScriptEventDownloadDone, task, nil) - waitForFile(t, outputFile, 3*time.Second) - content, err := os.ReadFile(outputFile) - if err != nil { - t.Fatalf("Failed to read output file: %v", err) - } - - output := string(content) + // GOPEED_TASK_PATH is the last line the script writes. + output := waitForFileContains(t, outputFile, "GOPEED_TASK_PATH=", 3*time.Second) if !strings.Contains(output, "GOPEED_EVENT=DOWNLOAD_DONE") { t.Errorf("Expected GOPEED_EVENT in output, got: %s", output) } diff --git a/pkg/download/script_windows_test.go b/pkg/download/script_windows_test.go index 980f3394..bb72135a 100644 --- a/pkg/download/script_windows_test.go +++ b/pkg/download/script_windows_test.go @@ -87,13 +87,8 @@ func TestScript_EnvironmentVariables(t *testing.T) { t.Setenv("GOPEED_TEST_OUTPUT_FILE", outputFile) downloader.triggerScripts(ScriptEventDownloadDone, task, nil) - waitForFile(t, outputFile, 5*time.Second) - content, err := os.ReadFile(outputFile) - if err != nil { - t.Fatalf("Failed to read output file: %v", err) - } - - output := string(content) + // GOPEED_TASK_PATH is the last line the script writes. + output := waitForFileContains(t, outputFile, "GOPEED_TASK_PATH=", 5*time.Second) if !strings.Contains(output, "GOPEED_EVENT=DOWNLOAD_DONE") { t.Errorf("Expected GOPEED_EVENT in output, got: %s", output) }