Skip to content

Commit

Permalink
add test
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Nov 18, 2024
1 parent 755532a commit 2edf119
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 5 deletions.
37 changes: 37 additions & 0 deletions internal/integration/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,31 @@ func Wait(module string) Action {
}
}

// WaitWithTimeout for the given module to deploy.
func WaitWithTimeout(module string, timeout time.Duration) Action {
return func(t testing.TB, ic TestContext) {
Infof("Waiting for %s to become ready", module)
deadline := time.After(timeout)
tick := time.NewTicker(time.Millisecond * 100)
defer tick.Stop()
for {
select {
case <-deadline:
t.Fatalf("deployment of module %q not found", module)
return
case <-tick.C:
status, err := ic.Controller.Status(ic, connect.NewRequest(&ftlv1.StatusRequest{}))
assert.NoError(t, err)
for _, deployment := range status.Msg.Deployments {
if deployment.Name == module {
return
}
}
}
}
}
}

func Sleep(duration time.Duration) Action {
return func(t testing.TB, ic TestContext) {
Infof("Sleeping for %s", duration)
Expand Down Expand Up @@ -414,6 +439,18 @@ func VerifySchema(check func(ctx context.Context, t testing.TB, sch *schemapb.Sc
}
}

// VerifyControllerStatus lets you test the current controller status
func VerifyControllerStatus(check func(ctx context.Context, t testing.TB, status *ftlv1.StatusResponse)) Action {
return func(t testing.TB, ic TestContext) {
sch, err := ic.Controller.Status(ic, connect.NewRequest(&ftlv1.StatusRequest{}))
if err != nil {
t.Errorf("failed to get schema: %v", err)
return
}
check(ic.Context, t, sch.Msg)
}
}

// VerifySchemaVerb lets you test the current schema for a specific verb
func VerifySchemaVerb(module string, verb string, check func(ctx context.Context, t testing.TB, schema *schemapb.Schema, verb *schemapb.Verb)) Action {
return func(t testing.TB, ic TestContext) {
Expand Down
24 changes: 21 additions & 3 deletions internal/integration/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,19 @@ func WithProvisionerConfig(config string) Option {
}
}

// WithDevMode starts the server using FTL dev, so modules are deployed automatically on change
func WithDevMode() Option {
return func(o *options) {
o.devMode = true
}
}

type options struct {
languages []string
testDataDir string
ftlConfigPath string
startController bool
devMode bool
startProvisioner bool
provisionerConfig string
requireJava bool
Expand Down Expand Up @@ -295,7 +303,12 @@ func run(t *testing.T, actionsOrOptions ...ActionOrOption) {
if opts.startController {
Infof("Starting ftl cluster")

args := []string{filepath.Join(binDir, "ftl"), "serve", "--recreate"}
command := "serve"
if opts.devMode {
command = "dev"
}

args := []string{filepath.Join(binDir, "ftl"), command, "--recreate", "--log-level=DEBUG"}
if !opts.console {
args = append(args, "--no-console")
}
Expand All @@ -308,7 +321,7 @@ func run(t *testing.T, actionsOrOptions ...ActionOrOption) {
args = append(args, "--provisioner-plugin-config="+configFile)
}
}
ctx = startProcess(ctx, t, args...)
ctx = startProcess(ctx, t, tmpDir, opts.devMode, args...)
}
if opts.startController || opts.kube {
controller = rpc.Dial(ftlv1connect.NewControllerServiceClient, "http://localhost:8892", log.Debug)
Expand All @@ -333,6 +346,7 @@ func run(t *testing.T, actionsOrOptions ...ActionOrOption) {
realT: t,
Language: language,
kubeNamespace: kubeNamespace,
devMode: opts.devMode,
kubeClient: optional.Ptr(kubeClient),
}
defer dumpKubePods(ctx, ic.kubeClient, ic.kubeNamespace)
Expand Down Expand Up @@ -412,6 +426,7 @@ type TestContext struct {
// Set if the test is running on kubernetes
kubeClient optional.Option[kubernetes.Clientset]
kubeNamespace string
devMode bool

Controller ftlv1connect.ControllerServiceClient
Provisioner provisionerconnect.ProvisionerServiceClient
Expand Down Expand Up @@ -497,10 +512,13 @@ func (l *logWriter) Write(p []byte) (n int, err error) {
}

// startProcess runs a binary in the background and terminates it when the test completes.
func startProcess(ctx context.Context, t testing.TB, args ...string) context.Context {
func startProcess(ctx context.Context, t testing.TB, tempDir string, devMode bool, args ...string) context.Context {
t.Helper()
ctx, cancel := context.WithCancel(ctx)
cmd := ftlexec.Command(ctx, log.Debug, "..", args[0], args[1:]...)
if devMode {
cmd.Dir = tempDir
}
err := cmd.Start()
assert.NoError(t, err)
terminated := make(chan bool)
Expand Down
4 changes: 4 additions & 0 deletions jvm-runtime/ftl-runtime/common/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-development-mode-spi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-grpc</artifactId>
Expand Down
40 changes: 39 additions & 1 deletion jvm-runtime/jvm_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,62 @@ import (

"github.com/alecthomas/repr"

ftlv1 "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1"
schemapb "github.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/v1/schema"
"github.com/TBD54566975/ftl/go-runtime/ftl"
in "github.com/TBD54566975/ftl/internal/integration"
"github.com/TBD54566975/ftl/internal/schema"
)

func TestLifecycleJVM(t *testing.T) {
deployment := ""
in.Run(t,
in.WithLanguages("java", "kotlin"),
in.WithDevMode(),
in.GitInit(),
in.Exec("rm", "ftl-project.toml"),
in.Exec("ftl", "init", "test", "."),
in.IfLanguage("java", in.Exec("ftl", "new", "java", ".", "echo")),
in.IfLanguage("kotlin", in.Exec("ftl", "new", "kotlin", ".", "echo")),
in.Deploy("echo"),
in.WaitWithTimeout("echo", time.Minute),
in.VerifyControllerStatus(func(ctx context.Context, t testing.TB, status *ftlv1.StatusResponse) {
assert.Equal(t, 1, len(status.Deployments))
deployment = status.Deployments[0].Key
}),
in.Call("echo", "echo", "Bob", func(t testing.TB, response string) {
assert.Equal(t, "Hello, Bob!", response)
}),
// Now test hot reload
in.IfLanguage("java", in.EditFile("echo", func(content []byte) []byte {
return []byte(strings.ReplaceAll(string(content), "Hello", "Bye"))
}, "src/main/java/com/example/EchoVerb.java")),
in.IfLanguage("kotlin", in.EditFile("echo", func(content []byte) []byte {
return []byte(strings.ReplaceAll(string(content), "Hello", "Bye"))
}, "src/main/kotlin/com/example/EchoVerb.kt")),
in.Call("echo", "echo", "Bob", func(t testing.TB, response string) {
assert.Equal(t, "Bye, Bob!", response)
}),
in.VerifyControllerStatus(func(ctx context.Context, t testing.TB, status *ftlv1.StatusResponse) {
// Non structurally changing edits should not trigger a new deployment.
t.Logf("status %v", status)
assert.Equal(t, 1, len(status.Deployments))
assert.Equal(t, deployment, status.Deployments[0].Key)
}),
// Structural change should result in a new deployment
in.IfLanguage("java", in.EditFile("echo", func(content []byte) []byte {
return []byte(strings.ReplaceAll(string(content), "@Export", ""))
}, "src/main/java/com/example/EchoVerb.java")),
in.IfLanguage("kotlin", in.EditFile("echo", func(content []byte) []byte {
return []byte(strings.ReplaceAll(string(content), "@Export", ""))
}, "src/main/kotlin/com/example/EchoVerb.kt")),
in.Call("echo", "echo", "Bob", func(t testing.TB, response string) {
assert.Equal(t, "Bye, Bob!", response)
}),
in.VerifyControllerStatus(func(ctx context.Context, t testing.TB, status *ftlv1.StatusResponse) {
// Non structurally changing edits should not trigger a new deployment.
assert.Equal(t, 1, len(status.Deployments))
assert.NotEqual(t, deployment, status.Deployments[0].Key)
}),
)
}

Expand Down
4 changes: 3 additions & 1 deletion jvm-runtime/plugin/common/jvmcommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ func (s *Service) handleDevModeRequest(ctx context.Context, req *connect.Request

schemaChangeTicker := time.NewTicker(100 * time.Millisecond)
forceReloadTicker := time.NewTicker(time.Second)
defer schemaChangeTicker.Stop()
defer forceReloadTicker.Stop()
for {
select {
case <-ctx.Done():
Expand Down Expand Up @@ -378,7 +380,7 @@ func build(ctx context.Context, bctx buildContext, autoRebuild bool) (*langpb.Bu
}
logger.Infof("Using build command '%s'", config.Build)
command := exec.Command(ctx, log.Debug, config.Dir, "bash", "-c", config.Build)
err = command.RunBuffered(ctx)
err = command.Run()
if err != nil {
return &langpb.BuildEvent{Event: &langpb.BuildEvent_BuildFailure{&langpb.BuildFailure{
IsAutomaticRebuild: autoRebuild,
Expand Down

0 comments on commit 2edf119

Please sign in to comment.