brokertest

package
v0.0.0-...-c54b712 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package brokertest provides a controllable HTTP stub for the GitHub Actions broker protocol used in integration tests.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Server

type Server struct {
	URL string
	// contains filtered or unexported fields
}

Server is a test HTTP server that implements the broker v2 protocol endpoints.

func New

func New() *Server

New creates and starts a new broker Stub. Call Close when done.

func (*Server) AcknowledgeCalls

func (s *Server) AcknowledgeCalls() int

AcknowledgeCalls returns the number of delete-message ("acknowledge") calls served — DELETE {poolBase}/messages/{id}. The probe issues this call only after AcquireJob has returned client-side, so observing it reach 1 guarantees the AcquireJob round-trip completed and its context is safe to cancel (Q258).

func (*Server) AcquireJobCalls

func (s *Server) AcquireJobCalls() int

AcquireJobCalls returns the number of /acquirejob calls the stub has fully served — like CompleteJobCalls, the counter is published only after the call's fan-out accounting is committed, so waiting on it and then reading the accounting is race-free.

func (*Server) ActiveSessionCount

func (s *Server) ActiveSessionCount() int

ActiveSessionCount returns the number of goroutines that have registered a session but not yet called DELETE /session. It is computed as (#POST /session − #DELETE /session) so each listener goroutine contributes +1 on start and −1 on exit, regardless of v2 mode.

func (*Server) ActiveSessionsForOwner

func (s *Server) ActiveSessionsForOwner(name string) []string

ActiveSessionsForOwner returns the IDs of currently-active sessions owned by the runner name of the given stem. A listener owns its session as its own registered runner name, so a session matches when its ownerName is that stem, a "-", and a decimal index. The index segment is matched exactly rather than by prefix, so a stem that extends this one ("<name>-set") keeps its own bucket. Scoping by owner lets a test assert on only its own CR's sessions, immune to sessions other tests left active on this shared stub — the global RegisteredSessions/ActiveSessionCount counters accumulate across the whole package and cause cross-test flakes when used for exact-count assertions.

The stem is the registered name's, not the CR's, and the two differ by kind: pass "<name>" for a RunnerGroup and "rs-<name>" for a RunnerSet, matching what Q466 kind-scoped and Q677 carried onto the wire. A same-named group and set are now separable, which they were not before Q677.

func (*Server) Close

func (s *Server) Close()

Close shuts down the stub server.

func (*Server) CompleteJobCalls

func (s *Server) CompleteJobCalls() int

CompleteJobCalls returns the number of /completejob calls the stub has FULLY SERVED — the counter is published only after the call's effects are committed, so waiting on it and then reading LastCompleteJob or the fan-out accounting is race-free. The AGC issues completejob for a deduplicated duplicate delivery it abandons (Q260 follow-up), so a test can assert the loser released its dangling assignment.

It counts calls, not resolved deliveries: a call whose body never arrives (the client's context was cancelled mid-request) is served and counted, yet resolves nothing. Assert on DeliveryResults when what you mean is "these deliveries are resolved" (Q490).

func (*Server) DeliveryResults

func (s *Server) DeliveryResults(planID string) map[string]broker.TaskResult

DeliveryResults returns, for the fan-out job identified by planID, each delivery's resolved completejob result keyed by its RunnerRequestID — only deliveries a completejob has resolved appear. Empty when the job is unknown or none resolved. It lets a test assert the winner completed each deduped sibling delivery keyed on its OWN RunnerRequestID with the expected result (Q260 Option A). Read-only.

func (*Server) EnableFanoutAccounting

func (s *Server) EnableFanoutAccounting()

EnableFanoutAccounting turns on the per-delivery fan-out job-accounting model (Q260). Off by default. Call once before enqueuing a fan-out job. When on, the server tracks a logical job per planID with one assignment per delivery and only concludes the job when the accounting is reconciled — modeling GitHub's real fan-out completion semantics that the default stub omits (the gap that let the Q260 dedup pass envtest yet wedge production).

func (*Server) EnqueueFanoutJob

func (s *Server) EnqueueFanoutJob(planID string, n int) []string

EnqueueFanoutJob registers one logical job (planID) that GitHub fans out to n sibling sessions as n deliveries with DISTINCT RunnerRequestIDs. The deliveries are handed to pollers on GET /message (one per poll, to whichever sessions poll), so a burst of n concurrent pollers each receives one delivery of the same job — exactly the shape the planID dedup must collapse. Returns the n RunnerRequestIDs. Requires EnableFanoutAccounting. Safe to call once per planID.

func (*Server) EnqueueJob

func (s *Server) EnqueueJob(sessionID string, payload broker.RunnerJobRequestBody)

EnqueueJob places a job message onto the given session's queue. The RunServiceURL in the payload is overridden to point back to the stub so that /acquirejob calls come back here.

func (*Server) ExpireUnstartedDeliveries

func (s *Server) ExpireUnstartedDeliveries(planID string)

ExpireUnstartedDeliveries fires GitHub's ~15-minute unstarted-job timeout deterministically (no real timer): if any delivery of the job was acquired but never resolved with a terminal result, the whole job is CANCELLED — GitHub is still waiting on that phantom assignment even though a sibling already ran the job. A no-op once the job has concluded. This is the mechanism that turns the Q260 dedup's silently-abandoned sibling deliveries into a cancelled job.

func (*Server) FailCreateSessionForOwner

func (s *Server) FailCreateSessionForOwner(prefix string)

FailCreateSessionForOwner makes POST /session return 401 Unauthorized for any session whose ownerName has the given prefix, simulating a broker that rejects a tenant's session creation. createSession maps the 401 to a NonRetriableError, so the listener's permanent baseline exits without being auto-restarted — letting a test drive the controller's baseline-revival path (Q137). An empty prefix clears the override. The prefix is matched against ownerName, which is the listener's registered runner name and so kind-scoped: pass "<name>-" for a RunnerGroup and "rs-<name>-" for a RunnerSet (Q677) to scope it to one CR's pool.

func (*Server) GetMessageCalls

func (s *Server) GetMessageCalls() int

GetMessageCalls returns the number of GET /message polls served. The stub answers 202 at once rather than holding the poll, so a caller with no pacing of its own shows up here as a request storm — the rate a poll-loop test measures against.

func (*Server) HTTPClient

func (s *Server) HTTPClient() *http.Client

HTTPClient returns an *http.Client suitable for use with the stub server. Since the stub uses a real TCP listener via httptest, the default client works and the unbounded read timeout is harmless — the test bounds the call (Q138).

func (*Server) JobState

func (s *Server) JobState(planID string) string

JobState returns the accounting state of the logical fan-out job: "queued", "in_progress", "completed", "failed", or "cancelled" (or "" if unknown). See EnableFanoutAccounting.

func (*Server) LastCompleteJob

func (s *Server) LastCompleteJob() (broker.CompleteJobRequest, bool)

LastCompleteJob returns the request body of the most recent /completejob call, and false if none has been received. AuthToken is never populated (the client sends it as a header, not in the body).

func (*Server) RegisteredSessions

func (s *Server) RegisteredSessions() []string

RegisteredSessions returns the IDs of sessions that are currently active (i.e. POST /session was called but DELETE /session has not been called yet). Deleted sessions from prior tests are not included.

func (*Server) RenewJobCalls

func (s *Server) RenewJobCalls() int

RenewJobCalls returns the number of times /renewjob was called.

func (*Server) SetAcquireJobResponse

func (s *Server) SetAcquireJobResponse(v any)

SetAcquireJobResponse configures the JSON body returned by the next /acquirejob call. Pass nil to reset to the default response. The value is serialised with json.Marshal.

func (*Server) WaitForFirstPoll

func (s *Server) WaitForFirstPoll(sessionID string, timeout time.Duration) bool

WaitForFirstPoll blocks until the session with the given ID sends its first GET /message request, or until the timeout elapses. Returns true on success. Use this to confirm a listener goroutine has fully started (passed createSession and entered the poll loop) before simulating SIGTERM, so the goroutine is guaranteed to have registered its cleanup defer and will send DELETE /session.

func (*Server) WaitForSessionDelete

func (s *Server) WaitForSessionDelete(sessionID string, timeout time.Duration) bool

WaitForSessionDelete blocks until the given sessionID is deleted via DELETE /session or the timeout elapses. Returns true if the session was deleted in time. If the DELETE already arrived before this call, returns true immediately.

Jump to

Keyboard shortcuts

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