feat:runscope test
Chart Release / chart (push) Successful in 23s
Server Deploy / deploy (push) Failing after 58s

This commit is contained in:
2026-09-09 08:43:55 +00:00
parent 3695bc9e1a
commit a2351c75dd
3 changed files with 162 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
package api
import (
"testing"
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
)
func runFixture() *models.WorkflowRun {
return &models.WorkflowRun{
RunID: "r1",
ServerRuns: []models.ServerRun{
{ServerID: "stg-1", Hostname: "staging-web"},
{ServerID: "prod-1", Hostname: "prod-db"},
},
}
}
// A run document names every host it touched, hostname included. A restricted
// caller must see only its own, and must be told some entries are missing
// without being told how many — the targets_restricted precedent.
func TestScopeRunHidesOutOfScopeServerRuns(t *testing.T) {
got := scopeRun(runFixture(), map[string]bool{"stg-1": true}, true)
if len(got.ServerRuns) != 1 || got.ServerRuns[0].ServerID != "stg-1" {
t.Fatalf("server_runs = %v, want only stg-1", got.ServerRuns)
}
for _, sr := range got.ServerRuns {
if sr.Hostname == "prod-db" {
t.Error("out-of-scope hostname survived filtering")
}
}
if !got.ServersRestricted {
t.Error("servers_restricted = false with an entry dropped")
}
}
func TestScopeRunLeavesUnrestrictedCallerWhole(t *testing.T) {
got := scopeRun(runFixture(), nil, false)
if len(got.ServerRuns) != 2 {
t.Fatalf("server_runs = %v, want both", got.ServerRuns)
}
if got.ServersRestricted {
t.Error("unrestricted caller told entries were restricted")
}
}
// A restricted caller whose scope happens to cover the whole run must not be
// told anything was hidden — the flag is about disclosure, not about being
// restricted in general.
func TestScopeRunNoFlagWhenNothingDropped(t *testing.T) {
got := scopeRun(runFixture(), map[string]bool{"stg-1": true, "prod-1": true}, true)
if got.ServersRestricted {
t.Error("servers_restricted set with nothing dropped")
}
}
+33
View File
@@ -193,3 +193,36 @@ func TestCreateMonitorDecodesTargetFields(t *testing.T) {
t.Errorf("Target.Keyword = %q", m.Target.Keyword)
}
}
// A runner is a server ID. buildMonitor must refuse one outright rather than
// dropping it silently, or a model would believe it had pinned a check to an
// agent it never reached.
func TestBuildMonitorRefusesRunner(t *testing.T) {
args := map[string]any{
"name": "api health",
"type": "http",
"target": map[string]any{"url": "https://example.com"},
"runner": "some-server-id",
}
if _, err := buildMonitor(args); err == nil {
t.Fatal("buildMonitor accepted a runner argument")
}
}
// Without a runner it still builds, and never arms itself.
func TestBuildMonitorWithoutRunnerIsDisabled(t *testing.T) {
m, err := buildMonitor(map[string]any{
"name": "api health",
"type": "http",
"target": map[string]any{"url": "https://example.com"},
})
if err != nil {
t.Fatalf("buildMonitor: %v", err)
}
if m.Runner != "" {
t.Errorf("Runner = %q, want empty so CreateMonitor defaults it to the control plane", m.Runner)
}
if m.Enabled {
t.Error("monitor created enabled")
}
}
@@ -111,3 +111,77 @@ func TestScopedTargetsExcludeOutOfScopeServers(t *testing.T) {
t.Errorf("scoped targets = %v, want only a", got)
}
}
// A workflow run is dispatched to whatever ResolveTargetsScoped returns, so
// the property Critical 1 restores is that a run of a production-targeting
// workflow, started by a staging token, reaches nothing. Proved over
// UnionTargets for the same reason as the test above: no database.
func TestScopedRunOfOutOfScopeWorkflowReachesNothing(t *testing.T) {
all := []models.Server{
{ServerID: "prod-1", Tags: map[string]string{"env": "prod"}},
{ServerID: "prod-2", Tags: map[string]string{"env": "prod"}},
}
scope := map[string]string{"env": "staging"}
visible := []models.Server{}
for _, s := range all {
if ServerInTokenScope(s, scope) {
visible = append(visible, s)
}
}
// The workflow's own saved targets, both by ID and by tag.
got := UnionTargets(visible, []string{"prod-1", "prod-2"}, map[string]string{"env": "prod"})
if len(got) != 0 {
t.Errorf("staging-scoped run resolved %v, want nothing", got)
}
// ResolveTargetsScoped turns that empty set into ErrNoTargets, which is
// the same answer a workflow targeting no servers at all gives — so the
// refusal does not tell the caller that production hosts exist.
}
// The scheduler passes a nil scope because it acts as the system. That must
// keep meaning "the whole fleet", never "nothing", or every scheduled workflow
// would silently stop firing.
func TestNilScopeIsUnrestrictedNotEmpty(t *testing.T) {
all := []models.Server{
{ServerID: "prod-1", Tags: map[string]string{"env": "prod"}},
{ServerID: "stg-1", Tags: map[string]string{"env": "staging"}},
}
visible := []models.Server{}
for _, s := range all {
if ServerInTokenScope(s, nil) {
visible = append(visible, s)
}
}
if len(visible) != 2 {
t.Errorf("nil scope admitted %d servers, want all %d", len(visible), len(all))
}
}
// FilterVisibleServerIDs is what narrows a run document's ServerRuns and a
// workflow's target list. The property that matters is that the caller is told
// something was hidden without being told how much.
func TestFilterVisibleServerIDsReportsHiddenWithoutCount(t *testing.T) {
visible := map[string]bool{"a": true}
got, hidden := FilterVisibleServerIDs([]string{"a", "b", "c"}, visible, true)
if len(got) != 1 || got[0] != "a" {
t.Errorf("filtered = %v, want [a]", got)
}
if !hidden {
t.Error("hidden = false with two ids dropped")
}
// Dropping one and dropping ten are indistinguishable: hidden is a bool.
_, hiddenOne := FilterVisibleServerIDs([]string{"a", "b"}, visible, true)
if hiddenOne != hidden {
t.Error("hidden distinguishes how many were dropped")
}
// An unrestricted caller is never told anything was hidden.
got, hidden = FilterVisibleServerIDs([]string{"a", "b"}, nil, false)
if len(got) != 2 || hidden {
t.Errorf("unrestricted filter = %v, %v; want everything and no hidden flag", got, hidden)
}
}