Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions e2e/harness/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,52 @@ func AssertState(ctx *ExecutionContext, env string, expect *StateExpect) []error
env, expect.Version, actual.Version))
}

// Check integration-branch ref (exact match).
if expect.Ref != "" && actual.Ref != expect.Ref {
errs = append(errs, fmt.Errorf("state[%s].ref expected %s, got %s",
env, expect.Ref, actual.Ref))
}

// Check base SHA (resolve commit references, falling back to literal).
if expect.BaseSHA != "" {
expectedBase := ctx.ResolveSHA(expect.BaseSHA)
if expectedBase == "" {
expectedBase = expect.BaseSHA
}
if actual.BaseSHA != expectedBase {
errs = append(errs, fmt.Errorf("state[%s].base_sha expected %s, got %s",
env, expectedBase, actual.BaseSHA))
}
}

// Check patches: every listed patch (resolved via reference, falling back to
// literal) must be present in the recorded patches slice.
for _, p := range expect.Patches {
want := ctx.ResolveSHA(p)
if want == "" {
want = p
}
if !containsString(actual.Patches, want) {
errs = append(errs, fmt.Errorf("state[%s].patches missing %s, got %v",
env, want, actual.Patches))
}
}

// Check patches_contain: each entry must match at least one recorded patch
// by substring (no reference resolution).
for _, frag := range expect.PatchesContain {
if !anyContains(actual.Patches, frag) {
errs = append(errs, fmt.Errorf("state[%s].patches has no entry containing %q, got %v",
env, frag, actual.Patches))
}
}

// Check pre-divergence version (exact match).
if expect.PreviousVersion != "" && actual.PreviousVersion != expect.PreviousVersion {
errs = append(errs, fmt.Errorf("state[%s].previous_version expected %s, got %s",
env, expect.PreviousVersion, actual.PreviousVersion))
}

// Check deploys
for deployName, deployExpect := range expect.Deploys {
actualDeploy := actual.Deploys[deployName]
Expand All @@ -261,6 +307,26 @@ func AssertState(ctx *ExecutionContext, env string, expect *StateExpect) []error
return errs
}

// containsString reports whether s appears exactly in list.
func containsString(list []string, s string) bool {
for _, v := range list {
if v == s {
return true
}
}
return false
}

// anyContains reports whether any element of list contains substr.
func anyContains(list []string, substr string) bool {
for _, v := range list {
if strings.Contains(v, substr) {
return true
}
}
return false
}

// AssertJobs validates job conclusions against expectations (used internally)
// Returns errors instead of failing test directly for more flexible error handling
func AssertJobs(actual map[string]string, expect map[string]string) []error {
Expand Down
35 changes: 32 additions & 3 deletions e2e/harness/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ type EnvState struct {
SHA string
Version string
Deploys map[string]*DeployState
// Ref is the integration branch the environment tracks instead of trunk.
Ref string
// BaseSHA is the trunk anchor the integration branch diverged from.
BaseSHA string
// Patches lists the patch commit SHAs applied on top of BaseSHA.
Patches []string
// PreviousVersion is the version held before the latest divergence update.
// Harness-side only; not read back from the product manifest.
PreviousVersion string
}

// DeployState tracks per-deploy state
Expand Down Expand Up @@ -163,6 +172,22 @@ func (c *ExecutionContext) RecordState(env, sha, version string) {
c.state[env].Version = version
}

// RecordStateDivergence records the integration-branch divergence fields for an
// environment (ref, base SHA, patch list, and pre-divergence version) without
// disturbing the recorded SHA/Version. Used by manifest sync and setup staging
// so divergence assertions can observe a hotfixed environment.
func (c *ExecutionContext) RecordStateDivergence(env, ref, baseSHA string, patches []string, previousVersion string) {
c.mu.Lock()
defer c.mu.Unlock()
if c.state[env] == nil {
c.state[env] = &EnvState{Deploys: make(map[string]*DeployState)}
}
c.state[env].Ref = ref
c.state[env].BaseSHA = baseSHA
c.state[env].Patches = append([]string(nil), patches...)
c.state[env].PreviousVersion = previousVersion
}

// ClearState removes all env state. Used by sync routines that need to
// rebuild ctx from an authoritative source (the manifest), so deletions in
// that source (e.g. finalize wiping state[prerelease] on publish) are
Expand Down Expand Up @@ -250,9 +275,13 @@ func (c *ExecutionContext) Clone() *ExecutionContext {
clone.commitSeq = append([]string{}, c.commitSeq...)
for k, v := range c.state {
clone.state[k] = &EnvState{
SHA: v.SHA,
Version: v.Version,
Deploys: make(map[string]*DeployState),
SHA: v.SHA,
Version: v.Version,
Deploys: make(map[string]*DeployState),
Ref: v.Ref,
BaseSHA: v.BaseSHA,
Patches: append([]string(nil), v.Patches...),
PreviousVersion: v.PreviousVersion,
}
for dk, dv := range v.Deploys {
clone.state[k].Deploys[dk] = &DeployState{SHA: dv.SHA}
Expand Down
Loading
Loading