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) } }