package selectel import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/vasyakrg/dns-autoresolver/internal/diff" "github.com/vasyakrg/dns-autoresolver/internal/model" "github.com/vasyakrg/dns-autoresolver/internal/provider" ) func creds() provider.Credentials { return provider.Credentials{Secret: "secret-token"} } func newTestClient(h http.Handler) (*Client, *httptest.Server) { srv := httptest.NewServer(h) return &Client{BaseURL: srv.URL, HTTP: srv.Client()}, srv } func TestListZonesSendsTokenAndParses(t *testing.T) { var gotToken string c, srv := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotToken = r.Header.Get("X-Auth-Token") json.NewEncoder(w).Encode(map[string]any{ "result": []map[string]any{ {"id": "z1", "name": "example.com."}, {"id": "z2", "name": "test.org."}, }, "next_offset": 0, }) })) defer srv.Close() zs, err := c.ListZones(context.Background(), creds()) if err != nil { t.Fatal(err) } if gotToken != "secret-token" { t.Fatalf("token not sent, got %q", gotToken) } if len(zs) != 2 || zs[0].ID != "z1" || zs[1].Name != "test.org." { t.Fatalf("unexpected zones: %+v", zs) } } func TestGetRecordsMapsRRSet(t *testing.T) { c, srv := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]any{ "result": []map[string]any{ {"id": "r1", "name": "example.com.", "type": "MX", "ttl": 3600, "records": []map[string]any{{"content": "10 mx1.example.com.", "disabled": false}}}, {"id": "r2", "name": "www.example.com.", "type": "A", "ttl": 300, "records": []map[string]any{{"content": "1.2.3.4"}, {"content": "5.6.7.8", "disabled": true}}}, }, "next_offset": 0, }) })) defer srv.Close() recs, err := c.GetRecords(context.Background(), creds(), "z1") if err != nil { t.Fatal(err) } if len(recs) != 2 { t.Fatalf("want 2 records, got %d", len(recs)) } var a model.Record for _, r := range recs { if r.Type == model.A { a = r } } // disabled record dropped -> only one value if len(a.Values) != 1 || a.Values[0] != "1.2.3.4" { t.Fatalf("disabled record must be skipped, got %+v", a.Values) } } func TestApplyChangesRoutesVerbs(t *testing.T) { type call struct{ method, path string } var calls []call c, srv := newTestClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // GET rrset -> return existing set with ids for update/delete resolution if r.Method == http.MethodGet { json.NewEncoder(w).Encode(map[string]any{ "result": []map[string]any{ {"id": "up1", "name": "b.example.com.", "type": "A", "ttl": 300, "records": []map[string]any{{"content": "9.9.9.9"}}}, {"id": "del1", "name": "d.example.com.", "type": "A", "ttl": 300, "records": []map[string]any{{"content": "4.4.4.4"}}}, }, "next_offset": 0, }) return } calls = append(calls, call{r.Method, r.URL.Path}) w.WriteHeader(http.StatusOK) })) defer srv.Close() add := model.Record{Type: model.A, Name: "c.example.com.", TTL: 300, Values: []string{"3.3.3.3"}} updDesired := model.Record{Type: model.A, Name: "b.example.com.", TTL: 300, Values: []string{"2.2.2.2"}} delActual := model.Record{Type: model.A, Name: "d.example.com.", TTL: 300, Values: []string{"4.4.4.4"}} ns := model.Record{Type: model.NS, Name: "example.com.", TTL: 3600, Values: []string{"ns1.example.com."}} cs := diff.Changeset{Diffs: []diff.RecordDiff{ {Kind: diff.Add, Type: add.Type, Name: add.Name, Desired: &add}, {Kind: diff.Update, Type: updDesired.Type, Name: updDesired.Name, Desired: &updDesired}, {Kind: diff.Delete, Type: delActual.Type, Name: delActual.Name, Actual: &delActual}, {Kind: diff.Update, Type: ns.Type, Name: ns.Name, Desired: &ns, ReadOnly: true}, // must be skipped }} if err := c.ApplyChanges(context.Background(), creds(), "z1", cs); err != nil { t.Fatal(err) } want := map[string]bool{ "POST /zones/z1/rrset": true, "PATCH /zones/z1/rrset/up1": true, "DELETE /zones/z1/rrset/del1": true, } if len(calls) != len(want) { t.Fatalf("want %d calls, got %v", len(want), calls) } for _, cl := range calls { if !want[cl.method+" "+cl.path] { t.Fatalf("unexpected call %s %s", cl.method, cl.path) } } }