Documentation
¶
Overview ¶
Package policytest is the offline tier of testing a policy change (`halo test`, MCP `policy_test`): in seconds, with no network or credentials, it validates the policy, renders every ring for every harness and OS and asserts the security invariants on the output, builds each ring's release and checks its manifest, previews who a change reaches, runs policy-declared cases (the PolicyTest documents under tests/) and simulates every rollout. It reuses internal/policy, internal/release, internal/impact and internal/rollout; it implements no rendering, hashing or gate logic of its own.
Index ¶
Constants ¶
const ( SuiteValidate = "validate" SuiteRender = "render" SuiteRelease = "release" SuiteInvariants = "invariants" SuiteImpact = "impact" SuiteCases = "cases" SuiteRollout = "rollout" )
Check suites, in report order.
const ( // DefaultMaxImpact is the default --max-impact: a change that moves the // ring, experiment variant or effective settings of more than this share // (percent) of the sampled users of any ring fails the impact check. DefaultMaxImpact = 25.0 // DefaultVersion is the release version label tests build with. DefaultVersion = "0.0.0-dev" )
const Kind = "PolicyTest"
Kind is the document kind of a test file (schemas/policytest.schema.json).
Variables ¶
var ErrNoPolicy = errors.New("no policy repo")
ErrNoPolicy is wrapped by Run's error when Dir holds no policy repo.
Functions ¶
func LoadSuites ¶
LoadSuites reads every YAML file under dir/tests. A missing directory is no tests, not an error. Files that do not decode strictly (unknown fields are errors, as everywhere in policy) or fail Check are returned as FileErrors; the rest load.
func Lookup ¶
Lookup resolves a dotted key path ("permissions.defaultMode", "hooks.PreToolUse.0.matcher") in a decoded JSON document. Array elements are addressed by index. A map key that itself contains dots matches as the longest run of path segments that names a key.
func ScanBypass ¶
ScanBypass lists every permission-bypass token (AGENTS.md invariant 1) in a release: the manifest and every blob, whatever its format, found with the one policy.BypassToken scan the validator and the release check use. It covers what CheckRendered's per-file pass does not reach: toggle fragments, unit overlays and cloud agent files.
Types ¶
type Case ¶
type Case struct {
Name string `yaml:"name" json:"name"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Identity Identity `yaml:"identity" json:"identity"`
// OS selects the rendered files settings are read from: darwin, linux
// (default) or windows.
OS string `yaml:"os,omitempty" json:"os,omitempty"`
Expect Expect `yaml:"expect" json:"expect"`
}
Case says what an identity must get from the policy.
type Expect ¶
type Expect struct {
// Ring is the ring the identity lands in. A pointer so that "" can assert
// that no ring matches.
Ring *string `yaml:"ring,omitempty" json:"ring,omitempty"`
// Variants maps a running experiment to the variant the identity gets;
// "" asserts the identity is not enrolled in it.
Variants map[string]string `yaml:"variants,omitempty" json:"variants,omitempty"`
// Settings maps a harness to dotted key paths in its rendered config and
// the value each must have (a null value asserts the key is absent).
Settings map[string]map[string]any `yaml:"settings,omitempty" json:"settings,omitempty"`
}
Expect lists the assertions of a case; at least one is required.
type Identity ¶
type Identity struct {
User string `yaml:"user" json:"user"`
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
}
Identity is the subject a case resolves, as the IdP presents it.
type Options ¶
type Options struct {
Dir string // policy repo
// Ring limits the render, release and invariant checks to one ring
// ("" = every ring). Cases still resolve whatever ring their identity gets.
Ring string
// Against is a git ref of the policy to compare with; it turns on the
// impact check.
Against string
// Strict fails the run on any warning: policy validation warnings and
// adapter warnings in the release manifest.
Strict bool
// MaxImpact is the impact check's threshold in percent (0-100; 100 never
// trips). Set it with DefaultMaxImpact.
MaxImpact float64
// Version is the release version label (default DefaultVersion).
Version string
}
Options configures Run.
type Report ¶
type Report struct {
Dir string `json:"policyDir"`
Org string `json:"org,omitempty"`
OK bool `json:"ok"`
Strict bool `json:"strict"`
Against string `json:"against,omitempty"`
Passed int `json:"passed"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
// Warnings is the count of warnings across all checks.
Warnings int `json:"warnings"`
Results []Result `json:"results"`
Impact *impact.Report `json:"impact,omitempty"`
NextSteps []string `json:"next_steps"`
ElapsedMS int64 `json:"elapsedMs"`
}
Report is the outcome of a run.
func Run ¶
Run executes every offline check. A failed check is recorded in the report (OK false); an error means the run itself could not happen (no policy repo, a bad option, an unreadable git ref, cancellation).
type Result ¶
type Result struct {
Suite string `json:"suite"`
Name string `json:"name"`
Status Status `json:"status"`
Message string `json:"message,omitempty"`
Details []string `json:"details,omitempty"` // why it failed, one line each
Warnings []string `json:"warnings,omitempty"` // surfaced, failing only under Strict
ElapsedMS int64 `json:"elapsedMs"`
}
Result is one check.
type Suite ¶
type Suite struct {
APIVersion string `yaml:"apiVersion" json:"apiVersion"`
Kind string `yaml:"kind" json:"kind"`
Name string `yaml:"name" json:"name"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Cases []Case `yaml:"cases" json:"cases"`
// File is the path relative to the policy dir; set by LoadSuites.
File string `yaml:"-" json:"-"`
}
Suite is one PolicyTest document: a named list of cases.