From 7f0c29a1a4ff53f8c2bc6703cd50f7f1aff5973e Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Mon, 15 Jul 2024 13:27:38 +1000 Subject: [PATCH] add integration test --- .../controller/dal/fsm_integration_test.go | 24 +++++++++---------- .../sql/database_integration_test.go | 2 +- cmd/ftl/integration_test.go | 2 +- .../compile/compile_integration_test.go | 13 ++++++++++ .../compile/testdata/{ => go}/one/ftl.toml | 0 .../compile/testdata/{ => go}/one/go.mod | 2 +- .../compile/testdata/{ => go}/one/go.sum | 0 .../compile/testdata/{ => go}/one/one.go | 13 +++++++--- .../compile/testdata/{ => go}/two/ftl.toml | 0 .../compile/testdata/{ => go}/two/go.mod | 2 +- .../compile/testdata/{ => go}/two/go.sum | 0 .../compile/testdata/{ => go}/two/two.go | 0 go-runtime/internal/integration_test.go | 2 +- integration/actions.go | 6 +++-- 14 files changed, 44 insertions(+), 22 deletions(-) rename go-runtime/compile/testdata/{ => go}/one/ftl.toml (100%) rename go-runtime/compile/testdata/{ => go}/one/go.mod (97%) rename go-runtime/compile/testdata/{ => go}/one/go.sum (100%) rename go-runtime/compile/testdata/{ => go}/one/one.go (93%) rename go-runtime/compile/testdata/{ => go}/two/ftl.toml (100%) rename go-runtime/compile/testdata/{ => go}/two/go.mod (97%) rename go-runtime/compile/testdata/{ => go}/two/go.sum (100%) rename go-runtime/compile/testdata/{ => go}/two/two.go (100%) diff --git a/backend/controller/dal/fsm_integration_test.go b/backend/controller/dal/fsm_integration_test.go index d78e210615..d52c61c083 100644 --- a/backend/controller/dal/fsm_integration_test.go +++ b/backend/controller/dal/fsm_integration_test.go @@ -26,34 +26,34 @@ func TestFSM(t *testing.T) { in.CopyModule("fsm"), in.Deploy("fsm"), - in.Call("fsm", "sendOne", in.Obj{"instance": "1"}, nil), - in.Call("fsm", "sendOne", in.Obj{"instance": "2"}, nil), + in.Call[in.Obj, in.Obj]("fsm", "sendOne", in.Obj{"instance": "1"}, nil), + in.Call[in.Obj, in.Obj]("fsm", "sendOne", in.Obj{"instance": "2"}, nil), in.FileContains(logFilePath, "start 1"), in.FileContains(logFilePath, "start 2"), fsmInState("1", "running", "fsm.start"), fsmInState("2", "running", "fsm.start"), - in.Call("fsm", "sendOne", in.Obj{"instance": "1"}, nil), + in.Call[in.Obj, in.Obj]("fsm", "sendOne", in.Obj{"instance": "1"}, nil), in.FileContains(logFilePath, "middle 1"), fsmInState("1", "running", "fsm.middle"), - in.Call("fsm", "sendOne", in.Obj{"instance": "1"}, nil), + in.Call[in.Obj, in.Obj]("fsm", "sendOne", in.Obj{"instance": "1"}, nil), in.FileContains(logFilePath, "end 1"), fsmInState("1", "completed", "fsm.end"), - in.Fail(in.Call("fsm", "sendOne", in.Obj{"instance": "1"}, nil), + in.Fail(in.Call[in.Obj, in.Obj]("fsm", "sendOne", in.Obj{"instance": "1"}, nil), "FSM instance 1 is already in state fsm.end"), // Invalid state transition - in.Fail(in.Call("fsm", "sendTwo", in.Obj{"instance": "2"}, nil), + in.Fail(in.Call[in.Obj, in.Obj]("fsm", "sendTwo", in.Obj{"instance": "2"}, nil), "invalid state transition"), - in.Call("fsm", "sendOne", in.Obj{"instance": "2"}, nil), + in.Call[in.Obj, in.Obj]("fsm", "sendOne", in.Obj{"instance": "2"}, nil), in.FileContains(logFilePath, "middle 2"), fsmInState("2", "running", "fsm.middle"), // Invalid state transition - in.Fail(in.Call("fsm", "sendTwo", in.Obj{"instance": "2"}, nil), + in.Fail(in.Call[in.Obj, in.Obj]("fsm", "sendTwo", in.Obj{"instance": "2"}, nil), "invalid state transition"), ) } @@ -86,14 +86,14 @@ func TestFSMRetry(t *testing.T) { in.Build("fsmretry"), in.Deploy("fsmretry"), // start 2 FSM instances - in.Call("fsmretry", "start", in.Obj{"id": "1"}, func(t testing.TB, response in.Obj) {}), - in.Call("fsmretry", "start", in.Obj{"id": "2"}, func(t testing.TB, response in.Obj) {}), + in.Call("fsmretry", "start", in.Obj{"id": "1"}, func(t testing.TB, response any) {}), + in.Call("fsmretry", "start", in.Obj{"id": "2"}, func(t testing.TB, response any) {}), in.Sleep(2*time.Second), // transition the FSM, should fail each time. - in.Call("fsmretry", "startTransitionToTwo", in.Obj{"id": "1"}, func(t testing.TB, response in.Obj) {}), - in.Call("fsmretry", "startTransitionToThree", in.Obj{"id": "2"}, func(t testing.TB, response in.Obj) {}), + in.Call("fsmretry", "startTransitionToTwo", in.Obj{"id": "1"}, func(t testing.TB, response any) {}), + in.Call("fsmretry", "startTransitionToThree", in.Obj{"id": "2"}, func(t testing.TB, response any) {}), in.Sleep(8*time.Second), //5s is longest run of retries diff --git a/backend/controller/sql/database_integration_test.go b/backend/controller/sql/database_integration_test.go index 95996fa55c..57e355beb2 100644 --- a/backend/controller/sql/database_integration_test.go +++ b/backend/controller/sql/database_integration_test.go @@ -15,7 +15,7 @@ func TestDatabase(t *testing.T) { in.CopyModule("database"), in.CreateDBAction("database", "testdb", false), in.Deploy("database"), - in.Call("database", "insert", in.Obj{"data": "hello"}, nil), + in.Call[in.Obj, in.Obj]("database", "insert", in.Obj{"data": "hello"}, nil), in.QueryRow("testdb", "SELECT data FROM requests", "hello"), // run tests which should only affect "testdb_test" diff --git a/cmd/ftl/integration_test.go b/cmd/ftl/integration_test.go index 3789c89a8f..d8319535ac 100644 --- a/cmd/ftl/integration_test.go +++ b/cmd/ftl/integration_test.go @@ -28,7 +28,7 @@ func TestBox(t *testing.T) { CopyModule("echo"), Exec("ftl", "box", "echo", "--compose=echo-compose.yml"), Exec("docker", "compose", "-f", "echo-compose.yml", "up", "--wait"), - Call("echo", "echo", Obj{"name": "Alice"}, nil), + Call[Obj, Obj]("echo", "echo", Obj{"name": "Alice"}, nil), Exec("docker", "compose", "-f", "echo-compose.yml", "down", "--rmi", "local"), ) } diff --git a/go-runtime/compile/compile_integration_test.go b/go-runtime/compile/compile_integration_test.go index 35102a56a7..567eca4cc3 100644 --- a/go-runtime/compile/compile_integration_test.go +++ b/go-runtime/compile/compile_integration_test.go @@ -4,6 +4,7 @@ package compile_test import ( "testing" + "time" "github.com/alecthomas/assert/v2" @@ -47,3 +48,15 @@ func TestNonFTLTypes(t *testing.T) { }), ) } + +func TestNonStructRequestResponse(t *testing.T) { + in.Run(t, "", + in.CopyModule("two"), + in.Deploy("two"), + in.CopyModule("one"), + in.Deploy("one"), + in.Call("one", "stringToTime", "1985-04-12T23:20:50.52Z", func(t testing.TB, response time.Time) { + assert.Equal(t, time.Date(1985, 04, 12, 23, 20, 50, 520_000_000, time.UTC), response) + }), + ) +} diff --git a/go-runtime/compile/testdata/one/ftl.toml b/go-runtime/compile/testdata/go/one/ftl.toml similarity index 100% rename from go-runtime/compile/testdata/one/ftl.toml rename to go-runtime/compile/testdata/go/one/ftl.toml diff --git a/go-runtime/compile/testdata/one/go.mod b/go-runtime/compile/testdata/go/one/go.mod similarity index 97% rename from go-runtime/compile/testdata/one/go.mod rename to go-runtime/compile/testdata/go/one/go.mod index 0bfce4765a..2e08588b16 100644 --- a/go-runtime/compile/testdata/one/go.mod +++ b/go-runtime/compile/testdata/go/one/go.mod @@ -2,7 +2,7 @@ module ftl/one go 1.22.2 -replace github.com/TBD54566975/ftl => ../../../.. +replace github.com/TBD54566975/ftl => ../../../../.. require github.com/TBD54566975/ftl v0.150.3 diff --git a/go-runtime/compile/testdata/one/go.sum b/go-runtime/compile/testdata/go/one/go.sum similarity index 100% rename from go-runtime/compile/testdata/one/go.sum rename to go-runtime/compile/testdata/go/one/go.sum diff --git a/go-runtime/compile/testdata/one/one.go b/go-runtime/compile/testdata/go/one/one.go similarity index 93% rename from go-runtime/compile/testdata/one/one.go rename to go-runtime/compile/testdata/go/one/one.go index 4b338506a8..9195a76633 100644 --- a/go-runtime/compile/testdata/one/one.go +++ b/go-runtime/compile/testdata/go/one/one.go @@ -2,7 +2,6 @@ package one import ( "context" - "errors" "time" "ftl/builtin" @@ -186,10 +185,18 @@ func (NonFTLStruct) NonFTLInterface() {} //ftl:verb func StringToTime(ctx context.Context, input string) (time.Time, error) { - return time.Time{}, errors.New("not implemented") + return time.Parse(time.RFC3339, input) } //ftl:verb func BatchStringToTime(ctx context.Context, input []string) ([]time.Time, error) { - return nil, errors.New("not implemented") + var output = []time.Time{} + for _, s := range input { + t, err := time.Parse(time.RFC3339, s) + if err != nil { + return nil, err + } + output = append(output, t) + } + return output, nil } diff --git a/go-runtime/compile/testdata/two/ftl.toml b/go-runtime/compile/testdata/go/two/ftl.toml similarity index 100% rename from go-runtime/compile/testdata/two/ftl.toml rename to go-runtime/compile/testdata/go/two/ftl.toml diff --git a/go-runtime/compile/testdata/two/go.mod b/go-runtime/compile/testdata/go/two/go.mod similarity index 97% rename from go-runtime/compile/testdata/two/go.mod rename to go-runtime/compile/testdata/go/two/go.mod index 4d067ca5fb..759f910538 100644 --- a/go-runtime/compile/testdata/two/go.mod +++ b/go-runtime/compile/testdata/go/two/go.mod @@ -2,7 +2,7 @@ module ftl/two go 1.22.2 -replace github.com/TBD54566975/ftl => ../../../.. +replace github.com/TBD54566975/ftl => ../../../../.. require github.com/TBD54566975/ftl v0.150.3 diff --git a/go-runtime/compile/testdata/two/go.sum b/go-runtime/compile/testdata/go/two/go.sum similarity index 100% rename from go-runtime/compile/testdata/two/go.sum rename to go-runtime/compile/testdata/go/two/go.sum diff --git a/go-runtime/compile/testdata/two/two.go b/go-runtime/compile/testdata/go/two/two.go similarity index 100% rename from go-runtime/compile/testdata/two/two.go rename to go-runtime/compile/testdata/go/two/two.go diff --git a/go-runtime/internal/integration_test.go b/go-runtime/internal/integration_test.go index 0736266cb2..9c7f07b566 100644 --- a/go-runtime/internal/integration_test.go +++ b/go-runtime/internal/integration_test.go @@ -20,7 +20,7 @@ func TestRealMap(t *testing.T) { Call("mapper", "get", Obj{}, func(t testing.TB, response Obj) { assert.Equal(t, Obj{"underlyingCounter": 2.0, "mapCounter": 1.0, "mapped": "0"}, response) }), - Call("mapper", "inc", Obj{}, nil), + Call[Obj, Obj]("mapper", "inc", Obj{}, nil), Call("mapper", "get", Obj{}, func(t testing.TB, response Obj) { assert.Equal(t, Obj{"underlyingCounter": 3.0, "mapCounter": 2.0, "mapped": "1"}, response) }), diff --git a/integration/actions.go b/integration/actions.go index 772ebda325..c1be523edc 100644 --- a/integration/actions.go +++ b/integration/actions.go @@ -15,6 +15,7 @@ import ( "strings" "testing" "time" + "unicode" "connectrpc.com/connect" "github.com/alecthomas/assert/v2" @@ -284,9 +285,10 @@ type Obj map[string]any // Call a verb. // // "check" may be nil -func Call(module, verb string, request Obj, check func(t testing.TB, response Obj)) Action { +func Call[Req any, Resp any](module, verb string, request Req, check func(t testing.TB, response Resp)) Action { return func(t testing.TB, ic TestContext) { Infof("Calling %s.%s", module, verb) + assert.False(t, unicode.IsUpper([]rune(verb)[0]), "verb %q must start with an lowercase letter", verb) data, err := json.Marshal(request) assert.NoError(t, err) resp, err := ic.Verbs.Call(ic, connect.NewRequest(&ftlv1.CallRequest{ @@ -294,7 +296,7 @@ func Call(module, verb string, request Obj, check func(t testing.TB, response Ob Body: data, })) assert.NoError(t, err) - var response Obj + var response Resp assert.Zero(t, resp.Msg.GetError(), "verb failed: %s", resp.Msg.GetError().GetMessage()) err = json.Unmarshal(resp.Msg.GetBody(), &response) assert.NoError(t, err)