1package gandi 2 3import ( 4 "io" 5 "io/ioutil" 6 "net/http" 7 "net/http/httptest" 8 "regexp" 9 "strings" 10 "testing" 11 12 "github.com/go-acme/lego/v3/platform/tester" 13 "github.com/stretchr/testify/require" 14) 15 16var envTest = tester.NewEnvTest(EnvAPIKey) 17 18func TestNewDNSProvider(t *testing.T) { 19 testCases := []struct { 20 desc string 21 envVars map[string]string 22 expected string 23 }{ 24 { 25 desc: "success", 26 envVars: map[string]string{ 27 EnvAPIKey: "123", 28 }, 29 }, 30 { 31 desc: "missing api key", 32 envVars: map[string]string{ 33 EnvAPIKey: "", 34 }, 35 expected: "gandi: some credentials information are missing: GANDI_API_KEY", 36 }, 37 } 38 39 for _, test := range testCases { 40 t.Run(test.desc, func(t *testing.T) { 41 defer envTest.RestoreEnv() 42 envTest.ClearEnv() 43 44 envTest.Apply(test.envVars) 45 46 p, err := NewDNSProvider() 47 48 if len(test.expected) == 0 { 49 require.NoError(t, err) 50 require.NotNil(t, p) 51 require.NotNil(t, p.config) 52 require.NotNil(t, p.inProgressFQDNs) 53 require.NotNil(t, p.inProgressAuthZones) 54 } else { 55 require.EqualError(t, err, test.expected) 56 } 57 }) 58 } 59} 60 61func TestNewDNSProviderConfig(t *testing.T) { 62 testCases := []struct { 63 desc string 64 apiKey string 65 expected string 66 }{ 67 { 68 desc: "success", 69 apiKey: "123", 70 }, 71 { 72 desc: "missing credentials", 73 expected: "gandi: no API Key given", 74 }, 75 } 76 77 for _, test := range testCases { 78 t.Run(test.desc, func(t *testing.T) { 79 config := NewDefaultConfig() 80 config.APIKey = test.apiKey 81 82 p, err := NewDNSProviderConfig(config) 83 84 if len(test.expected) == 0 { 85 require.NoError(t, err) 86 require.NotNil(t, p) 87 require.NotNil(t, p.config) 88 require.NotNil(t, p.inProgressFQDNs) 89 require.NotNil(t, p.inProgressAuthZones) 90 } else { 91 require.EqualError(t, err, test.expected) 92 } 93 }) 94 } 95} 96 97// TestDNSProvider runs Present and CleanUp against a fake Gandi RPC 98// Server, whose responses are predetermined for particular requests. 99func TestDNSProvider(t *testing.T) { 100 // serverResponses is the XML-RPC Request->Response map used by the 101 // fake RPC server. It was generated by recording a real RPC session 102 // which resulted in the successful issue of a cert, and then 103 // anonymizing the RPC data. 104 var serverResponses = map[string]string{ 105 // Present Request->Response 1 (getZoneID) 106 presentGetZoneIDRequestMock: presentGetZoneIDResponseMock, 107 // Present Request->Response 2 (cloneZone) 108 presentCloneZoneRequestMock: presentCloneZoneResponseMock, 109 // Present Request->Response 3 (newZoneVersion) 110 presentNewZoneVersionRequestMock: presentNewZoneVersionResponseMock, 111 // Present Request->Response 4 (addTXTRecord) 112 presentAddTXTRecordRequestMock: presentAddTXTRecordResponseMock, 113 // Present Request->Response 5 (setZoneVersion) 114 presentSetZoneVersionRequestMock: presentSetZoneVersionResponseMock, 115 // Present Request->Response 6 (setZone) 116 presentSetZoneRequestMock: presentSetZoneResponseMock, 117 // CleanUp Request->Response 1 (setZone) 118 cleanupSetZoneRequestMock: cleanupSetZoneResponseMock, 119 // CleanUp Request->Response 2 (deleteZone) 120 cleanupDeleteZoneRequestMock: cleanupDeleteZoneResponseMock, 121 } 122 123 fakeKeyAuth := "XXXX" 124 125 regexpDate := regexp.MustCompile(`\[ACME Challenge [^\]:]*:[^\]]*\]`) 126 127 // start fake RPC server 128 fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 129 require.Equal(t, "text/xml", r.Header.Get("Content-Type"), "invalid content type") 130 131 req, errS := ioutil.ReadAll(r.Body) 132 require.NoError(t, errS) 133 134 req = regexpDate.ReplaceAllLiteral(req, []byte(`[ACME Challenge 01 Jan 16 00:00 +0000]`)) 135 resp, ok := serverResponses[string(req)] 136 require.True(t, ok, "Server response for request not found") 137 138 _, errS = io.Copy(w, strings.NewReader(resp)) 139 require.NoError(t, errS) 140 })) 141 defer fakeServer.Close() 142 143 // define function to override findZoneByFqdn with 144 fakeFindZoneByFqdn := func(fqdn string) (string, error) { 145 return "example.com.", nil 146 } 147 148 config := NewDefaultConfig() 149 config.BaseURL = fakeServer.URL + "/" 150 config.APIKey = "123412341234123412341234" 151 152 provider, err := NewDNSProviderConfig(config) 153 require.NoError(t, err) 154 155 // override findZoneByFqdn function 156 savedFindZoneByFqdn := provider.findZoneByFqdn 157 defer func() { 158 provider.findZoneByFqdn = savedFindZoneByFqdn 159 }() 160 provider.findZoneByFqdn = fakeFindZoneByFqdn 161 162 // run Present 163 err = provider.Present("abc.def.example.com", "", fakeKeyAuth) 164 require.NoError(t, err) 165 166 // run CleanUp 167 err = provider.CleanUp("abc.def.example.com", "", fakeKeyAuth) 168 require.NoError(t, err) 169} 170