Move a rule several places in one change

The Rules page has a Move… button that puts a rule before any other of
its chain, or at the end, in one move. Staged in a changeset, a move
right after a move of the same object takes its place, so pressing ↑
three times is one operation; moving it back where it was takes the
operation out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-25 01:33:37 +01:00
1 parent 57ab9881db
commit 5348765d90
6 files changed
+129 -3

No files matched your search

+47
View File
@@ -1336,6 +1336,53 @@ func TestCreatePlaced(t *testing.T) {
}
}
// TestStagedMovesFold: moving a rule several steps in a changeset stays
// one operation, and moving it back where it was takes it out.
func TestStagedMovesFold(t *testing.T) {
e := newEnv(t)
var ids []int64
for _, svc := range []string{"ssh", "dns", "http", "https"} {
var mr api.MutationResponse
var r model.Rule
e.call(e.token, "POST", "/rules", model.Rule{Chain: "input", Match: model.Match{Services: []string{svc}}}, &mr)
json.Unmarshal(mr.Object, &r)
ids = append(ids, r.ID)
}
var cs model.Changeset
e.call(e.token, "POST", "/changesets", nil, &cs)
q := "?changeset=" + itoa(cs.ID)
var mr api.MutationResponse
for _, before := range []int64{ids[2], ids[1], ids[0]} { // https up three steps
if code := e.call(e.token, "POST", "/rules/"+itoa(ids[3])+"/move"+q, api.MoveInput{Before: before}, &mr); code != 200 || !mr.Staged {
t.Fatalf("move before %d: %d %+v", before, code, mr)
}
}
if n := len(mr.Changeset.Ops); n != 1 {
t.Fatalf("%d operations, want 1: %+v", n, mr.Changeset.Ops)
}
// Another operation in between: the next move is its own.
e.call(e.token, "PATCH", "/rules/"+itoa(ids[0])+q, map[string]any{"comment": "x"}, nil)
e.call(e.token, "POST", "/rules/"+itoa(ids[3])+"/move"+q, api.MoveInput{After: ids[0]}, &mr)
if n := len(mr.Changeset.Ops); n != 3 {
t.Errorf("%d operations after a patch and a move, want 3", n)
}
// Back to the start: the same place as before that move, so it goes.
e.call(e.token, "POST", "/rules/"+itoa(ids[3])+"/move"+q, api.MoveInput{To: "start"}, &mr)
if n := len(mr.Changeset.Ops); n != 2 {
t.Errorf("%d operations after moving back, want 2", n)
}
if code := e.call(e.token, "POST", "/changesets/"+itoa(cs.ID)+"/apply", nil, nil); code != 200 {
t.Fatalf("apply: %d", code)
}
var out []string
for _, r := range e.eng.State().Rules {
out = append(out, strings.Join(r.Services, ""))
}
if o := strings.Join(out, ","); o != "https,ssh,dns,http" {
t.Errorf("order: %s", o)
}
}
// TestTokenForUser: an administrator makes a token for another user (the
// web UI's New token on a user's row); that user can't make one for
// someone else.