diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/client/WorkflowStubExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/client/WorkflowStubExtTest.kt new file mode 100644 index 0000000000..1b6d09554c --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/client/WorkflowStubExtTest.kt @@ -0,0 +1,57 @@ +package io.temporal.client + +import io.temporal.common.converter.DefaultDataConverter +import io.temporal.common.converter.JacksonJsonPayloadConverter +import io.temporal.common.converter.KotlinObjectMapperFactory +import io.temporal.testing.internal.SDKTestWorkflowRule +import io.temporal.workflow.WorkflowInterface +import io.temporal.workflow.WorkflowMethod +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test + +class WorkflowStubExtTest { + + @Rule + @JvmField + val testWorkflowRule: SDKTestWorkflowRule = SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(TestWorkflowImpl::class.java) + .setWorkflowClientOptions( + WorkflowClientOptions { + setDataConverter(DefaultDataConverter(JacksonJsonPayloadConverter(KotlinObjectMapperFactory.new()))) + } + ) + .build() + + @WorkflowInterface + interface TestWorkflow { + @WorkflowMethod + fun execute(input: String): String + } + + class TestWorkflowImpl : TestWorkflow { + override fun execute(input: String) = "result:$input" + } + + @Test + fun `getResult reified extension should return typed result`() { + val client = testWorkflowRule.workflowClient + val stub = client.newUntypedWorkflowStub( + "TestWorkflow", + WorkflowOptions { setTaskQueue(testWorkflowRule.taskQueue) } + ) + stub.start("hello") + assertEquals("result:hello", stub.getResult()) + } + + @Test + fun `getResultAsync reified extension should return typed result via CompletableFuture`() { + val client = testWorkflowRule.workflowClient + val stub = client.newUntypedWorkflowStub( + "TestWorkflow", + WorkflowOptions { setTaskQueue(testWorkflowRule.taskQueue) } + ) + stub.start("async") + assertEquals("result:async", stub.getResultAsync().get()) + } +} diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/serviceclient/RpcRetryOptionsExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/serviceclient/RpcRetryOptionsExtTest.kt new file mode 100644 index 0000000000..c2db4e62d2 --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/serviceclient/RpcRetryOptionsExtTest.kt @@ -0,0 +1,51 @@ +package io.temporal.serviceclient + +import org.junit.Assert.assertEquals +import org.junit.Test +import java.time.Duration + +class RpcRetryOptionsExtTest { + + @Test + fun `RpcRetryOptions DSL should be equivalent to builder`() { + val dslOptions = RpcRetryOptions { + setInitialInterval(Duration.ofMillis(100)) + setMaximumInterval(Duration.ofSeconds(1)) + setBackoffCoefficient(1.5) + setMaximumAttempts(5) + } + + val builderOptions = RpcRetryOptions.newBuilder() + .setInitialInterval(Duration.ofMillis(100)) + .setMaximumInterval(Duration.ofSeconds(1)) + .setBackoffCoefficient(1.5) + .setMaximumAttempts(5) + .build() + + assertEquals(builderOptions, dslOptions) + } + + @Test + fun `RpcRetryOptions copy() DSL should merge override options`() { + val sourceOptions = RpcRetryOptions { + setInitialInterval(Duration.ofMillis(100)) + setMaximumInterval(Duration.ofSeconds(1)) + setBackoffCoefficient(1.5) + setMaximumAttempts(5) + } + + val overriddenOptions = sourceOptions.copy { + setInitialInterval(Duration.ofMillis(10)) + setMaximumAttempts(10) + } + + val expectedOptions = RpcRetryOptions { + setInitialInterval(Duration.ofMillis(10)) + setMaximumInterval(Duration.ofSeconds(1)) + setBackoffCoefficient(1.5) + setMaximumAttempts(10) + } + + assertEquals(expectedOptions, overriddenOptions) + } +} diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkerFactoryOptionsExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkerFactoryOptionsExtTest.kt new file mode 100644 index 0000000000..e0d678d350 --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkerFactoryOptionsExtTest.kt @@ -0,0 +1,33 @@ +package io.temporal.worker + +import org.junit.Assert.assertEquals +import org.junit.Test + +class WorkerFactoryOptionsExtTest { + + @Test + fun `WorkerFactoryOptions DSL should set fields correctly`() { + val options = WorkerFactoryOptions { + setWorkflowCacheSize(800) + setMaxWorkflowThreadCount(400) + } + + assertEquals(800, options.workflowCacheSize) + assertEquals(400, options.maxWorkflowThreadCount) + } + + @Test + fun `WorkerFactoryOptions copy() DSL should override specified fields and preserve others`() { + val sourceOptions = WorkerFactoryOptions { + setWorkflowCacheSize(800) + setMaxWorkflowThreadCount(400) + } + + val overriddenOptions = sourceOptions.copy { + setWorkflowCacheSize(1600) + } + + assertEquals(1600, overriddenOptions.workflowCacheSize) + assertEquals(400, overriddenOptions.maxWorkflowThreadCount) + } +} diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkerOptionsExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkerOptionsExtTest.kt new file mode 100644 index 0000000000..a62cfad8db --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkerOptionsExtTest.kt @@ -0,0 +1,41 @@ +package io.temporal.worker + +import org.junit.Assert.assertEquals +import org.junit.Test + +class WorkerOptionsExtTest { + + @Test + fun `WorkerOptions DSL should be equivalent to builder`() { + val dslOptions = WorkerOptions { + setMaxConcurrentActivityExecutionSize(10) + setMaxConcurrentWorkflowTaskExecutionSize(5) + } + + val builderOptions = WorkerOptions.newBuilder() + .setMaxConcurrentActivityExecutionSize(10) + .setMaxConcurrentWorkflowTaskExecutionSize(5) + .build() + + assertEquals(builderOptions, dslOptions) + } + + @Test + fun `WorkerOptions copy() DSL should merge override options`() { + val sourceOptions = WorkerOptions { + setMaxConcurrentActivityExecutionSize(10) + setMaxConcurrentWorkflowTaskExecutionSize(5) + } + + val overriddenOptions = sourceOptions.copy { + setMaxConcurrentActivityExecutionSize(20) + } + + val expectedOptions = WorkerOptions { + setMaxConcurrentActivityExecutionSize(20) + setMaxConcurrentWorkflowTaskExecutionSize(5) + } + + assertEquals(expectedOptions, overriddenOptions) + } +} diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkflowImplementationOptionsExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkflowImplementationOptionsExtTest.kt new file mode 100644 index 0000000000..86730e11c9 --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/worker/WorkflowImplementationOptionsExtTest.kt @@ -0,0 +1,49 @@ +package io.temporal.worker + +import io.temporal.activity.ActivityOptions +import org.junit.Assert.assertEquals +import org.junit.Test +import java.time.Duration + +class WorkflowImplementationOptionsExtTest { + + @Test + fun `WorkflowImplementationOptions DSL should be equivalent to builder`() { + val dslOptions = WorkflowImplementationOptions { + setDefaultActivityOptions( + ActivityOptions { + setStartToCloseTimeout(Duration.ofSeconds(10)) + } + ) + } + + val builderOptions = WorkflowImplementationOptions.newBuilder() + .setDefaultActivityOptions( + ActivityOptions.newBuilder() + .setStartToCloseTimeout(Duration.ofSeconds(10)) + .build() + ) + .build() + + assertEquals(builderOptions, dslOptions) + } + + @Test + fun `setActivityOptions vararg pairs DSL should be equivalent to map builder`() { + val activityOptions1 = ActivityOptions { setStartToCloseTimeout(Duration.ofSeconds(5)) } + val activityOptions2 = ActivityOptions { setStartToCloseTimeout(Duration.ofSeconds(10)) } + + val dslOptions = WorkflowImplementationOptions { + setActivityOptions( + "activity1" to activityOptions1, + "activity2" to activityOptions2 + ) + } + + val builderOptions = WorkflowImplementationOptions.newBuilder() + .setActivityOptions(mapOf("activity1" to activityOptions1, "activity2" to activityOptions2)) + .build() + + assertEquals(builderOptions, dslOptions) + } +} diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/workflow/ChildWorkflowOptionsExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/workflow/ChildWorkflowOptionsExtTest.kt new file mode 100644 index 0000000000..52070395f7 --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/workflow/ChildWorkflowOptionsExtTest.kt @@ -0,0 +1,53 @@ +package io.temporal.workflow + +import io.temporal.common.RetryOptions +import org.junit.Assert.assertEquals +import org.junit.Test +import java.time.Duration + +class ChildWorkflowOptionsExtTest { + + @Test + fun `ChildWorkflowOptions DSL should be equivalent to builder`() { + val dslOptions = ChildWorkflowOptions { + setTaskQueue("TestQueue") + setWorkflowRunTimeout(Duration.ofMinutes(5)) + setRetryOptions { + setInitialInterval(Duration.ofMillis(100)) + setMaximumAttempts(3) + } + } + + val builderOptions = ChildWorkflowOptions.newBuilder() + .setTaskQueue("TestQueue") + .setWorkflowRunTimeout(Duration.ofMinutes(5)) + .setRetryOptions( + RetryOptions.newBuilder() + .setInitialInterval(Duration.ofMillis(100)) + .setMaximumAttempts(3) + .build() + ) + .build() + + assertEquals(builderOptions, dslOptions) + } + + @Test + fun `ChildWorkflowOptions copy() DSL should merge override options`() { + val sourceOptions = ChildWorkflowOptions { + setTaskQueue("TestQueue") + setWorkflowRunTimeout(Duration.ofMinutes(5)) + } + + val overriddenOptions = sourceOptions.copy { + setTaskQueue("NewQueue") + } + + val expectedOptions = ChildWorkflowOptions { + setTaskQueue("NewQueue") + setWorkflowRunTimeout(Duration.ofMinutes(5)) + } + + assertEquals(expectedOptions, overriddenOptions) + } +} diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/workflow/ContinueAsNewOptionsExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/workflow/ContinueAsNewOptionsExtTest.kt new file mode 100644 index 0000000000..0c5a21332e --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/workflow/ContinueAsNewOptionsExtTest.kt @@ -0,0 +1,34 @@ +package io.temporal.workflow + +import org.junit.Assert.assertEquals +import org.junit.Test +import java.time.Duration + +class ContinueAsNewOptionsExtTest { + + @Test + fun `ContinueAsNewOptions DSL should set fields correctly`() { + val options = ContinueAsNewOptions { + setTaskQueue("TestQueue") + setWorkflowRunTimeout(Duration.ofMinutes(10)) + } + + assertEquals("TestQueue", options.taskQueue) + assertEquals(Duration.ofMinutes(10), options.workflowRunTimeout) + } + + @Test + fun `ContinueAsNewOptions copy() DSL should override specified fields and preserve others`() { + val sourceOptions = ContinueAsNewOptions { + setTaskQueue("TestQueue") + setWorkflowRunTimeout(Duration.ofMinutes(10)) + } + + val overriddenOptions = sourceOptions.copy { + setTaskQueue("NewQueue") + } + + assertEquals("NewQueue", overriddenOptions.taskQueue) + assertEquals(Duration.ofMinutes(10), overriddenOptions.workflowRunTimeout) + } +} diff --git a/temporal-kotlin/src/test/kotlin/io/temporal/workflow/SagaExtTest.kt b/temporal-kotlin/src/test/kotlin/io/temporal/workflow/SagaExtTest.kt new file mode 100644 index 0000000000..d44a2a7978 --- /dev/null +++ b/temporal-kotlin/src/test/kotlin/io/temporal/workflow/SagaExtTest.kt @@ -0,0 +1,81 @@ +package io.temporal.workflow + +import io.temporal.activity.ActivityInterface +import io.temporal.activity.ActivityMethod +import io.temporal.activity.ActivityOptions +import io.temporal.client.WorkflowClientOptions +import io.temporal.client.WorkflowOptions +import io.temporal.common.converter.DefaultDataConverter +import io.temporal.common.converter.JacksonJsonPayloadConverter +import io.temporal.common.converter.KotlinObjectMapperFactory +import io.temporal.testing.internal.SDKTestWorkflowRule +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import java.time.Duration +import java.util.concurrent.atomic.AtomicBoolean + +class SagaExtTest { + + companion object { + val compensated = AtomicBoolean(false) + } + + @Rule + @JvmField + val testWorkflowRule: SDKTestWorkflowRule = SDKTestWorkflowRule.newBuilder() + .setWorkflowTypes(SagaWorkflowImpl::class.java) + .setActivityImplementations(CompensationActivityImpl()) + .setWorkflowClientOptions( + WorkflowClientOptions.newBuilder() + .setDataConverter(DefaultDataConverter(JacksonJsonPayloadConverter(KotlinObjectMapperFactory.new()))) + .build() + ) + .build() + + @ActivityInterface + interface CompensationActivity { + @ActivityMethod + fun compensate() + } + + class CompensationActivityImpl : CompensationActivity { + override fun compensate() { + compensated.set(true) + } + } + + @WorkflowInterface + interface SagaWorkflow { + @WorkflowMethod + fun execute() + } + + class SagaWorkflowImpl : SagaWorkflow { + override fun execute() { + val activity = Workflow.newActivityStub( + CompensationActivity::class.java, + ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build() + ) + val saga = Saga { setParallelCompensation(false) } + try { + saga.addCompensation { activity.compensate() } + throw RuntimeException("simulated failure") + } catch (e: Exception) { + saga.compensate() + } + } + } + + @Test + fun `Saga DSL extension should build Saga with options and run compensations`() { + compensated.set(false) + val client = testWorkflowRule.workflowClient + val stub = client.newWorkflowStub( + SagaWorkflow::class.java, + WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.taskQueue).build() + ) + stub.execute() + assertTrue("compensation should have been called", compensated.get()) + } +}