Skip to content

test: harden Gloas harness and CI - #313

Draft
AntiD2ta wants to merge 6 commits into
review/gloas-events-multi-clientfrom
review/gloas-test-harness-ci
Draft

test: harden Gloas harness and CI#313
AntiD2ta wants to merge 6 commits into
review/gloas-events-multi-clientfrom
review/gloas-test-harness-ci

Conversation

@AntiD2ta

@AntiD2ta AntiD2ta commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

TL;DR

Live and stub coverage for the Gloas HTTP endpoints, the fork gates that decide when the live tests run, and a CI job that makes those gates fail loudly instead of skipping.

Where to start

  1. testclients/forkutil.go (56 lines). The three fork probes everything else is gated on. OnGloas compares the head's fork version against GLOAS_FORK_VERSION. Both are config values, so it needs no block download and does not care whether the node's preset matches the compiled-in one.
  2. http/gloasgate_test.go. The gate, and HTTP_REQUIRE_GLOAS switching it from skip to fail.
  3. http/main_test.go. Harness setup. There is one address knob, HTTP_ADDRESS. HTTP_REQUIRE_GLOAS sets severity only and is not a second endpoint.
  4. The per-endpoint tests: epbsproposal, executionpayloadenvelope, submitproposal, submitexecutionpayloadbid, submitexecutionpayloadenvelope.
  5. .github/workflows/http-tests.yml (30 lines). The only change outside testclients/ and http/.

How to run it

HTTP_ADDRESS=<gloas node> HTTP_REQUIRE_GLOAS=true \
  go test -v -run 'Gloas|EPBS|ExecutionPayload' ./http/...

Without HTTP_ADDRESS, TestMain returns before m.Run, so the package reports ok having run nothing. Worth knowing when reading a green log.

Dependency and review order

Review after the event-forwarding PR. Base is review/gloas-events-multi-client.

Public API

One addition outside a _test.go file. testclients/forkutil.go exports KnowsGloas, OnGloas and HeadVersion, and only package http_test consumes them. Say if you would rather they lived there.

Behaviour-sensitive checks

The Gloas job needs two repository settings before it does anything: the HTTP_ADDRESS_GLOAS variable, and the HTTP_BEARER_TOKEN_GLOAS secret if the endpoint is authenticated. Without the variable the job is skipped, so forks and branches with no devnet to point at are unaffected. With it, the job maps that variable to HTTP_ADDRESS and sets HTTP_REQUIRE_GLOAS=true, so a gate that skips fails the job.

The job's -run pattern is scoped by name rather than running the whole package. Post-Gloas block production is v4-only, so the v3 tests in ./http/... fail permanently against a Gloas node for reasons the job is not about. A new Gloas live test needs a name matching that pattern to be picked up.

Validation

go test ./...: 2,510 tests. Seven packages (spec/phase0, spec/bellatrix, spec/capella, spec/deneb, spec/electra, api/v1, api/v1/deneb) fail locally on go1.27 with jsontext error-string mismatches. None of those files are in this diff, and CI pins go1.25.2.

go test -race -p 1 ./http/... ./testclients/...: pass. go vet and gofmt: clean.

The gloas job has now run against glamsterdam-devnet-8. 25 tests, 0 skips, so the gates resolve and the live tests execute rather than quietly opting out. 24 pass, and TestEPBSProposal fails on 2 of its 4 rows. Neither cause is in this diff:

  • TestEPBSProposal/SSZPayloadExcluded and SSZPayloadIncluded fail with "no Eth-Execution-Payload-Included header in epbs proposal response". Asked with the Accept: application/octet-stream;q=1,application/json;q=0.9 header the client sends, the node answers 200 with content-type: application/octet-stream and sends Eth-Consensus-Version, Eth-Consensus-Block-Value and Eth-Execution-Payload-Value, but no Eth-Execution-Payload-Included. The SSZ path in epbsProposalFromResponse has nowhere else to read the flag, so it cannot succeed against this node. Both JSON rows pass, because the JSON path reads the flag from the body.
  • Live block production on this devnet answers 500 no cached P2P bid available for roughly 6 requests in 10, whichever value include_payload takes. TestSubmitProposalGloas fails on that locally and passed in CI on a luckier slot, so any live production test on a Gloas devnet needs to expect it.

Since the last push

  • OnGloas no longer fetches the head block. Any error on that path was swallowed and made every Gloas live test skip with "head is unknown", so the gate could fail open on a node that does not serve the block endpoint, or whose preset the compiled-in codec cannot decode. Comparing fork versions removes both.
  • The SSZ rows of TestEPBSProposal and the block production in TestSubmitProposalGloas now use a custom-spec service, which is what the comments already claimed.
  • disagreeingService fronts the live node with a reverse proxy and passes WithAddress to New, instead of writing s.base after New had already handed the service to periodicUpdateConnectionState. That write was a data race go test -race reports whenever HTTP_ADDRESS is set.
  • TestEPBSProposal re-reads the head slot per subtest, so the later rows do not ask a devnet to build for a slot it has passed.
  • Assertions moved out of httptest handler goroutines, where a failure calls runtime.Goexit outside the test's goroutine and surfaces as a transport error naming nothing.
  • The envelope sentinel test names the head slot instead of underflowing phase0.Slot when the head is below slot 100.

@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from 192c523 to 803b196 Compare August 5, 2026 11:49
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from 803b196 to 500b9e8 Compare August 5, 2026 12:01
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from 500b9e8 to 4869e14 Compare August 5, 2026 12:25
@Bez625
Bez625 force-pushed the review/gloas-test-harness-ci branch from 4869e14 to f254ad1 Compare August 5, 2026 14:51
@AntiD2ta
AntiD2ta marked this pull request as draft August 5, 2026 15:03
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from f254ad1 to 211a657 Compare August 5, 2026 15:07
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from 211a657 to 4d6f7c4 Compare August 6, 2026 10:12
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from 4d6f7c4 to 9bddf38 Compare August 6, 2026 10:19
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from 9bddf38 to 3d2b53a Compare August 6, 2026 10:28
@Bez625
Bez625 force-pushed the review/gloas-test-harness-ci branch from 3d2b53a to 0634b2b Compare August 6, 2026 13:43
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from a64512e to 432c8f8 Compare August 17, 2026 08:37
@Bez625
Bez625 force-pushed the review/gloas-test-harness-ci branch from 432c8f8 to c1cd5cb Compare August 21, 2026 08:24
@Bez625
Bez625 force-pushed the review/gloas-test-harness-ci branch from c1cd5cb to 79e68a0 Compare August 21, 2026 08:50
Covers the Gloas HTTP endpoints, and adds the machinery that decides when the
live tests among them may run.

testclients/forkutil.go holds the three fork probes: whether the node's config
knows the Gloas fork, whether the chain head is past it, and a diagnostic name
for the head's fork. http/gloasgate_test.go turns those into skips, or into
failures when HTTP_REQUIRE_GLOAS is set, so a suite pointed at a Gloas node can
assert the gated tests actually ran.

The endpoint coverage is split by what it needs. Live tests against
HTTP_ADDRESS exercise EPBSProposal, ExecutionPayloadEnvelope and the Gloas
SubmitProposal path. Stub tests over httptest cover the request shape of
SubmitExecutionPayloadBid and SubmitExecutionPayloadEnvelope, and the
request-consistency guards, without needing a node on the fork.
OnGloas read the head fork by fetching the whole head block and reading its
version. Every caller passes a service built without custom spec support, so on
a minimal-preset devnet the SSZ block is decoded against the mainnet-preset
codec and fails. The error was swallowed, HeadVersion returned "unknown", and
every Gloas live test skipped with "chain is not on gloas (head is unknown)".

Compare the head's fork version from Fork(state: "head") against
GLOAS_FORK_VERSION instead. Both are config values, so the answer no longer
depends on the compiled-in preset, and no block is downloaded. HeadVersion is
now a diagnostic name derived from the spec's *_FORK_VERSION keys, and OnGloas
no longer routes through it.
Several of the new live tests used the shared service, which has no custom spec
support, for SSZ work that cannot decode on the minimal preset this devnet runs:
the SSZ rows of TestEPBSProposal, which contradicted the comment above them, and
TestSubmitProposalGloas, which failed before reaching the publish path it exists
to exercise. Both now go through a custom-spec service.

disagreeingService wrote s.base after New had already handed the service to
periodicUpdateConnectionState, an unsynchronised write against a goroutine that
reads it every 30s, which go test -race reports whenever HTTP_ADDRESS is set.
It now fronts the live node with a reverse proxy and passes WithAddress to New.

Other fixes:

- TestEPBSProposal re-reads the head slot per subtest, rather than computing it
  once for four live block productions the head can advance past.
- Assertions move out of httptest handler goroutines, where a failure calls
  runtime.Goexit outside the test's goroutine, abandons the request, and reports
  as a transport error naming nothing.
- The envelope sentinel test skips with the head slot named instead of
  underflowing phase0.Slot when the head is below slot 100.
- The KnowsGloas gate reason no longer claims the fork is absent when the spec
  may simply have been unreadable.
The harness gates every Gloas live test on the chain being past the fork, and
HTTP_REQUIRE_GLOAS turns those skips into failures. Nothing set it, so the gates
could only ever skip and the bugs fixed in the preceding commits passed CI
unnoticed.

Add a second job that points HTTP_ADDRESS at the HTTP_ADDRESS_GLOAS repository
variable and sets HTTP_REQUIRE_GLOAS. It is skipped when that variable is empty,
so forks and branches with no devnet to point at are unaffected. The run is
scoped by name: post-Gloas block production is v4-only, so the v3 rows of the
package fail permanently against a Gloas node for reasons this job is not about.
@AntiD2ta
AntiD2ta force-pushed the review/gloas-test-harness-ci branch from 8ed628b to 8567ce0 Compare August 26, 2026 09:45
AntiD2ta added a commit that referenced this pull request Aug 28, 2026
http/events_internal_test.go drove Service.handleEvent directly with synthetic
sse.Event values.  This is a pure deletion: 200 lines out, none in.

It leaves handleEvent with no test that calls it, including the Gloas dispatch
arms the next commit adds.  Nothing in this repo replaces that coverage, and
neither does the stacked PR #313.
AntiD2ta added a commit that referenced this pull request Aug 28, 2026
http/events_internal_test.go drove Service.handleEvent directly with synthetic
sse.Event values.  This is a pure deletion: 200 lines out, none in.

It leaves handleEvent with no test that calls it, including the Gloas dispatch
arms the next commit adds.  Nothing in this repo replaces that coverage, and
neither does the stacked PR #313.
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.

1 participant