Exemplo n.º 1
0
        public Test_RegistrationError(TemporalFixture fixture)
        {
            var settings = new TemporalSettings()
            {
                Namespace              = TemporalFixture.Namespace,
                ProxyLogLevel          = TemporalTestHelper.ProxyLogLevel,
                CreateNamespace        = true,
                Debug                  = TemporalTestHelper.Debug,
                DebugPrelaunched       = TemporalTestHelper.DebugPrelaunched,
                DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats,
                ClientIdentity         = TemporalTestHelper.ClientIdentity
            };

            if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started)
            {
                this.fixture     = fixture;
                this.client      = fixture.Client;
                this.proxyClient = new HttpClient()
                {
                    BaseAddress = client.ProxyUri
                };

                // NOTE: We're not auto-registering workflows and activities or starting
                //       workers for these unit tests.
            }
            else
            {
                this.fixture     = fixture;
                this.client      = fixture.Client;
                this.proxyClient = new HttpClient()
                {
                    BaseAddress = client.ProxyUri
                };
            }
        }
Exemplo n.º 2
0
        public static async Task Main(string[] args)
        {
            // Connect to Temporal

            var settings = new TemporalSettings()
            {
                Namespace       = "my-namespace",
                TaskQueue       = "my-tasks",
                CreateNamespace = true,
                HostPort        = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = await client.NewWorkerAsync();

                await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly());

                await worker.StartAsync();

                // Invoke the workflow.

                var workflowStub = client.NewWorkflowStub <IEmailWorkflow>();

                await workflowStub.SendMessagesAsync();
            }
        }
Exemplo n.º 3
0
        public static async Task Main(string[] args)
        {
            // Connect to Temporal

            var settings = new TemporalSettings()
            {
                Namespace       = "my-namespace",
                CreateNamespace = true,
                HostPort        = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = await client.NewWorkerAsync();

                await worker.RegisterWorkflowAsync <HelloWorkflow>();

                await worker.StartAsync();

                // Invoke your workflow.

                var stub = client.NewWorkflowStub <IHelloWorkflow>();

                Console.WriteLine(await stub.HelloAsync("Jeff"));
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// <para>
        /// Starts a Temporal compose application if it's not already running.  You'll generally want
        /// to call this in your test class constructor instead of <see cref="ITestFixture.Start(Action)"/>.
        /// </para>
        /// <note>
        /// You'll need to call <see cref="StartAsComposed(TemporalSettings, string, string, string, Neon.Diagnostics.LogLevel, bool, bool, bool, bool)"/>
        /// instead when this fixture is being added to a <see cref="ComposedFixture"/>.
        /// </note>
        /// </summary>
        /// <param name="settings">Optional Temporal settings.</param>
        /// <param name="name">Optionally specifies the Docker compose application name (defaults to <c>temporal-dev</c>).</param>
        /// <param name="composeFile">
        /// <para>
        /// Optionally specifies the Temporal Docker compose file text.  This defaults to
        /// <see cref="DefaultComposeFile"/> which configures Temporal server to start with
        /// a new Cassandra database instance listening on port <b>9042</b> as well as the
        /// Temporal web UI running on port <b>8088</b>.  Temporal server is listening on
        /// its standard gRPC port <b>7233</b>.
        /// </para>
        /// <para>
        /// You may specify your own Docker compose text file to customize this by configuring
        /// a different backend database, etc.
        /// </para>
        /// </param>
        /// <param name="defaultNamespace">Optionally specifies the default namespace for the fixture's client.  This defaults to <b>test-namespace</b>.</param>
        /// <param name="logLevel">Specifies the Temporal log level.  This defaults to <see cref="Neon.Diagnostics.LogLevel.None"/>.</param>
        /// <param name="reconnect">
        /// Optionally specifies that a new Temporal connection <b>should</b> be established for each
        /// unit test case.  By default, the same connection will be reused which will save about a
        /// second per test case.
        /// </param>
        /// <param name="keepRunning">
        /// Optionally indicates that the application should remain running after the fixture is disposed.
        /// This is handy for using the Temporal web UI for port mortems after tests have completed.
        /// </param>
        /// <param name="noClient">
        /// Optionally disables establishing a client connection when <c>true</c>
        /// is passed.  The <see cref="Client"/> and <see cref="HttpClient"/> properties
        /// will be set to <c>null</c> in this case.
        /// </param>
        /// <param name="noReset">
        /// Optionally prevents the fixture from calling <see cref="TemporalClient.Reset()"/> to
        /// put the Temporal client library into its initial state before the fixture starts as well
        /// as when the fixture itself is reset.
        /// </param>
        /// <returns>
        /// <see cref="TestFixtureStatus.Started"/> if the fixture wasn't previously started and
        /// this method call started it or <see cref="TestFixtureStatus.AlreadyRunning"/> if the
        /// fixture was already running.
        /// </returns>
        /// <remarks>
        /// <note>
        /// Some of the <paramref name="settings"/> properties will be ignored including
        /// <see cref="TemporalSettings.HostPort"/>.  This will be replaced by the local
        /// endpoint for the Temporal application.  Also, the fixture will connect to the
        /// <b>default</b> Temporal namespace by default (unless another is specified).
        /// </note>
        /// <note>
        /// A fresh Temporal client <see cref="Client"/> will be established every time this
        /// fixture is started, regardless of whether the fixture has already been started.  This
        /// ensures that each unit test will start with a client in the default state.
        /// </note>
        /// </remarks>
        public TestFixtureStatus Start(
            TemporalSettings settings = null,
            string name             = "temporal-dev",
            string composeFile      = DefaultComposeFile,
            string defaultNamespace = Namespace,
            LogLevel logLevel       = LogLevel.None,
            bool reconnect          = false,
            bool keepRunning        = false,
            bool noClient           = false,
            bool noReset            = false)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(composeFile), nameof(composeFile));

            return(base.Start(
                       () =>
            {
                StartAsComposed(
                    settings:         settings,
                    name:             name,
                    composeFile:      composeFile,
                    defaultNamespace: defaultNamespace,
                    logLevel:         logLevel,
                    reconnect:        reconnect,
                    keepRunning:      keepRunning,
                    noClient:         noClient,
                    noReset:          noReset);
            }));
        }
Exemplo n.º 5
0
        public Test_Replay(TemporalFixture fixture)
        {
            TestHelper.ResetDocker(this.GetType());

            var settings = new TemporalSettings()
            {
                Namespace              = TemporalFixture.Namespace,
                ProxyLogLevel          = TemporalTestHelper.ProxyLogLevel,
                CreateNamespace        = true,
                Debug                  = TemporalTestHelper.Debug,
                DebugPrelaunched       = TemporalTestHelper.DebugPrelaunched,
                DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats,
                ClientIdentity         = TemporalTestHelper.ClientIdentity,
                TaskQueue              = TemporalTestHelper.TaskQueue
            };

            if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started)
            {
                this.fixture = fixture;
                this.client  = fixture.Client;

                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = client.NewWorkerAsync().Result;

                worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()).WaitWithoutAggregate();
                worker.StartAsync().WaitWithoutAggregate();
            }
            else
            {
                this.fixture = fixture;
                this.client  = fixture.Client;
            }
        }
Exemplo n.º 6
0
        public Test_SignalChecks(TemporalFixture fixture)
        {
            TestHelper.ResetDocker(this.GetType());

            var settings = new TemporalSettings()
            {
                Namespace              = TemporalFixture.Namespace,
                ProxyLogLevel          = TemporalTestHelper.ProxyLogLevel,
                CreateNamespace        = true,
                Debug                  = TemporalTestHelper.Debug,
                DebugPrelaunched       = TemporalTestHelper.DebugPrelaunched,
                DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats,
                ClientIdentity         = TemporalTestHelper.ClientIdentity
            };

            if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started)
            {
                this.fixture     = fixture;
                this.client      = fixture.Client;
                this.proxyClient = new HttpClient()
                {
                    BaseAddress = client.ProxyUri
                };
            }
            else
            {
                this.fixture     = fixture;
                this.client      = fixture.Client;
                this.proxyClient = new HttpClient()
                {
                    BaseAddress = client.ProxyUri
                };
            }
        }
Exemplo n.º 7
0
        public static async Task Main(params string[] args)
        {
            var settings = new TemporalSettings()
            {
                // This specifies the default namespace for operations initiated by the
                // client connected below (this can be overridden for specific
                // operations).

                Namespace = "Acme-PROD",

                // Host/port for at least one of the Temporal cluster servers:

                HostPort = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a typed stub for the remote workflow.

                var stub = client.NewWorkflowStub <IMailer>();

                // Execute the workflow.

                var success = await stub.SendEmail("*****@*****.**", "Test subject", "This is a test email.");

                if (success)
                {
                    Console.WriteLine("Email SENT!");
                }
                else
                {
                    Console.WriteLine("Email FAILED!");
                }
            }
        }
Exemplo n.º 8
0
        public Test_EndToEnd(TemporalFixture fixture, ITestOutputHelper outputHelper)
        {
            TestHelper.ResetDocker(this.GetType());

            testWriter = new TestOutputWriter(outputHelper);

            // Configure a service for activity dependency injection testing if it doesn't
            // already exist.

            if (NeonHelper.ServiceContainer.GetService <ActivityDependency>() == null)
            {
                NeonHelper.ServiceContainer.AddSingleton(typeof(ActivityDependency), new ActivityDependency()
                {
                    Hello = "World!"
                });
            }

            // Initialize the Cadence fixture.

            var settings = new TemporalSettings()
            {
                Namespace              = TemporalFixture.Namespace,
                ProxyLogLevel          = TemporalTestHelper.ProxyLogLevel,
                CreateNamespace        = true,
                Debug                  = TemporalTestHelper.Debug,
                DebugPrelaunched       = TemporalTestHelper.DebugPrelaunched,
                DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats,
                ClientIdentity         = TemporalTestHelper.ClientIdentity,
                TaskQueue              = TemporalTestHelper.TaskQueue
            };

            if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started)
            {
                this.fixture = fixture;
                this.client  = fixture.Client;

                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = client.NewWorkerAsync().Result;

                worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()).WaitWithoutAggregate();
                worker.StartAsync().WaitWithoutAggregate();
            }
            else
            {
                this.fixture = fixture;
                this.client  = fixture.Client;
            }
        }
Exemplo n.º 9
0
        public static async Task UntypedStub()
        {
            #region code_untyped
            var settings = new TemporalSettings()
            {
                // This specifies the default namespace for operations initiated by the
                // client connected below (this can be overridden for specific
                // operations).

                Namespace = "Acme-PROD",

                // Host/port for at least one of the Temporal cluster servers:

                HostPort = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create an untyped stub for the remote workflow.

                var stub = client.NewUntypedWorkflowStub("SendEmail");

                // Start the workflow.  Note that we need to take to ensure that the number, order
                // and types of the parameters match what the GOLANG workflow implementation expects.
                // Untyped workflow stub arguments cannot be checked be the C# compiler.
                //
                // This method returns a [WorkflowExecution] which includes the workflow and
                // run IDs.  We're not using these here, but real applications may want to
                // persist this so that could check on long-running workflows later.

                var execution = await stub.StartAsync("*****@*****.**", "Test subject", "This is a test email.");

                // Wait for the workflow to complete and return it's result.  Note that we need
                // to explicitly specify the result [bool] type as a generic type parameter.
                // You need to ensure that this matches the workflow implementation as well.

                var success = await stub.GetResultAsync <bool>();

                if (success)
                {
                    Console.WriteLine("Email SENT!");
                }
                else
                {
                    Console.WriteLine("Email FAILED!");
                }
            }
            #endregion
        }
Exemplo n.º 10
0
        public static async Task Main(string[] args)
        {
            // Initialize the logger.

            LogManager.Default.SetLogLevel("info");

            logger = LogManager.Default.GetLogger(typeof(Program));
            logger.LogInfo("Starting workflow service");

            try
            {
                // Connect to Temporal

                var settings = new TemporalSettings()
                {
                    Namespace       = "my-namespace",
                    CreateNamespace = true,
                    HostPort        = "localhost:7933"
                };

                using (var client = await TemporalClient.ConnectAsync(settings))
                {
                    // Create a worker and register the workflow and activity
                    // implementations to let Temporal know we're open for business.

                    var worker = await client.NewWorkerAsync();

                    await worker.RegisterAssemblyAsync(System.Reflection.Assembly.GetExecutingAssembly());

                    await worker.StartAsync();

                    // Spin forever, processing workflows and activities assigned by Temporal.

                    while (true)
                    {
                        await Task.Delay(TimeSpan.FromMinutes(5));
                    }
                }
            }
            catch (Exception e)
            {
                logger.LogError(e);
            }
            finally
            {
                logger.LogInfo("Exiting workflow service");
            }
        }
Exemplo n.º 11
0
        public TemporalTests(TemporalFixture fixture)
        {
            TestHelper.ResetDocker(this.GetType());

            var settings = new TemporalSettings()
            {
                Namespace       = "test-domain",
                TaskQueue       = "test-tasks",
                ProxyLogLevel   = LogLevel.Info,
                CreateNamespace = true          // <-- this ensures that the default namespace exists
            };

            // This starts/restarts the [nforgeio/temporal-dev] container for the first test
            // run in this class.  Subsequent tests run from the class will use the existing
            // container instance, saving time by not having to wait for Temporal and Cassandra
            // to spin up and be ready for business.
            //
            // The [keepOpen=true] parameter tells the fixture to let the container continue running
            // after all of the tests have completed.  This is useful for examining workflow histories
            // via the Temporal UX after the tests have completed.  You can view the Temporal portal at
            //
            //      http://localhost:8088
            //
            // You can pass [keepOpen=false] to have the fixture remove the container after the
            // test run if you wish.

            if (fixture.Start(settings, reconnect: true, keepRunning: true) == TestFixtureStatus.Started)
            {
                this.fixture = fixture;
                this.client  = fixture.Client;

                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = client.NewWorkerAsync().Result;

                worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()).WaitWithoutAggregate();
                worker.StartAsync().WaitWithoutAggregate();
            }
            else
            {
                this.fixture = fixture;
                this.client  = fixture.Client;
            }
        }
Exemplo n.º 12
0
        public Test_Settings(TemporalFixture fixture)
        {
            TestHelper.ResetDocker(this.GetType());

            var settings = new TemporalSettings()
            {
                Namespace              = TemporalFixture.Namespace,
                ProxyLogLevel          = TemporalTestHelper.ProxyLogLevel,
                CreateNamespace        = true,
                Debug                  = TemporalTestHelper.Debug,
                DebugPrelaunched       = TemporalTestHelper.DebugPrelaunched,
                DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats,
                ClientIdentity         = TemporalTestHelper.ClientIdentity
            };

            this.fixture = fixture;

            fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen, noClient: true);
        }
Exemplo n.º 13
0
        public static async Task Main(string[] args)
        {
            // Configure the settings name such that they will be injected
            // into the email activity when it's constructed.
            //
            // Note that we did this before calling RegisterAssemblyAsync() below.
            // Dependencies added after activities have been registered will be
            // ignored.

            NeonHelper.ServiceContainer.AddSingleton(typeof(MailSettings), new MailSettings()
            {
                MailServer = "mail.my-company.com"
            });

            // Connect to Temporal

            var settings = new TemporalSettings()
            {
                Namespace       = "my-namespace",
                TaskQueue       = "my-tasks",
                CreateNamespace = true,
                HostPort        = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = await client.NewWorkerAsync();

                await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly());

                await worker.StartAsync();

                // Invoke the workflow.

                var workflowStub = client.NewWorkflowStub <IEmailWorkflow>();

                await workflowStub.SendMessagesAsync();
            }
        }
Exemplo n.º 14
0
        public static async Task Main(string[] args)
        {
            var settings = new TemporalSettings()
            {
                Namespace       = "my-namespace",
                TaskQueue       = "my-tasks",
                CreateNamespace = true,
                HostPort        = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = await client.NewWorkerAsync();

                await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly());

                await worker.StartAsync();

                // Invoke the workflow, send it some signals and very that
                // it changed its state to the signal value.

                var stub = client.NewWorkflowStub <IMyWorkflow>();
                var task = stub.DoItAsync();

                await stub.SignalAsync("signal #1");

                Console.WriteLine(await stub.GetStatusAsync());

                await stub.SignalAsync("signal #2");

                Console.WriteLine(await stub.GetStatusAsync());

                // This signal completes the workflow.

                await stub.SignalAsync("done");

                await task;
            }
        }
Exemplo n.º 15
0
        public Test_StubManager(TemporalFixture fixture)
        {
            TestHelper.ResetDocker(this.GetType());

            var settings = new TemporalSettings()
            {
                Namespace      = TemporalFixture.Namespace,
                ProxyLogLevel  = TemporalTestHelper.ProxyLogLevel,
                Debug          = TemporalTestHelper.Debug,
                ClientIdentity = TemporalTestHelper.ClientIdentity
            };

            fixture.Start(settings, reconnect: true);

            this.fixture     = fixture;
            this.client      = fixture.Client;
            this.proxyClient = new HttpClient()
            {
                BaseAddress = client.ProxyUri
            };
        }
Exemplo n.º 16
0
        public static async Task Main(string[] args)
        {
            // Connect to Temporal

            var settings = new TemporalSettings()
            {
                Namespace       = "my-namespace",
                TaskQueue       = "my-tasks",
                CreateNamespace = true,
                HostPort        = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = await client.NewWorkerAsync();

                await worker.RegisterWorkflowAsync <HelloWorkflow>();

                await worker.StartAsync();

                #region code
                // Invoke a workflow with options:

                var stub = client.NewWorkflowStub <IHelloWorkflow>(
                    new StartWorkflowOptions()
                {
                    Id = "my-ultimate-workflow",
                    WorkflowRunTimeout = TimeSpan.FromMinutes(5)
                });

                Console.WriteLine(await stub.HelloAsync("Jeff"));
                #endregion
            }
        }
Exemplo n.º 17
0
        public static async Task Main(string[] args)
        {
            // Connect to Temporal

            var settings = new TemporalSettings()
            {
                Namespace       = "my-namespace",
                CreateNamespace = true,
                HostPort        = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                var stub = client.NewWorkflowFutureStub <ICronWorkflow>(
                    "backup",
                    new StartWorkflowOptions()
                {
                    // Run the workflow every day at 1:00am UTC:
                    CronSchedule = "0 1 * * *"
                });;

                await stub.StartAsync();
            }
        }
Exemplo n.º 18
0
        public static async Task Main(string[] args)
        {
            var settings = new TemporalSettings()
            {
                Namespace       = "my-namespace",
                TaskQueue       = "my-tasks",
                CreateNamespace = true,
                HostPort        = "localhost:7933"
            };

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                var worker = await client.NewWorkerAsync();

                await worker.RegisterAssemblyAsync(System.Reflection.Assembly.GetExecutingAssembly());

                await worker.StartAsync();

                // Invoke the workflow and then query it's status a few times.

                var stub = client.NewWorkflowStub <IMyWorkflow>();
                var task = stub.DoItAsync();

                for (int i = 0; i < 5; i++)
                {
                    await Task.Delay(TimeSpan.FromSeconds(2.5));

                    Console.WriteLine(await stub.GetStatusAsync());
                }

                await task;
            }
        }
Exemplo n.º 19
0
        public async Task Multiple_TaskQueues()
        {
            await SyncContext.Clear;

            // Test the scenario where there multiple clients without
            // workers that will be used to simulate apps that make calls
            // on workflows and then create multiple clients that register
            // different workflows and activities and then verify that
            // each of the workerless clients are able to execute workflows
            // and activities and that these end up being executed on the
            // correct clients.

            var clients = new List <TemporalClient>();

            try
            {
                // Initialize the non-worker clients.

                TemporalClient client1;
                TemporalClient client2;
                TemporalClient client3;

                TemporalSettings settings1 = fixture.Settings.Clone();
                TemporalSettings settings2 = fixture.Settings.Clone();
                TemporalSettings settings3 = fixture.Settings.Clone();

                settings1.TaskQueue = "taskqueue-1";
                settings2.TaskQueue = "taskqueue-2";
                settings3.TaskQueue = "taskqueue-3";

                clients.Add(client1 = await TemporalClient.ConnectAsync(fixture.Settings));
                clients.Add(client2 = await TemporalClient.ConnectAsync(fixture.Settings));
                clients.Add(client3 = await TemporalClient.ConnectAsync(fixture.Settings));

                // Initialize the worker clients.

                clients.Add(workerClient1 = await TemporalClient.ConnectAsync(settings1));
                clients.Add(workerClient2 = await TemporalClient.ConnectAsync(settings2));
                clients.Add(workerClient3 = await TemporalClient.ConnectAsync(settings3));

                // Initialize and start the workers.

                var worker1 = await workerClient1.NewWorkerAsync();

                await worker1.RegisterActivityAsync <ActivityWorker1>();

                await worker1.RegisterWorkflowAsync <WorkflowWorker1>();

                await worker1.StartAsync();

                var worker2 = await workerClient2.NewWorkerAsync();

                await worker2.RegisterActivityAsync <ActivityWorker2>();

                await worker2.RegisterWorkflowAsync <WorkflowWorker2>();

                await worker2.StartAsync();

                var worker3 = await workerClient3.NewWorkerAsync();

                await worker3.RegisterActivityAsync <ActivityWorker3>();

                await worker3.RegisterWorkflowAsync <WorkflowWorker3>();

                await worker3.StartAsync();

                // Execute each of the worker workflows WITHOUT the associated activities
                // from each client (both the worker and non-worker clients).

                foreach (var client in clients)
                {
                    var stub = client.NewWorkflowStub <IWorkflowWorker1>();

                    Assert.True(await stub.RunAsync(testActivity: false));
                }

                foreach (var client in clients)
                {
                    var stub = client.NewWorkflowStub <IWorkflowWorker2>();

                    Assert.True(await stub.RunAsync(testActivity: false));
                }

                foreach (var client in clients)
                {
                    var stub = client.NewWorkflowStub <IWorkflowWorker3>();

                    Assert.True(await stub.RunAsync(testActivity: false));
                }

                // Re-run the workflows calling the activities this time.

                foreach (var client in clients)
                {
                    var stub = client.NewWorkflowStub <IWorkflowWorker1>();

                    Assert.True(await stub.RunAsync(testActivity: true));
                }

                foreach (var client in clients)
                {
                    var stub = client.NewWorkflowStub <IWorkflowWorker2>();

                    Assert.True(await stub.RunAsync(testActivity: true));
                }

                foreach (var client in clients)
                {
                    var stub = client.NewWorkflowStub <IWorkflowWorker3>();

                    Assert.True(await stub.RunAsync(testActivity: true));
                }
            }
            finally
            {
                foreach (var client in clients)
                {
                    client.Dispose();
                }

                workerClient1 = null;
                workerClient2 = null;
                workerClient3 = null;
            }
        }
Exemplo n.º 20
0
        /// <summary>
        /// Used to start the fixture within a <see cref="ComposedFixture"/>.
        /// </summary>
        /// <param name="settings">Optional Temporal settings.</param>
        /// <param name="name">Optionally specifies the Docker compose application name (defaults to <c>temporal-dev</c>).</param>
        /// <param name="composeFile">
        /// <para>
        /// Optionally specifies the Temporal Docker compose file text.  This defaults to
        /// <see cref="DefaultComposeFile"/> which configures Temporal server to start with
        /// a new Cassandra database instance listening on port <b>9042</b> as well as the
        /// Temporal web UI running on port <b>8088</b>.  Temporal server is listening on
        /// its standard gRPC port <b>7233</b>.
        /// </para>
        /// <para>
        /// You may specify your own Docker compose text file to customize this by configuring
        /// a different backend database, etc.
        /// </para>
        /// </param>
        /// <param name="defaultNamespace">Optionally specifies the default namespace for the fixture's client.  This defaults to <b>test-namespace</b>.</param>
        /// <param name="logLevel">Specifies the Temporal log level.  This defaults to <see cref="Neon.Diagnostics.LogLevel.None"/>.</param>
        /// <param name="reconnect">
        /// Optionally specifies that a new Temporal connection <b>should</b> be established for each
        /// unit test case.  By default, the same connection will be reused which will save about a
        /// second per test.
        /// </param>
        /// <param name="keepRunning">
        /// Optionally indicates that the compose application should remain running after the fixture is disposed.
        /// This is handy for using the Temporal web UI for port mortems after tests have completed.
        /// </param>
        /// <param name="noClient">
        /// Optionally disables establishing a client connection when <c>true</c>
        /// is passed.  The <see cref="Client"/> and <see cref="HttpClient"/> properties
        /// will be set to <c>null</c> in this case.
        /// </param>
        /// <param name="noReset">
        /// Optionally prevents the fixture from calling <see cref="TemporalClient.Reset()"/> to
        /// put the Temporal client library into its initial state before the fixture starts as well
        /// as when the fixture itself is reset.
        /// </param>
        /// <remarks>
        /// <note>
        /// A fresh Temporal client <see cref="Client"/> will be established every time this
        /// fixture is started, regardless of whether the fixture has already been started.  This
        /// ensures that each unit test will start with a client in the default state.
        /// </note>
        /// </remarks>
        public void StartAsComposed(
            TemporalSettings settings = null,
            string name             = "temporal-dev",
            string composeFile      = DefaultComposeFile,
            string defaultNamespace = Namespace,
            LogLevel logLevel       = LogLevel.None,
            bool reconnect          = false,
            bool keepRunning        = false,
            bool noClient           = false,
            bool noReset            = false)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(composeFile), nameof(composeFile));

            base.CheckWithinAction();

            if (!IsRunning)
            {
                // The [cadence-dev] container started by [CadenceFixture] has conflicting ports
                // for Cassandra and the web UI, so we're going to stop that container if it's running.

                NeonHelper.Execute(NeonHelper.DockerCli, new object[] { "rm", "--force",
                                                                        new string[]
                                                                        {
                                                                            "cadence-dev_cadence_1",
                                                                            "cadence-dev_cadence-web_1",
                                                                            "cadence-dev_cassandra_1",
                                                                            "cadence-dev_statsd_1"
                                                                        } });

                // Reset TemporalClient to its initial state.

                this.noReset = noReset;

                if (!noReset)
                {
                    TemporalClient.Reset();
                }

                // Start the Temporal Docker compose application.

                base.StartAsComposed(name, composeFile, keepRunning);

                // It can take Temporal server some time to start.  Rather than relying on [temporal-proxy]
                // to handle retries (which may take longer than the connect timeout), we're going to wait
                // up to 4 minutes for Temporal to start listening on its RPC socket.

                var retry = new LinearRetryPolicy(e => true, maxAttempts: int.MaxValue, retryInterval: TimeSpan.FromSeconds(0.5), timeout: TimeSpan.FromMinutes(4));

                retry.Invoke(
                    () =>
                {
                    // The [socket.Connect()] calls below will throw [SocketException] until
                    // Temporal starts listening on its RPC socket.

                    var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);

                    socket.Connect(IPAddress.IPv6Loopback, NetworkPorts.Temporal);
                    socket.Close();
                });

                Thread.Sleep(TimeSpan.FromSeconds(5));  // Wait a bit longer for luck!

                // Initialize the settings.

                settings = settings ?? new TemporalSettings()
                {
                    HostPort        = $"localhost:{NetworkPorts.Temporal}",
                    CreateNamespace = true,
                    Namespace       = defaultNamespace,
                    ProxyLogLevel   = logLevel,
                };

                this.settings  = settings;
                this.reconnect = reconnect;

                if (!noClient)
                {
                    // Establish the Temporal connection.

                    Client     = TemporalClient.ConnectAsync(settings).Result;
                    HttpClient = new HttpClient()
                    {
                        BaseAddress = Client.ListenUri
                    };
                }
            }
        }
Exemplo n.º 21
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            // Verify the environment variables.

            var settings   = new TemporalSettings();
            var hostPort   = GetEnvironmentVariable("TEMPORAL_HOSTPORT");
            var @namespace = GetEnvironmentVariable("TEMPORAL_NAMESPACE");
            var taskQueue  = GetEnvironmentVariable("TEMPORAL_TASKQUEUE");
            var error      = false;

            Log.LogInfo(() => $"TEMPORAL_HOSTPORT:  {hostPort}");
            Log.LogInfo(() => $"TEMPORAL_NAMESPACE: {@namespace}");
            Log.LogInfo(() => $"TEMPORAL_TASKQUEUE: {taskQueue}");

            if (string.IsNullOrEmpty(hostPort))
            {
                error = true;
                Log.LogError("The [TEMPORAL_HOSTPORT] environment variable is required.");
            }

            settings.HostPort = hostPort;

            if (string.IsNullOrEmpty(@namespace))
            {
                error = true;
                Log.LogError("The [TEMPORAL_NAMESPACE] environment variable is required.");
            }

            if (string.IsNullOrEmpty(taskQueue))
            {
                error = true;
                Log.LogError("The [TEMPORAL_TASKQUEUE] environment variable is required.");
            }

            if (error)
            {
                return(1);
            }

            // Connect to Temporal and register the workflows and activities.

            Log.LogInfo("Connecting to Temporal.");

            settings.Namespace = @namespace;
            settings.TaskQueue = taskQueue;

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                using (var worker = await client.NewWorkerAsync())
                {
                    Log.LogInfo("Registering workflows.");
                    await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly());

                    Log.LogInfo("Starting worker.");
                    await worker.StartAsync();

                    // Indicate that the service is running.

                    Log.LogInfo("Ready for work.");
                    await StartedAsync();
                }
            }

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }