Documentation
¶
Overview ¶
Package testing provides integration test helpers for Deployah scenarios.
Scenario directories under scenarios/ hold sample specs, environment files, expected Kubernetes output, and optional e2e.yaml Kind fixtures. DiscoverScenarios finds them; IntegrationTestSuite loads a spec, generates a chart, renders templates, and compares results to golden files. LoadE2EFixture decodes e2e.yaml.
Index ¶
- Constants
- Variables
- func DiffContainsByKey(path string, want, got []any) []string
- func DiffSubset(path string, want, got any) []string
- func RunPlanScenarioTest(t *testing.T, scenario PlanTestScenario)
- type DeleteOp
- type DeployOp
- type E2EFixture
- type IntegrationTestSuite
- type LogsOp
- type PlanConfig
- type PlanConfigChange
- type PlanConfigField
- type PlanConfigSummary
- type PlanTestScenario
- type ResourceAssertion
- type RunOp
- type Step
- type TestScenario
Constants ¶
const DefaultE2EStepTimeout = 3 * time.Minute
DefaultE2EStepTimeout is the wait applied when e2e.yaml omits timeout.
const E2EFixtureFile = "e2e.yaml"
E2EFixtureFile is the Kind fixture filename in a scenario directory.
Variables ¶
var AllowedE2EImages = []string{
"nginx:latest",
"nginx:1.26",
"busybox:1.36",
"redis:7-alpine",
"redis:7",
"postgres:16",
}
AllowedE2EImages is the Kind preload list. LoadE2EFixture rejects scenario specs that reference any other image.
var TestScenariosDir = getTestScenariosDir()
TestScenariosDir is the directory containing integration test scenarios.
Functions ¶
func DiffContainsByKey ¶ added in v0.8.0
DiffContainsByKey reports paths where each object in want is not found among got by identity key ("name", then "type") and recursive subset match. Extra got items are ignored. An empty want matches anything.
func DiffSubset ¶ added in v0.8.0
DiffSubset reports JSON-pointer-style paths where want is not a subset of got. An empty result means every value in want is present in got.
Maps: only keys present in want are compared; extra keys in got are ignored. A nil value in want (YAML null) requires the key to be absent or nil in got. Scalar slices must match in length and order. An empty want slice matches any got slice. Object slices (elements that are maps) are matched by identity key "name", then "type"; extra got elements are ignored.
func RunPlanScenarioTest ¶ added in v0.4.0
func RunPlanScenarioTest(t *testing.T, scenario PlanTestScenario)
RunPlanScenarioTest resolves scenario's previous/current manifest pair, computes the diff through the real plan engine, and checks the result against plan-config.yaml (and, when present, golden.txt/golden.json).
Types ¶
type E2EFixture ¶ added in v0.8.0
type E2EFixture struct {
Env string `json:"env"`
Parallel *bool `json:"parallel,omitempty"`
Timeout string `json:"timeout,omitempty"`
Resources []ResourceAssertion `json:"resources,omitempty"`
Steps []Step `json:"steps,omitempty"`
}
E2EFixture is the decoded form of scenarios/*/e2e.yaml, requiring exactly one of Resources or Steps.
func LoadE2EFixture ¶ added in v0.8.0
func LoadE2EFixture(path, scenarioDir string) (E2EFixture, error)
LoadE2EFixture reads path, validates document shape, and checks that images in scenarioDir specs are on AllowedE2EImages.
func (E2EFixture) RunParallel ¶ added in v0.8.0
func (f E2EFixture) RunParallel() bool
RunParallel reports whether the fixture may run concurrently. A nil Parallel field means true.
func (E2EFixture) StepTimeout ¶ added in v0.8.0
func (f E2EFixture) StepTimeout(step Step) (time.Duration, error)
StepTimeout is the per-step wait: step override, then fixture default, then DefaultE2EStepTimeout.
type IntegrationTestSuite ¶
type IntegrationTestSuite struct {
// ScenariosDir is the root directory of test scenarios.
ScenariosDir string
// OutputDir is the temporary workspace for a test run.
OutputDir string
}
IntegrationTestSuite runs scenario-based chart and manifest tests.
func NewIntegrationTestSuite ¶
func NewIntegrationTestSuite(t *testing.T) *IntegrationTestSuite
NewIntegrationTestSuite creates a suite rooted at TestScenariosDir.
func (*IntegrationTestSuite) RunScenarioTest ¶
func (suite *IntegrationTestSuite) RunScenarioTest(t *testing.T, scenario TestScenario)
RunScenarioTest executes a test for a specific scenario
type PlanConfig ¶ added in v0.4.0
type PlanConfig struct {
// FreshInstall selects an empty previous manifest instead of resolving
// one from before.yaml or previous.yaml, for the fresh-install case
// where neither file is expected to exist.
FreshInstall bool `yaml:"freshInstall"`
// Changes lists every expected change, in the exact order
// [plan.ComputeDiff] returns them (sorted by Kind, then Name).
Changes []PlanConfigChange `yaml:"changes"`
// Summary is the expected change tally.
Summary PlanConfigSummary `yaml:"summary"`
// HooksChanged asserts [plan.Plan.HooksChanged]. True when hook tasks
// are present on a fresh install (Helm reports hook manifests).
HooksChanged bool `yaml:"hooksChanged"`
// Masked lists field paths (as they appear in a Changes[].Fields[]
// entry's Path) that [plan.ApplyMasking] must flag as masked. Every
// masked path [RunPlanScenarioTest] finds in the computed plan must
// appear here, and vice versa.
Masked []string `yaml:"masked"`
// Warning is documentary only: this offline harness has no live/faked
// release store to compute a real warning from ([plan.LastSuccessfulRelease]
// is covered separately in history_test.go), so the test runner just
// sets Header.Warning to this value before checking it.
Warning string `yaml:"warning"`
}
PlanConfig is the structural expectation a scenario author writes by hand in plan-config.yaml: what plan.ComputeDiff must produce for this scenario's before/after pair, independent of how the text or JSON renderers format it.
type PlanConfigChange ¶ added in v0.4.0
type PlanConfigChange struct {
Action string `yaml:"action"`
Kind string `yaml:"kind"`
Name string `yaml:"name"`
Fields []PlanConfigField `yaml:"fields"`
}
PlanConfigChange is one expected entry in PlanConfig.Changes.
type PlanConfigField ¶ added in v0.4.0
type PlanConfigField struct {
Path string `yaml:"path"`
Old string `yaml:"old"`
New string `yaml:"new"`
}
PlanConfigField is one expected field-level difference within a PlanConfigChange. Old is empty for an added field; New is empty for a removed field, mirroring plan.FieldDiff.
type PlanConfigSummary ¶ added in v0.4.0
type PlanConfigSummary struct {
Add int `yaml:"add"`
Change int `yaml:"change"`
Destroy int `yaml:"destroy"`
}
PlanConfigSummary is the expected plan.Summary.
type PlanTestScenario ¶ added in v0.4.0
type PlanTestScenario struct {
// Name is the scenario directory name (e.g. "plan-image-bump").
Name string
// Dir is the scenario's absolute directory path.
Dir string
}
PlanTestScenario describes one scenarios/plan-* directory: a before/after manifest pair (however each side is sourced) that RunPlanScenarioTest diffs through the real plan.ComputeDiff engine and checks against plan-config.yaml.
func DiscoverPlanScenarios ¶ added in v0.4.0
func DiscoverPlanScenarios(scenariosDir string) ([]PlanTestScenario, error)
DiscoverPlanScenarios finds every scenarios/plan-* directory with a plan-config.yaml. Unlike DiscoverScenarios, deployah.yaml is optional: RunPlanScenarioTest falls back to a raw current.yaml for kinds the spec/chart pipeline can't produce (e.g. a Secret).
type ResourceAssertion ¶ added in v0.8.0
type ResourceAssertion struct {
Match map[string]any `json:"match"`
MinCount *int `json:"minCount,omitempty"`
}
ResourceAssertion is one cluster assertion. Match is a partial Kubernetes object compared with DiffSubset. MinCount defaults to 1; 0 requires the list to be empty. When Match sets metadata.name, MinCount may only be 0 or 1 because a named Get cannot count list items.
func (ResourceAssertion) Count ¶ added in v0.8.0
func (r ResourceAssertion) Count() int
Count returns the minimum matching resources, or 1 when MinCount is unset.
type RunOp ¶ added in v0.8.0
type RunOp struct {
Task string `json:"task"`
}
RunOp maps to `deployah run`.
type Step ¶ added in v0.8.0
type Step struct {
Deploy *DeployOp `json:"deploy,omitempty"`
Run *RunOp `json:"run,omitempty"`
Logs *LogsOp `json:"logs,omitempty"`
Delete *DeleteOp `json:"delete,omitempty"`
Timeout string `json:"timeout,omitempty"`
StderrContains string `json:"stderrContains,omitempty"`
StdoutContains string `json:"stdoutContains,omitempty"`
Resources []ResourceAssertion `json:"resources,omitempty"`
}
Step is one CLI operation plus optional output and resource assertions. Exactly one of Deploy, Run, Logs, or Delete must be set.
type TestScenario ¶
type TestScenario struct {
// Name is the scenario directory name.
Name string
// ScenarioDir is the relative path under TestScenariosDir.
ScenarioDir string
// ManifestFile is the manifest filename within ScenarioDir.
ManifestFile string
// Environment selects the manifest environment for chart generation.
Environment string
// EnvFiles lists dotenv files to copy into the test workspace.
EnvFiles []string
// PlatformFile is the platform filename within ScenarioDir when present.
PlatformFile string
// ExpectedDir is the golden output directory, relative to TestScenariosDir.
ExpectedDir string
// ExpectError requires manifest loading or resolution to fail.
ExpectError bool
// ExpectedErrors requires specific substrings in the load/resolve error message.
ExpectedErrors []string
// HasE2EFixture is true when the scenario directory contains e2e.yaml.
HasE2EFixture bool
// E2EFixturePath is the absolute path to e2e.yaml when HasE2EFixture is true.
E2EFixturePath string
}
TestScenario describes one integration test case under scenarios/.
func DiscoverScenarios ¶
func DiscoverScenarios(scenariosDir string) ([]TestScenario, error)
DiscoverScenarios discovers test scenarios from the directory structure.