| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 |
- package workflow_test
- import (
- "archive/zip"
- "bytes"
- "context"
- "fmt"
- "net/http"
- "net/http/httptest"
- "testing"
- "workflow"
- )
- // ── helpers ──────────────────────────────────────────────────────────────────
- // makeZip builds an in-memory zip archive from a map of filename→content.
- func makeZip(files map[string][]byte) []byte {
- var buf bytes.Buffer
- w := zip.NewWriter(&buf)
- for name, data := range files {
- f, _ := w.Create(name)
- f.Write(data)
- }
- w.Close()
- return buf.Bytes()
- }
- // v314Registry returns a standard registry allowing .tmp/**, artifacts/**,
- // ExtComponents/**, Services/**, and Sections/** writes.
- func v314Registry() workflow.Registry {
- return workflow.Registry{
- Services: []string{},
- Components: []string{},
- Vars: []string{},
- Files: workflow.FilesRegistry{
- Artifacts: []string{
- ".tmp/**",
- "artifacts/**",
- "ExtComponents/**",
- "Services/**",
- "Sections/**",
- },
- },
- }
- }
- // v314Adapters wraps a FileAdapter in the full Adapters struct.
- func v314Adapters(fa workflow.FileAdapter) *workflow.Adapters {
- return &workflow.Adapters{
- Service: workflow.NewDefaultServiceAdapter(),
- Component: workflow.NewDefaultComponentAdapter(),
- LLM: workflow.NewDefaultLLMAdapter(),
- File: fa,
- }
- }
- func mustEngineV314(t *testing.T, wf *workflow.Workflow) *workflow.Engine {
- t.Helper()
- eng, err := workflow.NewEngine(wf)
- if err != nil {
- t.Fatalf("NewEngine: %v", err)
- }
- return eng
- }
- // mustExecuteV314 starts execution and blocks until the workflow finishes.
- func mustExecuteV314(t *testing.T, eng *workflow.Engine, adapters *workflow.Adapters) *workflow.ExecutionResult {
- t.Helper()
- result, err := eng.Execute(context.Background(), map[string]interface{}{}, adapters)
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
- drainEvents(result.RunEventStream) // wait for goroutine to finish
- return result
- }
- // ── Write_* prepend (v3.14) ───────────────────────────────────────────────
- func TestV314_WritePrepend(t *testing.T) {
- fa := workflow.NewDefaultFileAdapter()
- fa.SetFile("artifacts/file.txt", []byte("world"))
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Prepend Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Write_Prepend",
- Target: "artifacts/file.txt",
- Value: "hello ",
- Mode: "prepend",
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- mustExecuteV314(t, mustEngineV314(t, wf), v314Adapters(fa))
- got := string(fa.GetFile("artifacts/file.txt"))
- if want := "hello world"; got != want {
- t.Errorf("prepend: got %q, want %q", got, want)
- }
- }
- // ── .tmp/ path isolation (v3.14) ─────────────────────────────────────────
- func TestV314_TmpPathIsolation(t *testing.T) {
- fa := workflow.NewDefaultFileAdapter()
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Tmp Path Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Write_Tmp",
- Target: ".tmp/bundle.zip",
- Value: "data",
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- result, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
- drainEvents(result.RunEventStream) // wait for workflow to finish
- runID := result.Context.WorkflowID
- expectedPath := ".tmp/" + runID + "/bundle.zip"
- if fa.GetFile(expectedPath) == nil {
- t.Errorf(".tmp/ not isolated: expected file at %s", expectedPath)
- }
- // Bare unresolved path must NOT exist
- if fa.GetFile(".tmp/bundle.zip") != nil {
- t.Errorf(".tmp/ isolation: bare path should not be written")
- }
- }
- // ── _iterDir injection in parallel loop (v3.14) ──────────────────────────
- func TestV314_IterDirParallel(t *testing.T) {
- fa := workflow.NewDefaultFileAdapter()
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "IterDir Parallel Test",
- Registry: workflow.Registry{
- Services: []string{},
- Components: []string{},
- Vars: []string{"$items([STRING])"},
- Files: workflow.FilesRegistry{Artifacts: []string{".tmp/**"}},
- },
- Steps: []workflow.Step{
- {ID: "Set_Items", Target: "$items", Value: `=["a","b","c"]`, Next: "Loop_Process"},
- {
- ID: "Loop_Process",
- Source: "=$items",
- Mode: "parallel",
- Children: []string{"Write_TmpFile"},
- Next: "Stop_Done",
- },
- {ID: "Write_TmpFile", Target: ".tmp/{_iterDir}/item.txt", Value: "=_item", Next: "RETURN"},
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- result, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
- drainEvents(result.RunEventStream) // wait for all parallel goroutines to finish
- runID := result.Context.WorkflowID
- for i, want := range []string{"a", "b", "c"} {
- iterDir := fmt.Sprintf("Loop_Process_%d", i)
- path := ".tmp/" + runID + "/" + iterDir + "/item.txt"
- got := fa.GetFile(path)
- if got == nil {
- t.Errorf("missing file for iteration %d at %s", i, path)
- continue
- }
- if string(got) != want {
- t.Errorf("iteration %d: got %q, want %q", i, string(got), want)
- }
- }
- }
- // ── _iterDir injection in serial loop (v3.14) ────────────────────────────
- func TestV314_IterDirSerial(t *testing.T) {
- fa := workflow.NewDefaultFileAdapter()
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "IterDir Serial Test",
- Registry: workflow.Registry{
- Services: []string{},
- Components: []string{},
- Vars: []string{"$items([STRING])"},
- Files: workflow.FilesRegistry{Artifacts: []string{".tmp/**"}},
- },
- Steps: []workflow.Step{
- {ID: "Set_Items", Target: "$items", Value: `=["x","y"]`, Next: "Loop_Serial"},
- {
- ID: "Loop_Serial",
- Source: "=$items",
- Mode: "serial",
- Children: []string{"Write_TmpFile"},
- Next: "Stop_Done",
- },
- {ID: "Write_TmpFile", Target: ".tmp/{_iterDir}/item.txt", Value: "=_item", Next: "RETURN"},
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- result, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
- drainEvents(result.RunEventStream) // wait for workflow to finish
- runID := result.Context.WorkflowID
- for i, want := range []string{"x", "y"} {
- iterDir := fmt.Sprintf("Loop_Serial_%d", i)
- path := ".tmp/" + runID + "/" + iterDir + "/item.txt"
- got := fa.GetFile(path)
- if got == nil {
- t.Errorf("missing file for serial iteration %d at %s", i, path)
- continue
- }
- if string(got) != want {
- t.Errorf("serial iteration %d: got %q, want %q", i, string(got), want)
- }
- }
- }
- // ── Download_* with explicit target (v3.14) ──────────────────────────────
- func TestV314_DownloadTarget(t *testing.T) {
- content := []byte("file-content-abc")
- srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write(content)
- }))
- defer srv.Close()
- fa := workflow.NewDefaultFileAdapter()
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Download Target Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Download_File",
- Source: fmt.Sprintf("%s/file.cp", srv.URL),
- Target: "artifacts/downloaded.cp",
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- mustExecuteV314(t, mustEngineV314(t, wf), v314Adapters(fa))
- got := fa.GetFile("artifacts/downloaded.cp")
- if !bytes.Equal(got, content) {
- t.Errorf("download content mismatch: got %q, want %q", got, content)
- }
- }
- // ── Download_* with routeByExt (v3.14) ───────────────────────────────────
- func TestV314_DownloadRouteByExt(t *testing.T) {
- content := []byte("component-data")
- srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write(content)
- }))
- defer srv.Close()
- fa := workflow.NewDefaultFileAdapter()
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Download RouteByExt Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Download_Cp",
- Source: fmt.Sprintf("%s/MyWidget.cp", srv.URL),
- RouteByExt: map[string]string{
- ".cp": "ExtComponents/",
- ".vs": "Services/",
- },
- DefaultDir: "artifacts/misc/",
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- mustExecuteV314(t, mustEngineV314(t, wf), v314Adapters(fa))
- got := fa.GetFile("ExtComponents/MyWidget.cp")
- if !bytes.Equal(got, content) {
- t.Errorf("routeByExt content mismatch: got %q, want %q", got, content)
- }
- }
- // ── Unzip_* with routeByExt (v3.14) ──────────────────────────────────────
- func TestV314_UnzipRouteByExt(t *testing.T) {
- zipData := makeZip(map[string][]byte{
- "Widget.cp": []byte("cp-content"),
- "Service.vs": []byte("vs-content"),
- "Readme.txt": []byte("readme"),
- })
- fa := workflow.NewDefaultFileAdapter()
- fa.SetFile("artifacts/bundle.zip", zipData)
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Unzip RouteByExt Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Unzip_Bundle",
- Source: "artifacts/bundle.zip",
- RouteByExt: map[string]string{
- ".cp": "ExtComponents/",
- ".vs": "Services/",
- },
- DefaultDir: "artifacts/misc/",
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- mustExecuteV314(t, mustEngineV314(t, wf), v314Adapters(fa))
- cases := []struct{ path, want string }{
- {"ExtComponents/Widget.cp", "cp-content"},
- {"Services/Service.vs", "vs-content"},
- {"artifacts/misc/Readme.txt", "readme"},
- }
- for _, c := range cases {
- got := string(fa.GetFile(c.path))
- if got != c.want {
- t.Errorf("%s: got %q, want %q", c.path, got, c.want)
- }
- }
- }
- // ── Unzip_* zip-slip protection (v3.14) ──────────────────────────────────
- func TestV314_UnzipZipSlipRejected(t *testing.T) {
- var buf bytes.Buffer
- zw := zip.NewWriter(&buf)
- f, _ := zw.Create("../../etc/passwd")
- f.Write([]byte("root:x:0:0"))
- zw.Close()
- fa := workflow.NewDefaultFileAdapter()
- fa.SetFile("artifacts/evil.zip", buf.Bytes())
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "ZipSlip Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Unzip_Evil",
- Source: "artifacts/evil.zip",
- RouteByExt: map[string]string{},
- DefaultDir: "artifacts/out/",
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- result, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute (start): %v", err)
- }
- drainEvents(result.RunEventStream) // wait for workflow to finish
- if result.Context.Status != workflow.StatusFailed {
- t.Errorf("expected workflow to fail for zip-slip entry, got status %q", result.Context.Status)
- }
- }
- // ── Unzip_* overwrite=false (v3.14) ──────────────────────────────────────
- func TestV314_UnzipNoOverwrite(t *testing.T) {
- zipData := makeZip(map[string][]byte{
- "file.cp": []byte("new-content"),
- })
- fa := workflow.NewDefaultFileAdapter()
- fa.SetFile("artifacts/bundle.zip", zipData)
- fa.SetFile("ExtComponents/file.cp", []byte("original"))
- overwriteFalse := false
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Unzip No Overwrite Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Unzip_Bundle",
- Source: "artifacts/bundle.zip",
- RouteByExt: map[string]string{".cp": "ExtComponents/"},
- Overwrite: &overwriteFalse,
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- result, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute (start): %v", err)
- }
- drainEvents(result.RunEventStream) // wait for workflow to finish
- if result.Context.Status != workflow.StatusFailed {
- t.Errorf("expected workflow to fail when overwrite=false on existing file, got status %q", result.Context.Status)
- }
- if got := string(fa.GetFile("ExtComponents/file.cp")); got != "original" {
- t.Errorf("original file overwritten despite overwrite=false, got %q", got)
- }
- }
- // ── Download_* → Unzip_* full pipeline (v3.14) ───────────────────────────
- func TestV314_DownloadThenUnzip(t *testing.T) {
- zipData := makeZip(map[string][]byte{
- "Alpha.cp": []byte("alpha"),
- "Beta.vs": []byte("beta"),
- })
- srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write(zipData)
- }))
- defer srv.Close()
- fa := workflow.NewDefaultFileAdapter()
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Download+Unzip Pipeline Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Download_Bundle",
- Source: fmt.Sprintf("%s/bundle.zip", srv.URL),
- Target: ".tmp/bundle.zip",
- Next: "Unzip_Bundle",
- },
- {
- ID: "Unzip_Bundle",
- Source: ".tmp/bundle.zip",
- RouteByExt: map[string]string{
- ".cp": "ExtComponents/",
- ".vs": "Services/",
- },
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- result, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
- drainEvents(result.RunEventStream) // wait for workflow to finish
- runID := result.Context.WorkflowID
- tmpZip := ".tmp/" + runID + "/bundle.zip"
- if fa.GetFile(tmpZip) == nil {
- t.Errorf("expected zip at isolated path %s", tmpZip)
- }
- if got := fa.GetFile("ExtComponents/Alpha.cp"); !bytes.Equal(got, []byte("alpha")) {
- t.Errorf("Alpha.cp: got %q, want %q", got, "alpha")
- }
- if got := fa.GetFile("Services/Beta.vs"); !bytes.Equal(got, []byte("beta")) {
- t.Errorf("Beta.vs: got %q, want %q", got, "beta")
- }
- }
- // ── Version 3.14 accepted by validator ───────────────────────────────────
- func TestV314_VersionAccepted(t *testing.T) {
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Version Test",
- Registry: v314Registry(),
- Steps: []workflow.Step{{ID: "Stop_Done"}},
- }
- if err := wf.Validate(); err != nil {
- t.Errorf("version 3.14 should be accepted: %v", err)
- }
- }
- // ── Validation: Download_* missing source ────────────────────────────────
- func TestV314_Validate_DownloadMissingSource(t *testing.T) {
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Validate Download No Source",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {ID: "Download_File", Target: "artifacts/file.txt", Next: "Stop_Done"}, // no Source
- {ID: "Stop_Done"},
- },
- }
- if err := wf.Validate(); err == nil {
- t.Error("expected validation error for Download_* without source, got nil")
- }
- }
- // ── Validation: Download_* missing target and routeByExt ─────────────────
- func TestV314_Validate_DownloadMissingRoute(t *testing.T) {
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Validate Download No Route",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- // Has source but neither target nor routeByExt/defaultDir
- {ID: "Download_File", Source: "http://example.com/file.zip", Next: "Stop_Done"},
- {ID: "Stop_Done"},
- },
- }
- if err := wf.Validate(); err == nil {
- t.Error("expected validation error for Download_* without target/routeByExt, got nil")
- }
- }
- // ── Validation: Download_* both target and routeByExt (mutually exclusive) ─
- func TestV314_Validate_DownloadBothTargetAndRoute(t *testing.T) {
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Validate Download Both Target and Route",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Download_File",
- Source: "http://example.com/file.zip",
- Target: "artifacts/file.zip",
- RouteByExt: map[string]string{".zip": "artifacts/"},
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- if err := wf.Validate(); err == nil {
- t.Error("expected validation error when both target and routeByExt are set, got nil")
- }
- }
- // ── Validation: Unzip_* missing source ───────────────────────────────────
- func TestV314_Validate_UnzipMissingSource(t *testing.T) {
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Validate Unzip No Source",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Unzip_Bundle",
- RouteByExt: map[string]string{".cp": "ExtComponents/"},
- Next: "Stop_Done",
- }, // no Source
- {ID: "Stop_Done"},
- },
- }
- if err := wf.Validate(); err == nil {
- t.Error("expected validation error for Unzip_* without source, got nil")
- }
- }
- // ── Validation: Unzip_* missing routeByExt ───────────────────────────────
- func TestV314_Validate_UnzipMissingRouteByExt(t *testing.T) {
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Validate Unzip No RouteByExt",
- Registry: v314Registry(),
- Steps: []workflow.Step{
- {
- ID: "Unzip_Bundle",
- Source: "artifacts/bundle.zip",
- // RouteByExt not set (nil)
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- if err := wf.Validate(); err == nil {
- t.Error("expected validation error for Unzip_* without routeByExt, got nil")
- }
- }
- // ── Unzip_* out mapping captures extraction summary ──────────────────────
- func TestV314_UnzipOutMapping(t *testing.T) {
- zipData := makeZip(map[string][]byte{
- "A.cp": []byte("a-content"),
- "B.cp": []byte("b-content"),
- })
- fa := workflow.NewDefaultFileAdapter()
- fa.SetFile("artifacts/pkg.zip", zipData)
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Unzip Out Mapping Test",
- Registry: workflow.Registry{
- Services: []string{},
- Components: []string{},
- Vars: []string{"$unzipCount(NUMBER)", "$unzipFiles([STRING])"},
- Files: workflow.FilesRegistry{
- Artifacts: []string{"artifacts/**", "ExtComponents/**"},
- },
- },
- Steps: []workflow.Step{
- {
- ID: "Unzip_Bundle",
- Source: "artifacts/pkg.zip",
- RouteByExt: map[string]string{
- ".cp": "ExtComponents/",
- },
- Out: workflow.StepOutput{
- "$unzipCount": "=_result.count",
- "$unzipFiles": "=_result.files",
- },
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- res, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
- drainEvents(res.RunEventStream)
- // Verify count was mapped (extractedPaths has 2 entries)
- count := res.Context.Variables["$unzipCount"]
- if count == nil {
- t.Error("$unzipCount should be set after out mapping")
- }
- // Verify files list was mapped (non-nil, and the correct count)
- files := res.Context.Variables["$unzipFiles"]
- if files == nil {
- t.Error("$unzipFiles should be set after out mapping")
- }
- }
- // ── Download_* out mapping captures written path ──────────────────────────
- func TestV314_DownloadOutMapping(t *testing.T) {
- srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write([]byte("download-content"))
- }))
- defer srv.Close()
- fa := workflow.NewDefaultFileAdapter()
- wf := &workflow.Workflow{
- Version: "3.14",
- Name: "Download Out Mapping Test",
- Registry: workflow.Registry{
- Services: []string{},
- Components: []string{},
- Vars: []string{"$downloadedPath(STRING)"},
- Files: workflow.FilesRegistry{
- Artifacts: []string{"artifacts/**"},
- },
- },
- Steps: []workflow.Step{
- {
- ID: "Download_File",
- Source: fmt.Sprintf("%s/widget.cp", srv.URL),
- Target: "artifacts/widget.cp",
- Out: workflow.StepOutput{
- "$downloadedPath": "=_result.path",
- },
- Next: "Stop_Done",
- },
- {ID: "Stop_Done"},
- },
- }
- eng := mustEngineV314(t, wf)
- res, err := eng.Execute(context.Background(), map[string]interface{}{}, v314Adapters(fa))
- if err != nil {
- t.Fatalf("Execute: %v", err)
- }
- drainEvents(res.RunEventStream)
- got, ok := res.Context.Variables["$downloadedPath"].(string)
- if !ok || got == "" {
- t.Errorf("$downloadedPath: got %v (%T), want non-empty string",
- res.Context.Variables["$downloadedPath"], res.Context.Variables["$downloadedPath"])
- }
- if got != "artifacts/widget.cp" {
- t.Errorf("$downloadedPath: got %q, want %q", got, "artifacts/widget.cp")
- }
- }
|