gtool

module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2026 License: Apache-2.0

README ΒΆ

gtool - component testing orchestrator for microservices

GTOOL - Component Testing Orchestrator

🌐 Language: English · Español

πŸ“š Documentation: oswaldom-code.github.io/gtool

Go Version Tests Pipeline License

A Go CLI to orchestrate microservice component tests: spin up mocks, launch the app, run the tests and clean everything up β€” with a single command.


🎯 What GTOOL does

GTOOL is a CLI orchestrator for component testing of microservices. It stands up a service's external dependencies as mocks, launches the service, runs its test suite against that isolated environment, and tears everything down β€” reproducibly, with a single command. It replaces hand-managed Docker setups and ad-hoc shell scripts with one typed Go binary featuring structured logging, strict config validation and a plugin architecture.

graph LR
    A[πŸ”§ Mocks] --> B[πŸš€ App]
    B --> C[πŸ§ͺ Karate Tests]
    C --> D[πŸ“Š HTML Report]
    D --> E[🧹 Cleanup]

    style A fill:#4fc3f7
    style B fill:#66bb6a
    style C fill:#ffa726
    style D fill:#ab47bc
    style E fill:#ef5350

A single gtool test runs the full pipeline:

  1. Mocks β€” starts the third-party dependencies in Docker. 10 built-in services: PostgreSQL, MySQL, MongoDB, Redis, Kafka, Pub/Sub, Couchbase, GCS, Floci (AWS: S3, SQS) and Mountebank.
  2. App β€” launches the service under test, as a Docker image or as native binaries.
  3. Tests β€” runs the Karate (backend API) suite against the app and its mocks.
  4. Report β€” generates the Karate HTML report and can open it in the browser.
  5. Cleanup β€” always tears down app and mocks, even on failure or Ctrl-C.

Beyond the pipeline, GTOOL also:

  • Stress-tests the app (gtool stress) β€” reuses mocks β†’ app, ramps the load over targets resolved from an OpenAPI contract, reports the degradation and breaking points and gates CI on a minimum sustained load.
  • Runs unit tests (gtool unit) β€” mock generation + Ginkgo.
  • Drives each phase on its own β€” gtool services, gtool app, gtool test karate.

New mock services plug in through the ServicePlugin interface.


πŸš€ Installation

# Latest release
go install github.com/oswaldom-code/gtool/cmd/gtool@latest

# Karate launcher image (not published; build it once per machine)
docker build -t gtool/test-launcher-back:latest \
  https://github.com/oswaldom-code/gtool.git#v1.0.0:test-launcher

# From source
git clone https://github.com/oswaldom-code/gtool.git && cd gtool
make build              # builds ./bin/gtool with version, commit and build date
./bin/gtool version --full
make install            # copies the binary to $(go env GOPATH)/bin
make test-launcher-image  # builds the Karate launcher image

go install does not inject build metadata, so gtool version --full reports commit dev; build with make build when you need it (it is also recorded in the stress reports).

⚠️ make install copies to $GOPATH/bin (~/go/bin). If your terminal can't find gtool after installing, that directory is not on your PATH. Add it:

echo 'export PATH="$PATH:$GOPATH/bin"' >> ~/.zshrc   # or ~/.bashrc
source ~/.zshrc && rehash

Requirements: Go 1.26+, Docker, Make.

AI coding agents: the repository ships an Agent Skill that teaches agents (Claude Code, Codex, Cursor, Gemini CLI and others) to install gtool and wire it into a service: npx skills add oswaldom-code/gtool --skill gtool.


⚑ Quick Start

# 1. Validate the repo configuration
gtool config validate --config component-config.yml

# 2. Start the mocks only
gtool services up
gtool services status
gtool services down

# 3. Full pipeline (mocks β†’ app β†’ tests β†’ cleanup)
gtool test

GTOOL looks for ./component-config.yml by default. Use --config <file> for another one.


πŸ“– Commands

Every command accepts --config <file>, --log-level debug|info|warn|error (also GTOOL_LOG_LEVEL or observability.log-level) and --verbose (debug). Output uses symbols only on an interactive terminal; with a pipe, NO_COLOR or CI it is plain text. gtool completion <shell> generates shell autocompletion.

gtool config β€” configuration
gtool config validate --config component-config.yml   # validates the schema
gtool config show --format yaml                        # prints the resolved config
gtool config show --format json
gtool services (alias s) β€” third-party mocks
gtool services up                     # starts every mock in the config
gtool s up postgresql kafka           # starts specific services
gtool s up --stable                   # fixed-contract mocks seeded from test/component/mocks-data
gtool s status                        # services status
gtool s logs postgresql --tail 200 -f # logs of a service; -f follows one service until Ctrl-C (--all for every service)
gtool s down                          # stops all (only containers without a run ID; --all-runs for every run)
gtool app β€” application under test
gtool app start --docker-image myapp:latest --port 8080 --env LOG_LEVEL=debug
gtool app start --native              # build-config.yml binaries as native processes
gtool app status
gtool app logs --tail 100
gtool app restart                     # --native restarts the native binaries
gtool app stop
gtool unit (alias u) β€” unit tests

Generates the mocks from build-config.yml (mockgen) and runs the suite with Ginkgo, leaving coverage and the JUnit report in ./coverage.

gtool unit
gtool unit --skip-mocks               # runs the tests only
gtool unit --build-config build-config.yml
gtool test β€” component pipeline
gtool test                            # gtool-native pipeline (public images); --tags overrides test-config.tags
gtool test --stable --tags "@smoke"   # fixed-contract mocks + native app + Karate
gtool test karate                     # Karate only (mocks and app already up, explicit ports)
gtool test karate --tags "@smoke" --reports ./out
gtool test karate --stable --no-open  # fixed-contract runner, after services up --stable + app start --native

gtool test karate reads test-config and the ports of the config file like gtool test; --tags, --features and --reports override it, and --image overrides the launcher image. --urls-to-block and --no-open only apply with --stable.

gtool stress β€” stress testing
gtool stress plan --config configs/stress-example.yml   # resolved targets, no load
gtool stress --config configs/stress-example.yml        # mocks β†’ app β†’ load ramp β†’ reports β†’ cleanup
gtool stress --target-url https://staging.example.com   # remote target, skips mocks and app

Ramps the load in steps over targets resolved from an OpenAPI contract (Swagger 2.0 / OpenAPI 3.x) and/or an explicit targets list, and reports the degradation point (p95 over a factor of the baseline or an absolute SLO, or throughput below 90% of the requested rate) and the breaking point (5xx + network error rate over the threshold). Writes stress-report.json (stable schema for CI) and a self-contained stress-report.html, and exits non-zero when the sustained load is below min-acceptable-rps. See the stress-config example.

gtool generate (alias g) / gtool version
gtool generate config                 # generates a sample component-config.yml
gtool version --full                  # version, git commit and build date

🧩 Configuration

GTOOL uses two files (.yml extension preferred; .yaml supported). ${VAR} and $VAR references in values are expanded from the environment after parsing (unset variables are left as written, $$ is a literal $). gtool test and gtool services up / app start validate the file before starting anything; gtool test fails when no config file is found.

component-config.yml β€” component pipeline
version: v1
app-technology: golang            # golang | generic
app-config:
  docker-image: myapp:latest      # the app under test runs as a container
  port: 8080                      # port the app listens on inside the container
  host-port: auto                 # free host port per run (omit = same as port)
  health-path: /health            # polled until it answers before tests run (default /)
  environment:
    DB_HOST: host.docker.internal
    DB_PORT: "${mocks.postgresql.port}"   # this run's mock port
test-launcher: test-launcher-back
test-config:
  features-path: ./test/component/features   # required by gtool test
  reports-path: ./test/component/reports
third-party:
  mocks: [postgresql, pubsub]
  mock-config:
    postgresql:
      port: auto                  # any port key (port, *-port) accepts auto
    pubsub:
      project-id: my-project
      topics: [my-topic]
      subscriptions:
        - name: my-sub
          topic: my-topic

Unknown mock-config keys, invalid ports and two port keys of one mock on the same host port are validation errors.

--stable mode reads Pub/Sub topics in its own layout instead:

    pubsub:
      project-id: my-project
      topics:
        - topic-id: my-topic
          subscription-ids: [my-sub]
Dynamic ports and concurrent runs
  • port: auto (mocks) and host-port: auto (app) pick free host ports per run, so several gtool test runs share one host (e.g. a CI agent).
  • ${mocks.<service>.<port-key>}, ${app.port} and ${app.host-port} in app-config.environment are replaced with this run's values; mocks that do not set a port expose their default.
  • Karate receives GTOOL_APP_URL, GTOOL_<SERVICE>_PORT and GTOOL_<SERVICE>_{USER,PASSWORD,DATABASE}; features use * url appUrl and the launcher helpers follow these ports.
  • Every gtool test / gtool stress run labels and names its containers with a run ID (GTOOL_RUN_ID, or a generated one), so one run's teardown never touches another. Without GTOOL_RUN_ID, services and app commands only act on containers of no run; --all-runs widens them to every run.
  • auto only works in single-invocation pipelines (gtool test, gtool stress): separate services up / app start commands cannot share allocated ports and reject it.
stress-config β€” stress testing (for gtool stress)
stress-config:
  source:
    openapi: api/swagger.yml        # Swagger 2.0 or OpenAPI 3.x
    methods: [GET]                  # default: GET only
    include-tags: [products]
    exclude-paths: ["/health"]
    headers: { Authorization: "Bearer ${STRESS_TOKEN}" }
    param-values: { productId: ["1", "2", "3"] }
    operation-overrides:
      createOrder: { body-file: test/stress/order.json, weight: 3 }
  targets: []                       # explicit targets, override spec operations
  ramp: { start-rps: 10, step-rps: 10, step-duration: 20s, max-rps: 500 }
  thresholds: { degradation-latency-factor: 2.0, degradation-p95-ms: 800, break-error-rate: 0.05 }
  min-acceptable-rps: 100
  reports-path: test/stress/reports
build-config.yml β€” Go binaries and mocks (for gtool unit)
version: v5
build:
  binaries:
    - name: api
      path: cmd/server/main.go
mocks:
  - source: internal/service/foo_interface.go
    filename: foo_interface.go

πŸ§ͺ Examples

examples/ holds self-contained sample apps (each with its own go.mod, Dockerfile, component-config.yml and Karate features) that exercise the pipeline end-to-end against one mock each:

Example Mock
postgres-app PostgreSQL β€” also the gtool stress sample (configs/stress-example.yml)
mysql-app MySQL
mongodb-app MongoDB
redis-app Redis
kafka-app Kafka
pubsub-app Pub/Sub
gcs-app GCS
s3-app Floci (S3)
mountebank-app Mountebank (stubbed external API)

examples/ci/jenkins runs a local Jenkins that executes the example pipelines with gtool.


πŸ—οΈ Architecture

graph TB
    User[πŸ‘€ User] --> CLI[CLI - Cobra]
    CLI --> Orch[Orchestrator]
    Orch --> Mock[Mock Manager]
    Orch --> App[App Manager]
    Orch --> Test[Test Runner]
    Orch -.-> Stress[Stress Runner]
    Mock --> Plugins[Service Plugins]
    Plugins --> Docker[Docker Client]
    App --> Docker
    Test --> Docker

    style CLI fill:#b3e5fc
    style Orch fill:#81d4fa
    style Mock fill:#4fc3f7
    style App fill:#4fc3f7
    style Test fill:#4fc3f7
    style Stress fill:#4fc3f7
  • Plugins (internal/plugin/): ServicePlugin (mocks), registered in a thread-safe plugin.Registry; defaults and accepted keys in internal/plugin/services/defaults.go.
  • App (internal/core/app): the app as a container (DockerManager) or as native processes (nativeapp).
  • Orchestrator (internal/core/orchestrator): runs mocks β†’ app β†’ test phase and always tears down. The test phase is a TestRunner: the Karate runner for gtool test, the stress runner (internal/core/stress, vegeta + libopenapi) for gtool stress.
  • Typed errors (pkg/errors) and structured logging with Zap (pkg/logger).

πŸ› οΈ Development

make build                # builds ./bin/gtool
make test                 # tests with -race
make test-coverage        # HTML coverage report
make lint                 # golangci-lint
make staticcheck          # staticcheck
make trivy                # dependency vulnerability scan
make fmt                  # go fmt + goimports
make test-launcher-image  # builds the Karate launcher image
make release              # cross-platform binaries in bin/releases
make clean                # cleans artifacts
make help                 # lists every target

Standards: minimum 80% coverage for new code (95%+ for config/orchestration), Ginkgo/Gomega or table-driven tests, errors from pkg/errors, structured logging with Zap, conventional commits.

Integration tests (require Docker) per plugin:

go test -tags=integration ./internal/plugin/services/...

⚠️ Known limitations

  • Appβ†’mock networking (native gtool test). The default pipeline runs the app as a bridge-network container while the mocks publish their ports on the host, so a containerized app cannot reach a mock at localhost. gtool maps host.docker.internal to the host gateway on the app container, so point the app at host.docker.internal:<mock port> on Linux and Docker Desktop alike. On Linux hosts with a firewall that drops incoming traffic from the Docker bridge (for example ufw with its default policy), allow it with sudo ufw allow in on docker0. The --stable / --native path runs the app as native processes that reach the mocks over 127.0.0.1.
  • Fixed ports for some mocks. Mountebank imposter ports come from the imposter files and are published on the same host port (not auto); Couchbase's data port must stay 11210 because the cluster advertises its container ports.
  • Stress results are host-relative. The load generator, the app and the mocks share the host, so gtool stress numbers are for comparing commits, not production capacity estimates; use --target-url for serious measurements. Container CPU/memory metrics are not collected yet.

πŸ“¦ Status

Functional end-to-end pipeline: 10 mock plugins, app launcher (Docker and native), Karate runner, stress testing and orchestration with guaranteed teardown.

Phase Status
1. Foundation (CLI, config, plugins, errors, logging) βœ…
2. Mocks (10 plugins) βœ…
3. App (Docker + native) βœ…
4. Test Executor (Karate) βœ…
5. Orchestration (pipeline + teardown) βœ…
Stress testing (gtool stress) βœ…
6–7. Advanced features, docs/release πŸ”„

Directories ΒΆ

Path Synopsis
cmd
gtool command
internal
cli
cli/cliconfig
Package cliconfig loads the configuration for CLI commands with an explicit policy, so every command treats a missing or invalid file the same way.
Package cliconfig loads the configuration for CLI commands with an explicit policy, so every command treats a missing or invalid file the same way.
cli/unit
Package unit implements gtool unit: it generates the mocks declared in build-config.yml and runs the unit-test suite with Ginkgo, producing a coverage profile and a JUnit report.
Package unit implements gtool unit: it generates the mocks declared in build-config.yml and runs the unit-test suite with Ginkgo, producing a coverage profile and a JUnit report.
core/app/nativeapp
Package nativeapp runs the app under test as native processes: it starts the project binaries built into $GOPATH/bin as detached processes, each on an incrementing port with the emulator host env vars, and stops them again.
Package nativeapp runs the app under test as native processes: it starts the project binaries built into $GOPATH/bin as detached processes, each on an incrementing port with the emulator host env vars, and stops them again.
core/mock/stablemocks
Package stablemocks launches the third-party mocks for the component pipeline using the official public images for each tool, with a fixed docker run contract (network, ports, mounts, container names and readiness) so `gtool services up --stable` provides a reproducible mock environment that seeds data from test/component/mocks-data.
Package stablemocks launches the third-party mocks for the component pipeline using the official public images for each tool, with a fixed docker run contract (network, ports, mounts, container names and readiness) so `gtool services up --stable` provides a reproducible mock environment that seeds data from test/component/mocks-data.
core/ports
Package ports resolves dynamic host ports for one gtool run and exposes the resulting values to the app (placeholders) and to the test runner (env).
Package ports resolves dynamic host ports for one gtool run and exposes the resulting values to the app (placeholders) and to the test runner (env).
core/readiness
Package readiness waits for an HTTP service to accept requests.
Package readiness waits for an HTTP service to accept requests.
core/test/stablekarate
Package stablekarate runs the Karate launcher for the fixed-contract mode: it runs the image on the host network against the already-running app and mocks, mounting test/component/features and writing the HTML report to the host, and can open that report in a browser.
Package stablekarate runs the Karate launcher for the fixed-contract mode: it runs the image on the host network against the already-running app and mocks, mounting test/component/features and writing the HTML report to the host, and can open that report in a browser.
infra/termout
Package termout prints user-facing CLI output.
Package termout prints user-facing CLI output.
plugin/services/svcconfig
Package svcconfig holds parsing helpers shared by the mock service plugins.
Package svcconfig holds parsing helpers shared by the mock service plugins.
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL