Skip to main content

SUT topology template

Use this optional template when your UI test environment depends on several services and a single sut_preset block in test_execution.yml becomes too cramped.

The idea is simple:

  • keep .ogoron/configs/test_execution.yml focused on execution profiles and thin SUT hooks
  • describe multi-service topology in a separate manifest
  • let a small orchestration layer read that manifest and implement up, healthcheck, and down

In practice, this template is most useful for the Ogoron UI track, where ogoron prepare ui-workspace, ogoron run smoke, and ogoron run ui-tests need a consistent view of the environment.

When to use it

This template becomes useful when your environment includes combinations such as:

  • several backends and several frontends
  • different runtime styles across services (shell, compose, external)
  • heavy local services that are sometimes reused from a shared environment
  • different service wiring in local and ci

Suggested file location

Recommended location:

.ogoron/configs/sut_topology.yml

This file is optional and separate from test_execution.yml.

File shape

The template uses four main ideas:

  • defaults
    • common timeout values
  • profiles
    • named runtime situations such as local, ci, or shared-env
  • services
    • named services with dependencies
  • modes
    • different ways to operate the same service (shell, compose, external, ...)

Each service mode may define:

  • start
  • stop
  • healthcheck
  • readiness
  • base_url
  • env
  • env_files

Design notes

Important decisions in this template:

  • no groups in the first version
  • external is a first-class mode
  • healthcheck.type: none is allowed only when declared explicitly with a reason
  • depends_on lives on services, not on profiles
  • the goal is not to recreate Docker Compose; the goal is to define a Docker-agnostic orchestration contract

Template

Copy this template into .ogoron/configs/sut_topology.yml and adapt the service names, commands, URLs, and environment values for your project:

version: 1

# Optional multi-service SUT topology template for Ogoron-owned UI testing.
#
# Use this file when a repository needs to describe:
# - multiple services
# - multiple runtime profiles (`local`, `ci`, `shared-env`, ...)
# - different execution modes per service (`shell`, `compose`, `external`, ...)
#
# Keep `test_execution.yml` thin.
# Let `test_execution.yml` call an orchestration layer that reads this topology.

defaults:
startup_timeout_seconds: 90
shutdown_timeout_seconds: 30
healthcheck_timeout_seconds: 5

profiles:
local:
description: Developer workstation profile.
service_modes:
backend-a: shell
backend-b: shell
frontend-main: shell
frontend-docs: shell
frontend-public: shell

ci:
description: CI-oriented profile.
service_modes:
backend-a: compose
backend-b: shell
frontend-main: compose
frontend-docs: compose
frontend-public: compose

shared-env:
description: Reuse services that are already running elsewhere.
service_modes:
backend-a: external
backend-b: external
frontend-main: external
frontend-docs: external
frontend-public: external

services:
backend-a:
role: backend
cwd: systems/backend-a
depends_on: []

modes:
shell:
start:
cmd: ["./run-backend-a.sh"]
stop:
strategy: process_group
healthcheck:
type: http
url: http://127.0.0.1:33141/health
base_url: http://127.0.0.1:33141
env_files:
- ./.env.local

compose:
start:
cmd: ["docker", "compose", "up", "-d", "backend-a"]
stop:
cmd: ["docker", "compose", "down"]
healthcheck:
type: http
url: http://127.0.0.1:33141/health
base_url: http://127.0.0.1:33141

external:
start:
skip: true
stop:
skip: true
healthcheck:
type: http
url: ${BACKEND_A_BASE_URL}/health
base_url: ${BACKEND_A_BASE_URL}

backend-b:
role: backend
cwd: systems/backend-b
depends_on: [backend-a]

modes:
shell:
start:
cmd: ["python", "-m", "uvicorn", "src.main:app", "--host", "127.0.0.1", "--port", "33142"]
stop:
strategy: process_group
healthcheck:
type: http
url: http://127.0.0.1:33142/healthz
readiness:
type: http
url: http://127.0.0.1:33142/readyz
base_url: http://127.0.0.1:33142
env:
FEATURE_FLAG_EXAMPLE: "1"

external:
start:
skip: true
stop:
skip: true
healthcheck:
type: http
url: ${BACKEND_B_BASE_URL}/healthz
readiness:
type: http
url: ${BACKEND_B_BASE_URL}/readyz
base_url: ${BACKEND_B_BASE_URL}

frontend-main:
role: frontend
cwd: systems/frontend-main
depends_on: [backend-a, backend-b]

modes:
shell:
start:
cmd: ["npm", "run", "dev", "--", "--host", "127.0.0.1", "--port", "33143"]
stop:
strategy: process_group
healthcheck:
type: http
url: http://127.0.0.1:33143
base_url: http://127.0.0.1:33143
env:
VITE_BACKEND_A_BASE_URL: http://127.0.0.1:33141
VITE_BACKEND_B_BASE_URL: http://127.0.0.1:33142

compose:
start:
cmd: ["docker", "compose", "up", "-d", "frontend-main"]
stop:
cmd: ["docker", "compose", "down"]
healthcheck:
type: http
url: http://127.0.0.1:33143
base_url: http://127.0.0.1:33143

external:
start:
skip: true
stop:
skip: true
healthcheck:
type: http
url: ${FRONTEND_MAIN_BASE_URL}
base_url: ${FRONTEND_MAIN_BASE_URL}

frontend-docs:
role: frontend
cwd: systems/frontend-docs
depends_on: []

modes:
shell:
start:
cmd: ["npm", "run", "start", "--", "--port", "33144"]
stop:
strategy: process_group
healthcheck:
type: http
url: http://127.0.0.1:33144
base_url: http://127.0.0.1:33144

compose:
start:
cmd: ["docker", "compose", "up", "-d", "frontend-docs"]
stop:
cmd: ["docker", "compose", "down"]
healthcheck:
type: http
url: http://127.0.0.1:33144
base_url: http://127.0.0.1:33144

external:
start:
skip: true
stop:
skip: true
healthcheck:
type: http
url: ${FRONTEND_DOCS_BASE_URL}
base_url: ${FRONTEND_DOCS_BASE_URL}

frontend-public:
role: frontend
cwd: systems/frontend-public
depends_on: []

modes:
shell:
start:
cmd: ["npm", "run", "dev", "--", "--host", "127.0.0.1", "--port", "33145"]
stop:
strategy: process_group
healthcheck:
type: http
url: http://127.0.0.1:33145
base_url: http://127.0.0.1:33145

compose:
start:
cmd: ["docker", "compose", "up", "-d", "frontend-public"]
stop:
cmd: ["docker", "compose", "down"]
healthcheck:
type: http
url: http://127.0.0.1:33145
base_url: http://127.0.0.1:33145

external:
start:
skip: true
stop:
skip: true
healthcheck:
type: http
url: ${FRONTEND_PUBLIC_BASE_URL}
base_url: ${FRONTEND_PUBLIC_BASE_URL}

shared-cdn:
role: external_dependency
cwd: .
depends_on: []

modes:
external:
start:
skip: true
stop:
skip: true
healthcheck:
type: none
reason: No dedicated readiness signal; availability is verified indirectly by consumer tests.
base_url: ${SHARED_CDN_BASE_URL}

If you prefer to download the same file directly, it is also available as:

How to adapt it

Replace the neutral service names with your own:

  • backend-a
  • backend-b
  • frontend-main
  • frontend-docs
  • frontend-public

Then adjust:

  • cwd
  • depends_on
  • cmd
  • base_url
  • health and readiness endpoints
  • environment variables

Relationship to test_execution.yml

Keep test_execution.yml small.

For example, a future SUT preset might call a dedicated orchestration entrypoint:

sut:
default_preset: qa
presets:
qa:
up:
- name: sut-up
cmd: ["./scripts/sut", "up", "--profile", "local"]
healthcheck:
- name: sut-healthcheck
cmd: ["./scripts/sut", "healthcheck", "--profile", "local"]
down:
- name: sut-down
cmd: ["./scripts/sut", "down", "--profile", "local"]