Skip to content

feat: gcx as the assertion engine#334

Open
zeitlinger wants to merge 109 commits into
mainfrom
v2
Open

feat: gcx as the assertion engine#334
zeitlinger wants to merge 109 commits into
mainfrom
v2

Conversation

@zeitlinger

@zeitlinger zeitlinger commented Jun 12, 2026

Copy link
Copy Markdown
Member

WIP — tracking PR for the OATS v2 major rewrite.

Design + implementation plan: see internal-docs#14.

Direction

v2 replaces the bespoke TraceQL / PromQL / LogQL HTTP query infrastructure with gcx.
This branch no longer carries the old root YAML runner additively: the root oats
binary now routes through the gcx-driven CLI, and the acceptance coverage in this
PR is being migrated onto that path.

What's landed in this branch

Package Purpose
engine/ Execute gcx, capture stdout/stderr/exit/duration
assert/ contains / not_contains / regex / value / count / absent
seed/ Inline-OTLP HTTP POST (no SDK, no example app)
wait/ Generic Until / While polling (replaces gomega.Eventually)
report/ Compact-text + NDJSON renderers, GHA annotations
discovery/ oats.toml parser + glob expansion + filter
runner/ Orchestrates seed → poll-and-assert → report
cache/ Skip-when-unchanged store, TTL-evicted
internal/cli/ Current CLI entry point + fixture lifecycle
migrate/ Legacy yaml → current schema migration helper
tests/e2e/ Real end-to-end coverage for remote / compose / k3d fixtures

All packages have tests; everything green; golangci-lint clean.

What's still to do in this PR

  • Finish remaining review cleanup / wording polish
  • Keep validating the migrated flow against real consumer repos
  • Remove the now-obsolete helper paths that are fully superseded by gcx

Compatibility

This rewrite drops the old public Go API (yaml.ReadTestCases, yaml.RunTestCase) and
the legacy “pass one or more old yaml files directly to oats” runner. Consumers now
migrate via oats --migrate ... plus the new oats.toml + oats-schema-version: 3
case format.

See README.md for current user-facing docs.

Do not merge

This PR will stay in draft until the full v2 plan ships.

Copilot AI review requested due to automatic review settings June 12, 2026 14:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces the initial OATS v2 “gcx-driven” execution stack (polling, case schema, discovery, seeding, assertion vocabulary, runner orchestration, and reporting), alongside a new oats-v2 entrypoint and an end-to-end integration test using a fake gcx.

Changes:

  • Add core v2 runtime packages (engine, signalcmd, seed, wait, assert, runner, report, cache) with unit tests.
  • Add v2 configuration + case parsing/validation via discovery (oats.toml) and v2case (case YAML), plus user-facing V2.md.
  • Add cmd/v2 binary with integration test wiring the full pipeline (fake gcx + httptest OTLP stub).

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
wait/wait.go New polling primitives (Until/While) for v2.
wait/wait_test.go Unit tests for polling semantics.
v2case/case.go v2 case YAML schema, parsing, and validation.
v2case/case_test.go Tests for YAML parsing and validation errors.
V2.md User-facing overview of v2 architecture and config shapes.
signalcmd/signalcmd.go Translate v2 assertions into gcx CLI args + render helper.
signalcmd/signalcmd_test.go Tests for arg building and shell-quoting behavior.
seed/seed.go Inline OTLP/HTTP JSON seeding implementation.
seed/seed_test.go Tests for OTLP send behavior and defaults.
runner/runner.go Orchestrate seed → poll/assert → emit report events + cache integration.
runner/runner_test.go Runner unit tests (stub exec + text reporter assertions).
runner/cache_integration_test.go Integration coverage for skip-when-unchanged cache behavior.
report/text.go Human-readable text reporter + optional GHA annotations.
report/ndjson.go NDJSON event stream reporter with verbosity-based filtering.
report/event.go Event vocabulary + shared types and verbosity enum.
report/report_test.go Tests for text/NDJSON output and annotation deduping.
engine/engine.go gcx executor abstraction + os/exec implementation.
engine/engine_test.go Executor behavior tests (stdout/stderr/exit/timeout/context).
discovery/discovery.go oats.toml loader/validator and plan expansion + filtering.
discovery/discovery_test.go Tests for config validation, glob expansion, and filtering.
cache/cache.go File-backed TTL cache keyed by case+fixture+versions.
cache/cache_test.go Tests for hashing, TTL eviction, corrupt entry behavior, clear/evict.
assert/assert.go Assertion vocabulary (contains, not_contains, regex, value, count, absent).
assert/assert_test.go Unit tests for assertion behavior and error cases.
cmd/v2/main.go New oats-v2 CLI entrypoint wiring discovery → runner → report.
cmd/v2/integration_test.go End-to-end integration test across v2 package boundaries.
cmd/v2/testdata/fake-gcx.sh Fake gcx script for integration tests.
go.mod Add TOML dependency for v2 discovery.
go.sum Dependency checksums for TOML addition.
.github/renovate-tracked-deps.json Track new Go module dependency for Renovate.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go
Comment thread runner/runner.go Outdated
Comment thread seed/seed.go
Comment thread v2case/case.go
Comment thread internal/cli/main.go Outdated
Comment thread report/event.go Outdated
Copilot AI review requested due to automatic review settings June 16, 2026 13:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 30 changed files in this pull request and generated 10 comments.

Comment thread wait/wait.go
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go
Comment thread runner/runner.go
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread internal/cli/main.go
Comment thread internal/cli/main.go
Copilot AI review requested due to automatic review settings June 16, 2026 14:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 33 changed files in this pull request and generated 5 comments.

Comment thread wait/wait.go
Comment thread runner/runner.go
Comment thread runner/runner_test.go Outdated
Comment thread migrate/migrate_test.go Outdated
Comment thread seed/seed.go Outdated
Copilot AI review requested due to automatic review settings June 16, 2026 14:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 37 changed files in this pull request and generated 12 comments.

Comment thread wait/wait.go
Comment thread wait/wait.go Outdated
Comment thread runner/runner.go Outdated
Comment thread report/text.go
Comment thread runner/runner_test.go
Comment thread V2.md Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Copilot AI review requested due to automatic review settings June 16, 2026 14:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 39 changed files in this pull request and generated 10 comments.

Comment thread wait/wait.go
Comment thread runner/runner.go
Comment thread runner/runner_test.go
Comment thread testhelpers/compose/compose.go
Comment thread testhelpers/compose/compose.go Outdated
Comment thread testhelpers/compose/compose.go
Comment thread v2case/case.go Outdated
Comment thread casefile/case.go
Comment thread assert/assert.go Outdated
Comment thread cmd/v2/main.go Outdated
Copilot AI review requested due to automatic review settings June 16, 2026 14:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 39 changed files in this pull request and generated 6 comments.

Comment thread wait/wait.go
Comment thread runner/runner.go
Comment thread runner/runner_test.go
Comment thread migrate/migrate_test.go
Comment thread cmd/v2/main.go Outdated
Comment thread cmd/v2/main.go Outdated
Copilot AI review requested due to automatic review settings June 16, 2026 14:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 42 changed files in this pull request and generated 12 comments.

Comment thread wait/wait.go
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread runner/runner.go Outdated
Comment thread cmd/v2/main.go Outdated
Comment thread cmd/v2/main.go Outdated
Comment thread runner/runner_test.go Outdated
Comment thread migrate/migrate_test.go Outdated
Copilot AI review requested due to automatic review settings June 16, 2026 14:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 46 changed files in this pull request and generated 9 comments.

Comment thread wait/wait.go
Comment thread cmd/v2/main.go Outdated
Comment thread cmd/v2/main.go Outdated
Comment thread report/text.go
Comment thread runner/runner.go
Comment thread runner/runner.go
Comment thread runner/runner_test.go
Comment thread migrate/migrate_test.go
Comment thread migrate/migrate_test.go
Copilot AI review requested due to automatic review settings June 16, 2026 14:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 46 changed files in this pull request and generated 8 comments.

Comment thread runner/runner_test.go
Comment thread migrate/migrate_test.go
Comment thread migrate/migrate_test.go
Comment thread wait/wait.go
Comment thread cmd/v2/main.go Outdated
Comment thread cmd/v2/main.go Outdated
Comment thread seed/seed.go Outdated
Comment thread testhelpers/compose/compose.go Outdated
Copilot AI review requested due to automatic review settings June 16, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 46 out of 47 changed files in this pull request and generated 6 comments.

Comment thread wait/wait.go Outdated
Comment thread wait/wait.go
Comment thread runner/runner_test.go Outdated
Comment thread migrate/migrate_test.go Outdated
Comment thread migrate/migrate_test.go Outdated
Comment thread cmd/v2/main.go Outdated
Copilot AI review requested due to automatic review settings July 7, 2026 10:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 117 changed files in this pull request and generated 3 comments.

Comment thread wait/wait.go
Comment thread wait/wait.go
Comment thread testhelpers/compose/compose.go
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
- internal/cli: remove per-run oats-gcx-*.yaml temp config on fixture
  teardown; fail loudly instead of swallowing the write error
- compose: start docker before the log-consumer goroutine so a failed
  Start can't leak a blocked reader
- wait: capture timer by reference in cleanup defer; fix LastFailures doc
- engine: correct Execute doc (--config/--context prepend order)
- tests: relax wait overshoot bounds for CI, explicit SeedSettleDelay
  units, fix trimmed-vs-untrimmed engine assertions, rename cache test
- docs: fix `oats -v` example (verbosity is -v=N)
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Copilot AI review requested due to automatic review settings July 7, 2026 12:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 116 out of 117 changed files in this pull request and generated 2 comments.

Comment thread testhelpers/compose/compose.go
Comment thread yaml/testcase.go
- cli: --fail-fast stops scheduling cases after the first failure
- e2e: metrics+value pass/fail cases; fail-fast two-case suite
- docs: README assertion reference, seed modes, custom-check contract;
  UPGRADING schema v2->v3 mappings; align example to attribute-list form
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
- assert/profile + profile-fail: query LGTM's Pyroscope self-profile
  (runtime.mcall), no seeding needed — profiles can't be inline-seeded.
  Runs serially so the flamegraph is warm and CPU isn't contended.
- assert/value: widen timeout to 60s (OTLP metric→Prometheus landing
  latency is 15-35s); validated against live otel-lgtm via the e2e harness
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Copilot AI review requested due to automatic review settings July 7, 2026 14:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 128 out of 129 changed files in this pull request and generated 5 comments.

Comment thread testhelpers/compose/compose.go
Comment thread mise.toml
Comment thread tests/e2e/cases/fixture/remote-basic/files/oats.yaml
Comment thread yaml/testcase.go
Comment thread casefile/case_test.go Outdated
- CLI moves to cobra: implicit-run root (bare `oats [flags]` still runs)
  plus run/list/migrate/cache clear/version subcommands. --list/--migrate
  kept as hidden deprecated flag aliases; verbosity is now -v/-vv/-vvv.
- add `oats cache clear` (wipes the cache dir)
- remove the `hermetic:` case field — it parsed but was never enforced
- docs: README CLI section + UPGRADING (migrate subcommand, worked
  legacy→v3 example)
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
- assert/{count,match-regexp,not-contains}-fail: FAIL-path coverage for the
  assertion vocab (not-contains-fail settles first so the forbidden string is
  present, otherwise not_contains trivially holds)
- format/ndjson: assert the NDJSON reporter emits run.end + pass count
- cli/migrate: drive `oats migrate` on a legacy yaml (no fixture needed)
- cli/cache-skip: run a suite twice against one cache dir; second run SKIPs

All validated against live otel-lgtm via the real e2e harness.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
fixture/compose-app-seed boots LGTM + a real instrumented app
(telemetrygen, a prebuilt public image) that exports OTLP traces, then
asserts the span is queryable through gcx. Covers the seed:app path that
inline-otlp cases don't. Validated against a live compose stack.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 144 out of 145 changed files in this pull request and generated 2 comments.

Comment thread wait/wait.go
Comment thread seed/seed.go Outdated
The cache clear subcommand had no end-to-end coverage; only the underlying
Store.Clear() was unit-tested. Add a case that populates the cache with one
green run, runs `oats cache clear`, then reruns and asserts the case
re-executes (no SKIP line) — the sibling cache-skip case proves a rerun would
skip without the clear.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Move suite fixture boot/teardown out of internal/cli into a new pluggable
fixture package (fixture.go, compose.go, k3d.go). The CLI keeps suite
orchestration and reporting; the package owns standing up compose/k3d/remote
backends, waiting for readiness, and parallel-safety.

Exports Runtime, Handle, Start, WaitForReady, SupportsParallel; compose/k3d/
gcx-config/token/port helpers stay unexported and are tested white-box. Pure
refactor, no behavior change — unit, integration, and compose/k3d/remote e2e
all pass unchanged.

The previously dead waitForGrafanaToken seam (only kept alive by test
overrides, never called in production) now has a real unit test.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
- compose: start the docker command before spawning the stdout-reader
  goroutine (and close the pipe on Start error) so a failed Start can't leak a
  goroutine blocked on ReadString.
- seed: make inline-otlp seeding context-aware (http.NewRequestWithContext,
  ctx threaded from Runner.RunCase) so a cancelled run stops seeding instead of
  delaying teardown.
- yaml: propagate the filepath.Abs error in readTestCaseDefinition instead of
  panicking on the user-facing migrate path; document LoadTestCaseDefinition as
  a migrate-only shim, not supported public API.
- go.mod: bump the go directive to 1.26.4 to match the toolchain mise pins.
- wait: correct the Result.LastFailures docstring (it is returned on
  cancel/timeout when a prior asserter call reported failures).
- casefile: rename TestValidate_RejectsPresentFalse to
  TestValidate_RejectsDuplicateAttributeKeys to match what it asserts.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 151 out of 152 changed files in this pull request and generated 2 comments.

Comment thread fixture/fixture.go
Comment thread testhelpers/compose/compose.go
- fixture: give waitForHTTP an http.Client with a 10s timeout so a target
  that accepts TCP but never responds can't block a single probe past the
  overall readiness deadline.
- compose: close the StdoutPipe read end when cmd.Start() fails in the
  logConsumer path, matching the background path and avoiding an FD leak.

Addresses Copilot review comments.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
…mcall

The assert/profile case matched runtime.mcall, a single scheduler frame only
sampled when goroutines happen to be scheduled mid-sample. Under CI load it was
frequently absent from Pyroscope's self-profile, flaking the case (repeated core
shard failures). Match the broader runtime. prefix instead: any non-empty Go CPU
profile contains many runtime.* frames, so the assertion still proves a real
flamegraph came back through oats -> gcx -> Pyroscope without the single-frame
fragility. Timeout unchanged.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 151 out of 152 changed files in this pull request and generated 1 comment.

Comment thread yaml/testcase.go Outdated
filepath.Abs returns an empty string on error, and the previous code
overwrote filePath with it before formatting the error, yielding
"failed to resolve path : ..." and losing the original input. Keep the
original filePath for the message and only assign the resolved path on success.

Addresses Copilot review comment.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@martincostello

Copy link
Copy Markdown
Member

Looks like the required statuses got broken?

@zeitlinger

Copy link
Copy Markdown
Member Author

Looks like the required statuses got broken?

no - it's just got renamed to "build"

@martincostello

Copy link
Copy Markdown
Member

Well, "broken" - the PR is unmergeable without it or the repo settings being updated.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 151 out of 152 changed files in this pull request and generated 1 comment.

Comment on lines 128 to 135
@@ -100,12 +135,13 @@ func (c *Compose) runDocker(cc command) error {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants