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