package gandi import ( "io" "io/ioutil" "net/http" "net/http/httptest" "regexp" "strings" "testing" "github.com/go-acme/lego/v3/platform/tester" "github.com/stretchr/testify/require" ) var envTest = tester.NewEnvTest(EnvAPIKey) func TestNewDNSProvider(t *testing.T) { testCases := []struct { desc string envVars map[string]string expected string }{ { desc: "success", envVars: map[string]string{ EnvAPIKey: "123", }, }, { desc: "missing api key", envVars: map[string]string{ EnvAPIKey: "", }, expected: "gandi: some credentials information are missing: GANDI_API_KEY", }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { defer envTest.RestoreEnv() envTest.ClearEnv() envTest.Apply(test.envVars) p, err := NewDNSProvider() if len(test.expected) == 0 { require.NoError(t, err) require.NotNil(t, p) require.NotNil(t, p.config) require.NotNil(t, p.inProgressFQDNs) require.NotNil(t, p.inProgressAuthZones) } else { require.EqualError(t, err, test.expected) } }) } } func TestNewDNSProviderConfig(t *testing.T) { testCases := []struct { desc string apiKey string expected string }{ { desc: "success", apiKey: "123", }, { desc: "missing credentials", expected: "gandi: no API Key given", }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { config := NewDefaultConfig() config.APIKey = test.apiKey p, err := NewDNSProviderConfig(config) if len(test.expected) == 0 { require.NoError(t, err) require.NotNil(t, p) require.NotNil(t, p.config) require.NotNil(t, p.inProgressFQDNs) require.NotNil(t, p.inProgressAuthZones) } else { require.EqualError(t, err, test.expected) } }) } } // TestDNSProvider runs Present and CleanUp against a fake Gandi RPC // Server, whose responses are predetermined for particular requests. func TestDNSProvider(t *testing.T) { // serverResponses is the XML-RPC Request->Response map used by the // fake RPC server. It was generated by recording a real RPC session // which resulted in the successful issue of a cert, and then // anonymizing the RPC data. var serverResponses = map[string]string{ // Present Request->Response 1 (getZoneID) present1RequestMock: present1ResponseMock, // Present Request->Response 2 (cloneZone) present2RequestMock: present2ResponseMock, // Present Request->Response 3 (newZoneVersion) present3RequestMock: present3ResponseMock, // Present Request->Response 4 (addTXTRecord) present4RequestMock: present4ResponseMock, // Present Request->Response 5 (setZoneVersion) present5RequestMock: present5ResponseMock, // Present Request->Response 6 (setZone) present6RequestMock: present6ResponseMock, // CleanUp Request->Response 1 (setZone) cleanup1RequestMock: cleanup1ResponseMock, // CleanUp Request->Response 2 (deleteZone) cleanup2RequestMock: cleanup2ResponseMock, } fakeKeyAuth := "XXXX" regexpDate := regexp.MustCompile(`\[ACME Challenge [^\]:]*:[^\]]*\]`) // start fake RPC server fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, "text/xml", r.Header.Get("Content-Type"), "invalid content type") req, errS := ioutil.ReadAll(r.Body) require.NoError(t, errS) req = regexpDate.ReplaceAllLiteral(req, []byte(`[ACME Challenge 01 Jan 16 00:00 +0000]`)) resp, ok := serverResponses[string(req)] require.True(t, ok, "Server response for request not found") _, errS = io.Copy(w, strings.NewReader(resp)) require.NoError(t, errS) })) defer fakeServer.Close() // define function to override findZoneByFqdn with fakeFindZoneByFqdn := func(fqdn string) (string, error) { return "example.com.", nil } config := NewDefaultConfig() config.BaseURL = fakeServer.URL + "/" config.APIKey = "123412341234123412341234" provider, err := NewDNSProviderConfig(config) require.NoError(t, err) // override findZoneByFqdn function savedFindZoneByFqdn := provider.findZoneByFqdn defer func() { provider.findZoneByFqdn = savedFindZoneByFqdn }() provider.findZoneByFqdn = fakeFindZoneByFqdn // run Present err = provider.Present("abc.def.example.com", "", fakeKeyAuth) require.NoError(t, err) // run CleanUp err = provider.CleanUp("abc.def.example.com", "", fakeKeyAuth) require.NoError(t, err) }