Orcraft 1.1.3 public beta released 08-Nov-2026. The Free Plan and the Team trial are open; card payments and invoices open shortly.

Orcraft 1.1.3 public beta released 08-Nov-2026. The Free Plan and the Team trial are open; card payments and invoices open shortly.

Orcraft

The platform for test development and orchestration
that lets you and your AI Agents run the entire testing operation

Watch the demo

Hello Endpoint test

Hello World ran on the controller's own machine. Hello Endpoint is the simplest test that runs on an endpoint: one flow sends one step to the machine chosen in the run panel, and the step runs the hostname command there and logs the answer. It needs an endpoint registered and licensed in the controller, as Adding endpoints walks through.

Open the test's folder in your IDE and let's review its three files:

orcraft-test-suite-tutorial/test_group_endpoints/test_hello_endpoint/

The test

class TestHelloEndpointConfig(OrcraftTestConfig):
	name = OrcraftConfigField.Text(default="Hello Endpoint")

	description = OrcraftConfigField.Text(
		default=(
			"The simplest test that runs on an endpoint.\n"
			"One flow sends one step to the Test Endpoint; the step runs the hostname command there and logs the answer.\n"
			"With the default localhost the step runs on the controller's machine; choose an endpoint in the run panel and it runs there."
		)
	)

	test_endpoint = OrcraftConfigField.Endpoint(default="localhost", label="Test Endpoint")

The config adds one field to what Hello World had: an Endpoint field named test_endpoint. Its value is one of the endpoints registered in the controller, and the run panel shows it as Test Endpoint with a pencil to choose the machine. The default, localhost, is the controller's own machine.

class TestHelloEndpointResult(OrcraftTestResult):
	hostname = OrcraftResultField.Text(default="", label="Hostname")


class TestHelloEndpoint(OrcraftTest):
	def __init__(self, config: TestHelloEndpointConfig | None = None) -> None:
		super().__init__(config or TestHelloEndpointConfig())

	def _run_case(self, case: OrcraftTestCase) -> OrcraftTestResult:
		case.result = TestHelloEndpointResult()

		flow = FlowHelloEndpoint(FlowHelloEndpointConfig())
		flow_result = flow.run()

		if self.handle_errors(flow_result, case.result):
			return case.result

		case.result.hostname = flow_result.hostname

		return case.result

The test case runs one flow and copies the host name the flow brings back into its own result field. handle_errors ends the case when the flow failed or errored, so the result below is filled only on a pass.

The flow

class FlowHelloEndpoint(OrcraftTestFlow):
	result_type = FlowHelloEndpointResult

	def __init__(self, config: FlowHelloEndpointConfig) -> None:
		super().__init__(config)

	def _run_flow(self) -> None:
		# No endpoint is named here: the step goes to the test's Test Endpoint.
		step_result = self.run_step(StepHostname)

		if self.handle_errors(step_result, self.result):
			return

		self.result.hostname = step_result.hostname

A step is run through a flow's run_step. The call names no endpoint, so the step goes to the test's Test Endpoint. That is the whole mechanism: the flow and the step never mention a machine, and the run panel decides where they run.

The step

class StepHostname(OrcraftTestStep):
	def __init__(self, config: StepHostnameConfig | None = None) -> None:
		super().__init__(config or StepHostnameConfig())

	def _run_step(self) -> OrcraftTestStepResult:
		self.result = StepHostnameResult()

		# The step's body runs on the endpoint it was sent to, so the command
		# answers with that machine's name.
		completed = subprocess.run(["hostname"], capture_output=True, text=True)
		hostname = completed.stdout.strip()

		self.checkpoint(
			completed.returncode == 0 and bool(hostname),
			f"hostname answered: {hostname}",
			f"hostname failed with exit code {completed.returncode}: {completed.stderr.strip()}",
		)

		Logger.log(f"Hello from {hostname}", color=Logger.Color.BLUE)

		self.result.hostname = hostname

		return self.result

The body of _run_step is what runs on the endpoint. It is plain Python: it calls the hostname command with subprocess, checks the answer with a checkpoint, logs one line and returns the name in the step's result. The step's log and result travel back to the controller like any other step's.

Running from CLI

The test imports its flow and step as packages of the suite, so it runs as a module from the suite's folder. With no endpoint chosen, the step runs on this machine:

cd orcraft-test-suite-tutorial
python -m test_group_endpoints.test_hello_endpoint.test_hello_endpoint
Orcraft Controller (v0.1.0.dev306+l1)
-------------------------------------

                                             Test: Hello Endpoint | Description                                             

The simplest test that runs on an endpoint.
One flow sends one step to the Test Endpoint; the step runs the hostname command there and logs the answer.
With the default localhost the step runs on the controller's machine; choose an endpoint in the run panel and it runs there.


                                    Test: Hello Endpoint | Configuration                                   

Name                 | Upload Test Package | Update Endpoint App | Test Endpoint | Start Time
-----------------------------------------------------------------------------------------------------------
Test: Hello Endpoint | if_missing          | on_mismatch         | localhost     | 2026-10-23  14:56:43.921

Skipping lock for local target localhost

             Flow: Hello Endpoint | Configuration 

            Start Time
            ------------------------
            2026-10-23  14:56:43.926


                      Step: Hostname | Configuration      

                Start Time               | Target Endpoint
                ------------------------------------------
                2026-10-23  14:56:43.926 | localhost


                     Checkpoint Pass   hostname answered: GMG-DESKTOP

                [StepHostname] Hello from GMG-DESKTOP

                                            Step: Hostname | Result                            

                Name           | Status | Finish Time              | Elapsed Time | Hostname
                -------------------------------------------------------------------------------
                Step: Hostname | PASS   | 2026-10-23  14:56:43.941 | 0.015 s      | GMG-DESKTOP


                                        Flow: Hello Endpoint | Result                            

            Name                 | Status | Finish Time              | Elapsed Time | Hostname
            -------------------------------------------------------------------------------------
            Flow: Hello Endpoint | PASS   | 2026-10-23  14:56:43.942 | 0.016 s      | GMG-DESKTOP


     Test: Hello Endpoint | Result     

Name         | Test: Hello Endpoint
Status       | PASS
Finish Time  | 2026-10-23  14:56:43.944
Elapsed Time | 0.023 s

The step's configuration table shows its Target Endpoint, localhost, and the Hello from line names the controller's machine.

Running on an endpoint

In the controller, select Hello Endpoint and click the pencil at the right of Test Endpoint. Choose the endpoint:

Click Run and confirm. Changing only the endpoint needs no Update Plan: the plan counts cases, and the endpoint is not one. The step's table now shows the endpoint as its Target Endpoint, and Hello from names that machine:

Before the first step ran, the controller uploaded the suite's code to the endpoint: the Upload Test Package row of the run panel, set to If Missing, does that once and again whenever the code changes. The Packages Uploading page explains it.

Type to search the docs.

    ↑↓ navigate ↵ open Esc close