fix(selectel): nil-guard в ApplyChanges + тесты пагинации rrset и nil-diff
This commit is contained in:
@@ -159,10 +159,16 @@ func (c *Client) ApplyChanges(ctx context.Context, creds provider.Credentials, z
|
||||
}
|
||||
switch d.Kind {
|
||||
case diff.Add:
|
||||
if d.Desired == nil {
|
||||
return fmt.Errorf("selectel: add/update diff without Desired record")
|
||||
}
|
||||
if err := c.do(ctx, http.MethodPost, base, creds.Secret, toRRSet(*d.Desired), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
case diff.Update:
|
||||
if d.Desired == nil {
|
||||
return fmt.Errorf("selectel: add/update diff without Desired record")
|
||||
}
|
||||
id, ok := idByKey[d.Desired.Key()]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot update: rrset %s not found in zone", d.Desired.Key())
|
||||
@@ -171,6 +177,9 @@ func (c *Client) ApplyChanges(ctx context.Context, creds provider.Credentials, z
|
||||
return err
|
||||
}
|
||||
case diff.Delete:
|
||||
if d.Actual == nil {
|
||||
return fmt.Errorf("selectel: delete diff without Actual record")
|
||||
}
|
||||
id, ok := idByKey[d.Actual.Key()]
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot delete: rrset %s not found in zone", d.Actual.Key())
|
||||
|
||||
@@ -266,6 +266,84 @@ func TestListZonesPaginatesAcrossMultiplePages(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Global Constraint: listRRSets (via GetRecords) must paginate across multiple pages,
|
||||
// accumulating records from every page, and must stop as soon as next_offset is 0 —
|
||||
// no third request should ever be issued.
|
||||
func TestGetRecordsPaginatesAcrossMultiplePages(t *testing.T) {
|
||||
var offsets []string
|
||||
c, srv := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
offset := r.URL.Query().Get("offset")
|
||||
offsets = append(offsets, offset)
|
||||
if len(offsets) > 2 {
|
||||
t.Fatalf("too many requests, possible infinite pagination loop: %v", offsets)
|
||||
}
|
||||
switch offset {
|
||||
case "0":
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"result": []map[string]any{
|
||||
{"id": "r1", "name": "a.example.com.", "type": "A", "ttl": 300,
|
||||
"records": []map[string]any{{"content": "1.1.1.1"}}},
|
||||
},
|
||||
"next_offset": 1000,
|
||||
})
|
||||
case "1000":
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"result": []map[string]any{
|
||||
{"id": "r2", "name": "b.example.com.", "type": "A", "ttl": 300,
|
||||
"records": []map[string]any{{"content": "2.2.2.2"}}},
|
||||
},
|
||||
"next_offset": 0,
|
||||
})
|
||||
default:
|
||||
t.Fatalf("unexpected offset %q", offset)
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
recs, err := c.GetRecords(context.Background(), creds(), "z1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(offsets) != 2 {
|
||||
t.Fatalf("expected exactly 2 page requests, got %d: %v", len(offsets), offsets)
|
||||
}
|
||||
if len(recs) != 2 {
|
||||
t.Fatalf("expected accumulated records from both pages, got %+v", recs)
|
||||
}
|
||||
names := map[string]bool{recs[0].Name: true, recs[1].Name: true}
|
||||
if !names["a.example.com."] || !names["b.example.com."] {
|
||||
t.Fatalf("expected records from both pages, got %+v", recs)
|
||||
}
|
||||
}
|
||||
|
||||
// Global Constraint: ApplyChanges must not panic on a Changeset with a nil Desired
|
||||
// record for Add/Update, and must instead return a clear error.
|
||||
func TestApplyChangesAddWithNilDesiredReturnsErrorNoPanic(t *testing.T) {
|
||||
c, srv := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
json.NewEncoder(w).Encode(map[string]any{"result": []map[string]any{}, "next_offset": 0})
|
||||
return
|
||||
}
|
||||
t.Fatalf("unexpected mutating call %s %s, nil Desired should have errored before reaching HTTP", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
cs := diff.Changeset{Diffs: []diff.RecordDiff{
|
||||
{Kind: diff.Add, Type: model.A, Name: "nil-desired.example.com.", Desired: nil},
|
||||
}}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("ApplyChanges panicked on nil Desired: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
err := c.ApplyChanges(context.Background(), creds(), "z1", cs)
|
||||
if err == nil {
|
||||
t.Fatal("expected non-nil error for Add diff with nil Desired")
|
||||
}
|
||||
}
|
||||
|
||||
// Global Constraint: HTTP errors (status >= 300) must surface a non-nil error whose text
|
||||
// includes the method/path/status (or response body) for diagnosability.
|
||||
func TestListZonesHTTPErrorIncludesMethodPathStatus(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user