A web changeset with a staged record delete could not be applied after the record was deleted elsewhere (e.g. with the CLI), and the error only said "record N does not exist", so it was hard to find the operation to remove. A delete of a record that is already gone is now a no-op with a warning. Other operations that no longer apply fail with their number and summary (op_summary in the API), and the Changes page marks that row and offers to remove it from the error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
52 lines
2.2 KiB
Go
52 lines
2.2 KiB
Go
package engine
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"jarl/internal/model"
|
|
)
|
|
|
|
// Records deleted or changed after a change to them was staged (in the
|
|
// CLI while a web UI changeset is open, say).
|
|
func TestStaleChangeset(t *testing.T) {
|
|
env := newEnv(t)
|
|
setupZone(env)
|
|
env.submit(env.admin, rec("example.com", "one", "A", "192.0.2.51"), rec("example.com", "two", "A", "192.0.2.52"), rec("example.com", "three", "A", "192.0.2.53"))
|
|
st := env.state()
|
|
one, two, three := findRec(st, "one.example.com.", "A"), findRec(st, "two.example.com.", "A"), findRec(st, "three.example.com.", "A")
|
|
|
|
// Two deletes staged; one of the records is deleted elsewhere first.
|
|
cs, err := env.e.CreateChangeset(env.ctx, env.admin, "web", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := env.e.Stage(env.ctx, cs.ID, []Request{NewRequest(OpRecordDelete, one.ID, nil), NewRequest(OpRecordDelete, two.ID, nil)}, env.admin); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
env.submit(env.admin, NewRequest(OpRecordDelete, one.ID, nil))
|
|
res, err := env.e.Apply(env.ctx, cs.ID, env.admin, ApplyOptions{})
|
|
if err != nil {
|
|
t.Fatalf("apply after the record was deleted elsewhere: %v", err)
|
|
}
|
|
if len(res.Warnings) != 1 || !strings.Contains(res.Warnings[0], "one.example.com. A 192.0.2.51: the record was already deleted") {
|
|
t.Errorf("warnings = %q", res.Warnings)
|
|
}
|
|
if st := env.state(); st.Records[two.ID] != nil {
|
|
t.Error("two was not deleted")
|
|
}
|
|
|
|
// A staged edit of a record deleted elsewhere names the operation.
|
|
cs, _ = env.e.CreateChangeset(env.ctx, env.admin, "web", "")
|
|
env.e.Stage(env.ctx, cs.ID, []Request{NewRequest(OpRecordUpdate, three.ID, RecordInput{Data: model.Ptr("192.0.2.54")})}, env.admin)
|
|
env.submit(env.admin, NewRequest(OpRecordDelete, three.ID, nil))
|
|
p, err := env.e.Preview(env.ctx, cs.ID, env.admin)
|
|
if err != nil || p.Error == nil || p.Error.OpSeq != 1 || !strings.HasPrefix(p.Error.OpSummary, "change three.example.com. A 192.0.2.53") {
|
|
t.Fatalf("preview = %+v, %v", p, err)
|
|
}
|
|
_, err = env.e.Apply(env.ctx, cs.ID, env.admin, ApplyOptions{})
|
|
if ee, ok := AsError(err); !ok || ee.Kind != KindConflict || !strings.Contains(err.Error(), "operation 1 (change three.example.com. A") || !strings.Contains(err.Error(), "remove this operation") {
|
|
t.Errorf("apply = %v", err)
|
|
}
|
|
}
|