policytest

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

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

View Source
const (
	SuiteValidate   = "validate"
	SuiteRender     = "render"
	SuiteRelease    = "release"
	SuiteInvariants = "invariants"
	SuiteImpact     = "impact"
	SuiteCases      = "cases"
	SuiteRollout    = "rollout"
)

Check suites, in report order.

View Source
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"
)
View Source
const Kind = "PolicyTest"

Kind is the document kind of a test file (schemas/policytest.schema.json).

Variables

View Source
var ErrNoPolicy = errors.New("no policy repo")

ErrNoPolicy is wrapped by Run's error when Dir holds no policy repo.

Functions

func LoadSuites

func LoadSuites(dir string) ([]Suite, []FileError, error)

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

func Lookup(doc any, path string) (any, bool)

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

func ScanBypass(rel *release.Release) []string

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 FileError

type FileError struct {
	File string
	Err  error
}

FileError is a test file that does not decode or validate.

func (FileError) Error

func (e FileError) Error() string

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 Painter

type Painter struct{ Green, Red, Yellow, Bold func(string) string }

Painter colours text output; nil leaves it plain.

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

func Run(ctx context.Context, opt Options) (*Report, error)

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).

func (*Report) JUnit

func (r *Report) JUnit(w io.Writer) error

JUnit writes the report as JUnit XML: one testsuite per check suite, one testcase per check. Warnings go to the case's system-out.

func (*Report) Text

func (r *Report) Text(w io.Writer, p Painter)

Text writes the human summary: one PASS/FAIL/SKIP line per check, failure reasons under it, a totals line and next_steps.

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 Status

type Status string

Status is the outcome of one check.

const (
	Pass Status = "pass"
	Fail Status = "fail"
	Skip Status = "skip"
)

Check outcomes.

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.

func (*Suite) Check

func (s *Suite) Check() error

Check validates a suite's shape; it mirrors schemas/policytest.schema.json (a test compares the two) and also knows the registered harnesses.

Jump to

Keyboard shortcuts

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