Documentation
¶
Overview ¶
Package adofake is a local, hermetic fake of the Azure DevOps HTTP surface Wardyn's ADO broker/gate work (#375) talks to: git smart-HTTP (served by the real `git http-backend`, not reimplemented), and the REST endpoints for branch policies, pull requests, refs, pushes, work items, connection data, projects and the personal-access-token lifecycle.
The fake exists to make a NARROWED token provable without parsing it: every endpoint declares the real Azure DevOps OAuth scope it requires, a test registers which scopes a given bearer/PAT carries, and a request presenting a token that lacks the scope gets back exactly the shape the real service returns — a 401 with typeKey UnauthorizedRequestException. A caller that can only see the fake's responses (not its internals) still learns whether its token was accepted for the right reason.
Index ¶
- Constants
- func NewFixtureRepo(tb testing.TB) string
- type Endpoint
- type Fault
- type PatTokenError
- type RecordedRequest
- type Server
- func (s *Server) AddProject(org, id, name string)
- func (s *Server) ClearOverride(endpoint Endpoint)
- func (s *Server) Close()
- func (s *Server) Count(endpoint Endpoint) int
- func (s *Server) RegisterRepo(org, project, repo, barePath string)
- func (s *Server) RegisterToken(token string, scopes ...string)
- func (s *Server) Requests() []RecordedRequest
- func (s *Server) SetFault(endpoint Endpoint, fault Fault)
- func (s *Server) SetOverride(endpoint Endpoint, status int, body []byte)
- func (s *Server) SetPatCreateError(err PatTokenError)
- func (s *Server) URL() string
Constants ¶
const ( SpacedProject = "Payments Platform" SpacedRepo = "Card Auth (v2).Service" )
SpacedProject and SpacedRepo are a project and a repository named the way Azure DevOps permits and field reports broke on (#485): a space in both, and parentheses and a dot in the repository. The cmd registers them beside its -project/-repo pair, so every lane driving the fake reaches one.
const ( ScopeCodeRead = "vso.code" ScopeCodeWrite = "vso.code_write" ScopeWorkRead = "vso.work" ScopeWorkWrite = "vso.work_write" ScopeProjectRead = "vso.project" ScopeTokens = "vso.tokens" )
The real Azure DevOps OAuth scope literals each endpoint below requires — see https://learn.microsoft.com/azure/devops/integrate/get-started/authentication/oauth for the canonical list. A fake that invented its own names would not prove anything about a token minted against the real scope model.
const SignInPage = `` /* 226-byte string literal not displayed */
SignInPage is the body FaultSignInPage answers with.
Variables ¶
This section is empty.
Functions ¶
func NewFixtureRepo ¶
NewFixtureRepo creates a bare git repository with two commits on `main`, suitable for RegisterRepo — enough history that both a clone and a push (of a third commit) are meaningful. Skips the calling test with a clear message if `git` is not on PATH.
Types ¶
type Endpoint ¶
type Endpoint string
Endpoint names one handler's counter/override slot and its entry in a recorded request — stable strings a test can assert against, independent of the URL shape underneath.
const ( EndpointGitAdvertise Endpoint = "git.advertise" EndpointGitUploadPack Endpoint = "git.upload-pack" EndpointGitReceivePack Endpoint = "git.receive-pack" EndpointPolicyGet Endpoint = "policy.get" EndpointPolicyPost Endpoint = "policy.post" EndpointPolicyPut Endpoint = "policy.put" EndpointPullRequestsPost Endpoint = "pullrequests.post" EndpointPullRequestsPatch Endpoint = "pullrequests.patch" EndpointRefsPost Endpoint = "refs.post" EndpointPushesGet Endpoint = "pushes.get" EndpointPushesPost Endpoint = "pushes.post" EndpointWiqlPost Endpoint = "wiql.post" EndpointWorkItemsPatch Endpoint = "workitems.patch" EndpointConnectionData Endpoint = "connectiondata.get" EndpointProjectsGet Endpoint = "projects.get" EndpointRepositoriesGet Endpoint = "repositories.get" EndpointPatsList Endpoint = "tokens.pats.list" EndpointPatsCreate Endpoint = "tokens.pats.create" EndpointPatsUpdate Endpoint = "tokens.pats.update" EndpointPatsRevoke Endpoint = "tokens.pats.revoke" )
type Fault ¶
type Fault int
Fault is one of the refusal shapes the live Azure DevOps service answers with, as measured (F-LIVE-7): status, the headers that tell the shapes apart, and the body, byte for byte where the service's is short.
const ( // FaultSignInPage is the answer to an absent, malformed or unknown // credential: 203 with a text/html sign-in page (the live page is ~16 KB; // this keeps its doctype and title). FaultSignInPage Fault = iota + 1 // the three are byte-identical: 401, WWW-Authenticate Basic, no // Content-Type, an empty body. FaultBasicUnauthorized // FaultBearerTF400813 is an Entra bearer refused: 401, WWW-Authenticate // Bearer, X-TFS-ServiceError, and a TF400813 body behind a UTF-8 BOM. FaultBearerTF400813 // FaultRepoNotFound is an authenticated identity without read on the // repository: 404 TF401019 with X-VSS-UserData. FaultRepoNotFound // FaultPolicyRejected is an authenticated identity a branch policy // refuses: 403 TF402455 with X-VSS-UserData. FaultPolicyRejected )
type PatTokenError ¶
type PatTokenError string
PatTokenError is the real Azure DevOps Tokens API's patTokenError enum — the value a CreatePat/UpdatePat response carries alongside a nil patToken when creation is refused for a policy reason rather than a scope failure.
const ( PatTokenErrorNone PatTokenError = "none" PatTokenErrorFullScopePolicyViolation PatTokenError = "fullScopePatPolicyViolation" PatTokenErrorAccessDenied PatTokenError = "accessDenied" PatTokenErrorLifespanPolicyViolation PatTokenError = "patLifespanPolicyViolation" )
type RecordedRequest ¶
type RecordedRequest struct {
Method string
Path string
Query string
Headers http.Header
Endpoint Endpoint
RequiredScope string // "" when the endpoint needs no scope
Token string // the bearer/PAT presented, "" if none
Authorized bool // true when no scope was required, or the token carried it
// APIVersion is the request's ?api-version= value, "" if absent. Real
// Azure DevOps requires this on every REST call; this fake does NOT
// refuse its absence (that would break every caller that hasn't wired it
// up yet), but records it so a lane that cares can assert its own client
// actually sent one.
APIVersion string
}
RecordedRequest is one call the fake answered, kept so a test can assert what actually reached the forge — method, path, every header seen, and the scope decision the fake made.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the fake Azure DevOps org. Zero value is not usable; use New.
func NewHandler ¶
NewHandler returns an UNSTARTED fake plus its handler, for a caller that owns its own listener (test/adofake/cmd serves it behind a TLS front). Mirrors entrafake.NewHandler; an unstarted Server's URL is "".
func (*Server) AddProject ¶
AddProject registers a project fixture that GET /{org}/_apis/projects returns; id defaults to name if empty.
func (*Server) ClearOverride ¶
ClearOverride removes a forced response set by SetOverride.
func (*Server) Count ¶
Count returns how many requests endpoint has answered so far (overridden responses count too).
func (*Server) RegisterRepo ¶
RegisterRepo makes barePath (a bare git repository — see NewFixtureRepo, or a caller-built one) reachable at /{org}/{project}/_git/{repo}, the real Azure DevOps git smart-HTTP URL shape. It sets http.receivepack=true on barePath: git http-backend refuses receive-pack for any repo that hasn't opted in, regardless of GIT_HTTP_EXPORT_ALL, so a caller-supplied repo that skipped this would be granted ScopeCodeWrite by the fake and then die 403 inside git for an unrelated config reason. Panics (loudly, at registration time rather than at the first confusing push) if barePath isn't a git directory git can configure.
func (*Server) RegisterToken ¶
RegisterToken makes token (a bearer access token OR a personal access token, presented either as "Authorization: Bearer <token>" or as HTTP Basic with the token as the password) carry exactly scopes, with no expiry — replacing any previous grant for the same token. A PAT minted through handlePatsCreate registers itself the same way, but with the validTo it declared.
func (*Server) Requests ¶
func (s *Server) Requests() []RecordedRequest
Requests returns every recorded request in arrival order.
func (*Server) SetFault ¶
SetFault makes every subsequent call to endpoint answer with fault's shape, the way SetOverride does (scope is still decided and recorded). ClearOverride removes it.
func (*Server) SetOverride ¶
SetOverride forces every subsequent call to endpoint to answer status/body verbatim, bypassing scope enforcement — for exercising a retry or a sweep against a simulated outage. ClearOverride restores normal behaviour.
func (*Server) SetPatCreateError ¶
func (s *Server) SetPatCreateError(err PatTokenError)
SetPatCreateError makes every subsequent PAT-create call answer err instead of minting a token (PatTokenErrorNone, the default, restores normal creation) — for exercising the policy-violation paths a real tenant can return without a real tenant.
Directories
¶
| Path | Synopsis |
|---|---|
|
Command adofake serves test/entrafake AND test/adofake from ONE process, behind a TLS-terminating forward proxy, so the kind walk's Azure DevOps profile (scripts/lib/kind-sso-walk-ado.sh) can reach "login.microsoftonline.com" and "dev.azure.com" by their real names on a cluster with no Microsoft tenant.
|
Command adofake serves test/entrafake AND test/adofake from ONE process, behind a TLS-terminating forward proxy, so the kind walk's Azure DevOps profile (scripts/lib/kind-sso-walk-ado.sh) can reach "login.microsoftonline.com" and "dev.azure.com" by their real names on a cluster with no Microsoft tenant. |