package internal import ( "context" "encoding/json" "net/http" "net/http/httptest" "net/url" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func setupTest(t *testing.T) (*Client, *http.ServeMux) { t.Helper() mux := http.NewServeMux() server := httptest.NewServer(mux) t.Cleanup(server.Close) client, err := NewClient("lego") require.NoError(t, err) client.HTTPClient = server.Client() client.baseURL, _ = url.Parse(server.URL) return client, mux } func TestAddRecord(t *testing.T) { testCases := []struct { desc string handler http.HandlerFunc data Record expectError bool }{ { desc: "success", handler: func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "lego", r.Header.Get(pddTokenHeader)) err := r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } assert.Equal(t, `content=txtTXTtxtTXTtxtTXT&domain=example.com&subdomain=foo&ttl=300&type=TXT`, r.PostForm.Encode()) response := AddResponse{ Domain: "example.com", Record: &Record{ ID: 1, Type: "TXT", Domain: "example.com", SubDomain: "foo", FQDN: "foo.example.com.", Content: "txtTXTtxtTXTtxtTXT", TTL: 300, }, BaseResponse: BaseResponse{ Success: "ok", }, } err = json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }, data: Record{ Domain: "example.com", Type: "TXT", Content: "txtTXTtxtTXTtxtTXT", SubDomain: "foo", TTL: 300, }, }, { desc: "error", handler: func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "lego", r.Header.Get(pddTokenHeader)) err := r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } assert.Equal(t, `content=txtTXTtxtTXTtxtTXT&domain=example.com&subdomain=foo&ttl=300&type=TXT`, r.PostForm.Encode()) response := AddResponse{ Domain: "example.com", BaseResponse: BaseResponse{ Success: "error", Error: "bad things", }, } err = json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }, data: Record{ Domain: "example.com", Type: "TXT", Content: "txtTXTtxtTXTtxtTXT", SubDomain: "foo", TTL: 300, }, expectError: true, }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { client, mux := setupTest(t) mux.HandleFunc("/add", test.handler) record, err := client.AddRecord(context.Background(), test.data) if test.expectError { require.Error(t, err) require.Nil(t, record) } else { require.NoError(t, err) require.NotNil(t, record) } }) } } func TestRemoveRecord(t *testing.T) { testCases := []struct { desc string handler http.HandlerFunc data Record expectError bool }{ { desc: "success", handler: func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "lego", r.Header.Get(pddTokenHeader)) err := r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } assert.Equal(t, `domain=example.com&record_id=6`, r.PostForm.Encode()) response := RemoveResponse{ Domain: "example.com", RecordID: 6, BaseResponse: BaseResponse{ Success: "ok", }, } err = json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }, data: Record{ ID: 6, Domain: "example.com", }, }, { desc: "error", handler: func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodPost, r.Method) assert.Equal(t, "lego", r.Header.Get(pddTokenHeader)) err := r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } assert.Equal(t, `domain=example.com&record_id=6`, r.PostForm.Encode()) response := RemoveResponse{ Domain: "example.com", RecordID: 6, BaseResponse: BaseResponse{ Success: "error", Error: "bad things", }, } err = json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }, data: Record{ ID: 6, Domain: "example.com", }, expectError: true, }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { client, mux := setupTest(t) mux.HandleFunc("/del", test.handler) id, err := client.RemoveRecord(context.Background(), test.data) if test.expectError { require.Error(t, err) require.Equal(t, 0, id) } else { require.NoError(t, err) require.Equal(t, 6, id) } }) } } func TestGetRecords(t *testing.T) { testCases := []struct { desc string handler http.HandlerFunc domain string expectError bool }{ { desc: "success", handler: func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodGet, r.Method) assert.Equal(t, "lego", r.Header.Get(pddTokenHeader)) assert.Equal(t, "domain=example.com", r.URL.RawQuery) response := ListResponse{ Domain: "example.com", Records: []Record{ { ID: 1, Type: "TXT", Domain: "example.com", SubDomain: "foo", FQDN: "foo.example.com.", Content: "txtTXTtxtTXTtxtTXT", TTL: 300, }, { ID: 2, Type: "NS", Domain: "example.com", SubDomain: "foo", FQDN: "foo.example.com.", Content: "bar", TTL: 300, }, }, BaseResponse: BaseResponse{ Success: "ok", }, } err := json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }, domain: "example.com", }, { desc: "error", handler: func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodGet, r.Method) assert.Equal(t, "lego", r.Header.Get(pddTokenHeader)) assert.Equal(t, "domain=example.com", r.URL.RawQuery) response := ListResponse{ Domain: "example.com", BaseResponse: BaseResponse{ Success: "error", Error: "bad things", }, } err := json.NewEncoder(w).Encode(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }, domain: "example.com", expectError: true, }, } for _, test := range testCases { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() client, mux := setupTest(t) mux.HandleFunc("/list", test.handler) records, err := client.GetRecords(context.Background(), test.domain) if test.expectError { require.Error(t, err) require.Empty(t, records) } else { require.NoError(t, err) require.Len(t, records, 2) } }) } }