aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKumar Damani <me@kumardamani.net>2022-06-08 02:01:19 +0000
committerKumar Damani <me@kumardamani.net>2022-06-08 02:01:19 +0000
commit3fe5f2789f0b2e7066184194a3ac3ced02353667 (patch)
tree91e97ab057d34364f1abb7e81f89e1f804ee8aad
parent4ec6622f7bb891fa68f8bf45f5b8d84dcc46dae7 (diff)
more unit tests.
-rw-r--r--nccli.go156
-rw-r--r--nccli_test.go814
2 files changed, 850 insertions, 120 deletions
diff --git a/nccli.go b/nccli.go
index fec69c3..73a8de8 100644
--- a/nccli.go
+++ b/nccli.go
@@ -1,4 +1,4 @@
-package main
+package nccli
import (
"bufio"
@@ -15,7 +15,6 @@ import (
var (
verbose = false
- apiUrl string
)
// Based on: https://www.namecheap.com/support/api/methods/domains-dns/set-hosts/
@@ -43,12 +42,12 @@ type GetResponseObj struct {
}
type Host struct {
- XMLName xml.Name `xml:"host"`
- Type string `xml:"Type,attr"`
- Name string `xml:"Name,attr"`
- Address string `xml:"Address,attr"`
- MXPref int `xml:"MXPref,attr"`
- TTL int `xml:"TTL,attr"`
+ //XMLName xml.Name `xml:"host"`
+ Type string `xml:"Type,attr"`
+ Name string `xml:"Name,attr"`
+ Address string `xml:"Address,attr"`
+ MXPref int `xml:"MXPref,attr"`
+ TTL int `xml:"TTL,attr"`
}
// Helper functions //
@@ -69,12 +68,12 @@ func getEnv(key string) (string, error) {
// Load ENV vars into the apiUrl var.
// Returns nil if config loaded successfully, error otherwise.
-func loadConfig() error {
+func loadConfig() (string, error) {
keys := [5]string{"API_USER", "API_KEY", "ACCESS_IP", "DOMAIN_MAIN", "DOMAIN_END"}
for _, key := range keys {
_, err := getEnv(key)
if err != nil {
- return err
+ return "", err
}
}
user, _ := getEnv("API_USER")
@@ -83,14 +82,14 @@ func loadConfig() error {
domainMain, _ := getEnv("DOMAIN_MAIN")
domainEnd, _ := getEnv("DOMAIN_END")
- apiUrl = "https://api.namecheap.com/xml.response?ApiUser=" + user +
+ apiUrl := "https://api.namecheap.com/xml.response?ApiUser=" + user +
"&ApiKey=" + apiKey +
"&UserName=" + user +
"&ClientIp=" + accessIP +
"&SLD=" + domainMain +
"&TLD=" + domainEnd
- return nil
+ return apiUrl, nil
}
// Print host recrods to stdout.
@@ -114,9 +113,9 @@ func logRecords(hosts []Host) {
// Args: endPoint - the api method to call.
// extraParams (optional) - more params to add to the apiUrl.
// Returns the response byte stream.
-func makeReq(endPoint string, extraParams ...string) []byte {
+func makeReq(baseUrl string, endPoint string, extraParams ...string) ([]byte, error) {
- methodUrl := apiUrl + "&Command=namecheap.domains.dns." + endPoint
+ methodUrl := baseUrl + "&Command=namecheap.domains.dns." + endPoint
for _, param := range extraParams {
methodUrl += param
}
@@ -135,18 +134,18 @@ func makeReq(endPoint string, extraParams ...string) []byte {
case "sethosts":
response, err = http.Post(methodUrl, "application/xml", nil)
default:
- log.Fatalln("Error: Invalid request type specified.")
+ return nil, fmt.Errorf("Error: Invalid request type specified.")
}
if err != nil {
- log.Fatalln(err)
+ return nil, err
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
- log.Fatalln(err)
+ return nil, err
}
- return responseData
+ return responseData, nil
}
// Converts user input strings into ints. Errors if input is wrong.
@@ -156,21 +155,19 @@ func makeReq(endPoint string, extraParams ...string) []byte {
func normalizeUserChoices(userChoicesStrs []string, maxVal int) ([]int, error) {
var chosenInts []int
for _, choice := range userChoicesStrs {
- text := strings.TrimSpace(choice)
+ text := strings.TrimSpace(choice)
if len(text) == 0 {
continue
}
val, err := strconv.Atoi(text)
if err != nil {
- //log.Fatalln("Error: Bad user input detected. Please only enter numbers.")
- return nil, fmt.Errorf("Error: Bad input detected: %s. Please only enter numbers.", text)
+ return nil, fmt.Errorf("Error: Bad input detected: %s. Please only enter numbers.", text)
}
// validate
if (val >= 0) && (val < maxVal) {
chosenInts = append(chosenInts, val)
} else {
- //log.Fatalln("Error: Bad user input detected. One of the numbers is not correct.")
- return nil, fmt.Errorf("Error: Bad user input detected. The number %d is out of range.", val)
+ return nil, fmt.Errorf("Error: Bad user input detected. The number %d is out of range.", val)
}
}
return chosenInts, nil
@@ -193,24 +190,30 @@ func updateHostRecordSet(hostRecords []Host, newHost Host) {
// Performs the API calls that updates Namecheap in any way,
// and handles the resulting response validation.
// Args: urlEncodedHosts - params consisting of DNS records.
-func updateNamecheapRecords(urlEncodedHosts string) {
+func updateNamecheapRecords(apiUrl string, urlEncodedHosts string) error {
// make the api request to namecheap
- responseData := makeReq("sethosts", urlEncodedHosts)
+ responseData, err := makeReq(apiUrl, "sethosts", urlEncodedHosts)
+ if err != nil {
+ return err
+ }
var responseObj SetResponseObj
xml.Unmarshal(responseData, &responseObj)
// validate
- if responseObj.Status != "OK" {
- log.Fatalln(string(responseData))
- log.Fatalln("Error: Api request failed. Exiting.")
+ switch responseObj.Status {
+ case "OK":
+ case "ERROR":
+ return fmt.Errorf("Error: Api request failed.")
+ default:
+ return fmt.Errorf("Error while parsing XML response.")
}
if !responseObj.DNSResultObj.IsSuccess {
- log.Fatalln(string(responseData))
- log.Fatalln("Error: Something went wrong updating DNS records for:", responseObj.DNSResultObj.Domain)
+ return fmt.Errorf("Error: Something went wrong updating DNS records for %s", responseObj.DNSResultObj.Domain)
}
if verbose {
fmt.Println("Success updating DNS records for:", responseObj.DNSResultObj.Domain)
}
+ return nil
}
// Build a string with all DNS records.
@@ -270,17 +273,23 @@ func userInputHandler() []string {
// Get all existing DNS records.
// Returns a slice of Host corresponding to DNS records.
-func getAllRecords() []Host {
- responseData := makeReq("gethosts")
+func getAllRecords(apiUrl string) ([]Host, error) {
+ responseData, err := makeReq(apiUrl, "gethosts")
+ if err != nil {
+ return nil, err
+ }
var responseObj GetResponseObj
xml.Unmarshal(responseData, &responseObj)
// validate
- if responseObj.Status != "OK" {
- log.Fatalln(string(responseData))
- log.Fatalln("Error: Api request failed. Exiting.")
+ switch responseObj.Status {
+ case "OK":
+ return responseObj.Hosts, nil
+ case "ERROR":
+ return nil, fmt.Errorf("Error: Api request failed. Exiting.")
+ default:
+ return nil, fmt.Errorf("Error while parsing XML response.")
}
- return responseObj.Hosts
}
// Add a DNS record.
@@ -289,9 +298,12 @@ func getAllRecords() []Host {
// rAddress - the value of the DNS record.
// rMXPref - the priority of the DNS record.
// rTTL - the ttl of the DNS record.
-func addRecord(rName string, rType string, rAddress string, rMXPref int, rTTL int) {
+func addRecord(apiUrl string, rName string, rType string, rAddress string, rMXPref int, rTTL int) error {
// we need to get the records before we can add to it.
- var hostRecords = getAllRecords()
+ hostRecords, err := getAllRecords(apiUrl)
+ if err != nil {
+ return err
+ }
if verbose {
logRecords(hostRecords)
}
@@ -303,23 +315,29 @@ func addRecord(rName string, rType string, rAddress string, rMXPref int, rTTL in
TTL: rTTL,
})
urlEncodedHosts := urlEncodeHosts(hostRecords)
- updateNamecheapRecords(urlEncodedHosts)
+ err = updateNamecheapRecords(apiUrl, urlEncodedHosts)
+ if err != nil {
+ return err
+ }
+ return nil
}
// Remove DNS record(s).
// Will prompt for user input.
-func removeRecord() {
+func removeRecord(apiUrl string) error {
// we need to get the records before we can remove one
- var hostRecords = getAllRecords()
+ hostRecords, err := getAllRecords(apiUrl)
+ if err != nil {
+ return err
+ }
logRecords(hostRecords)
// get user input for which ones to delete
userChoicesStrs := userInputHandler()
userChoicesInts, err := normalizeUserChoices(userChoicesStrs, len(hostRecords))
- if err != nil {
- log.Fatalln(err)
- }
-
+ if err != nil {
+ return err
+ }
// build the desired set of host records.
var newHostRecords []Host
@@ -336,7 +354,11 @@ func removeRecord() {
}
}
urlEncodedHosts := urlEncodeHosts(newHostRecords)
- updateNamecheapRecords(urlEncodedHosts)
+ err = updateNamecheapRecords(apiUrl, urlEncodedHosts)
+ if err != nil {
+ return err
+ }
+ return nil
}
// Update DNS record.
@@ -345,9 +367,12 @@ func removeRecord() {
// rAddress - the value of the DNS record.
// rMXPref - the priority of the DNS record.
// rTTL - the ttl of the DNS record.
-func updateRecord(rName string, rType string, rAddress string, rMXPref int, rTTL int) {
+func updateRecord(apiUrl string, rName string, rType string, rAddress string, rMXPref int, rTTL int) error {
// we need to get the records before we can update it.
- var hostRecords = getAllRecords()
+ hostRecords, err := getAllRecords(apiUrl)
+ if err != nil {
+ return err
+ }
if verbose {
logRecords(hostRecords)
}
@@ -362,7 +387,11 @@ func updateRecord(rName string, rType string, rAddress string, rMXPref int, rTTL
},
)
urlEncodedHosts := urlEncodeHosts(hostRecords)
- updateNamecheapRecords(urlEncodedHosts)
+ err = updateNamecheapRecords(apiUrl, urlEncodedHosts)
+ if err != nil {
+ return err
+ }
+ return nil
}
func main() {
@@ -398,38 +427,51 @@ func main() {
switch os.Args[1] {
case "list":
listCmd.Parse(os.Args[2:])
- err := loadConfig()
+ apiUrl, err := loadConfig()
if err != nil {
log.Fatalln(err)
}
- logRecords(getAllRecords())
+ hostRecords, err := getAllRecords(apiUrl)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ logRecords(hostRecords)
case "add":
addCmd.Parse(os.Args[2:])
if *addName == "" || *addType == "" || *addAddress == "" {
log.Fatalln(addUpdateErrorMsg)
}
- err := loadConfig()
+ apiUrl, err := loadConfig()
+ if err != nil {
+ log.Fatalln(err)
+ }
+ err = addRecord(apiUrl, *addName, *addType, *addAddress, *addMXPref, *addTTL)
if err != nil {
log.Fatalln(err)
}
- addRecord(*addName, *addType, *addAddress, *addMXPref, *addTTL)
case "remove":
removeCmd.Parse(os.Args[2:])
- err := loadConfig()
+ apiUrl, err := loadConfig()
+ if err != nil {
+ log.Fatalln(err)
+ }
+ err = removeRecord(apiUrl)
if err != nil {
log.Fatalln(err)
}
- removeRecord()
case "update":
updateCmd.Parse(os.Args[2:])
if *updateName == "" || *updateType == "" || *updateAddress == "" {
log.Fatalln(addUpdateErrorMsg)
}
- err := loadConfig()
+ apiUrl, err := loadConfig()
+ if err != nil {
+ log.Fatalln(err)
+ }
+ err = updateRecord(apiUrl, *updateName, *updateType, *updateAddress, *updateMXPref, *updateTTL)
if err != nil {
log.Fatalln(err)
}
- updateRecord(*updateName, *updateType, *updateAddress, *updateMXPref, *updateTTL)
default:
log.Fatalln(subCmdErrorMsg)
}
diff --git a/nccli_test.go b/nccli_test.go
index 43f0308..11c4910 100644
--- a/nccli_test.go
+++ b/nccli_test.go
@@ -1,10 +1,12 @@
-package main
+package nccli
import (
"fmt"
+ "net/http"
+ "net/http/httptest"
"os"
- "testing"
"reflect"
+ "testing"
)
// checking for a valid return value.
@@ -36,7 +38,7 @@ func TestLoadConfigMissingKey(t *testing.T) {
os.Setenv("DOMAIN_MAIN", "mysite")
// NOTE: DOMAIN_END is missing.
- err := loadConfig()
+ _, err := loadConfig()
_, errWant := getEnv("DOMAIN_END")
if err.Error() != errWant.Error() || err == nil {
@@ -53,7 +55,7 @@ func TestLoadConfigAllKeys(t *testing.T) {
os.Setenv("DOMAIN_MAIN", "mysite")
os.Setenv("DOMAIN_END", "xyz")
- loadConfig()
+ apiUrl, _ := loadConfig()
want := "https://api.namecheap.com/xml.response?ApiUser=user&ApiKey=mykey&UserName=user&ClientIp=1.1.1.1&SLD=mysite&TLD=xyz"
if apiUrl != want {
t.Fatalf(`apiUrl = %s, want %s`, apiUrl, want)
@@ -62,124 +64,810 @@ func TestLoadConfigAllKeys(t *testing.T) {
// checking for a valid return value.
func TestNormalizeUserChoicesCleanInput(t *testing.T) {
- userChoicesStrs := []string{"0", "1", "6"}
- maxVal := 7
- want := []int{0, 1, 6}
+ userChoicesStrs := []string{"0", "1", "6"}
+ maxVal := 7
+ want := []int{0, 1, 6}
have, err := normalizeUserChoices(userChoicesStrs, maxVal)
- if !reflect.DeepEqual(have, want) || err != nil {
+ if !reflect.DeepEqual(have, want) || err != nil {
t.Fatalf(`normalizeUserChoices(%v, %d) = %v, %v, want %v,nil`, userChoicesStrs, maxVal, have, err, want)
- }
+ }
}
// checking for a valid return value.
func TestNormalizeUserChoicesExtraSpaces(t *testing.T) {
- userChoicesStrs := []string{" ", " ", "1", "", " 5 ", " "}
- maxVal := 7
- want := []int{1, 5}
+ userChoicesStrs := []string{" ", " ", "1", "", " 5 ", " "}
+ maxVal := 7
+ want := []int{1, 5}
have, err := normalizeUserChoices(userChoicesStrs, maxVal)
- if !reflect.DeepEqual(have, want) || err != nil {
+ if !reflect.DeepEqual(have, want) || err != nil {
t.Fatalf(`normalizeUserChoices(%v, %d) = %v, %v, want %v,nil`, userChoicesStrs, maxVal, have, err, want)
- }
+ }
}
// checking for an error.
func TestNormalizeUserChoicesWrongChar(t *testing.T) {
- userChoicesStrs := []string{"6", "5b"}
- maxVal := 7
- errWant := "Error: Bad input detected: 5b. Please only enter numbers."
+ userChoicesStrs := []string{"6", "5b"}
+ maxVal := 7
+ errWant := "Error: Bad input detected: 5b. Please only enter numbers."
have, err := normalizeUserChoices(userChoicesStrs, maxVal)
- if have != nil || err.Error() != errWant {
+ if have != nil || err.Error() != errWant {
t.Fatalf(`normalizeUserChoices(%v, %d) = %v, %v, want nil,%v`, userChoicesStrs, maxVal, have, err, errWant)
- }
+ }
}
// checking for an error.
func TestNormalizeUserChoicesOutOfRangeLower(t *testing.T) {
- userChoicesStrs := []string{"-1", "5"}
- maxVal := 7
- errWant := "Error: Bad user input detected. The number -1 is out of range."
+ userChoicesStrs := []string{"-1", "5"}
+ maxVal := 7
+ errWant := "Error: Bad user input detected. The number -1 is out of range."
have, err := normalizeUserChoices(userChoicesStrs, maxVal)
- if have != nil || err.Error() != errWant {
+ if have != nil || err.Error() != errWant {
t.Fatalf(`normalizeUserChoices(%v, %d) = %v, %v, want nil,%v`, userChoicesStrs, maxVal, have, err, errWant)
- }
+ }
}
// checking for an error.
func TestNormalizeUserChoicesOutOfRangeUpper(t *testing.T) {
- userChoicesStrs := []string{"0", "7"}
- maxVal := 7
- errWant := "Error: Bad user input detected. The number 7 is out of range."
+ userChoicesStrs := []string{"0", "7"}
+ maxVal := 7
+ errWant := "Error: Bad user input detected. The number 7 is out of range."
have, err := normalizeUserChoices(userChoicesStrs, maxVal)
- if have != nil || err.Error() != errWant {
+ if have != nil || err.Error() != errWant {
t.Fatalf(`normalizeUserChoices(%v, %d) = %v, %v, want nil,%v`, userChoicesStrs, maxVal, have, err, errWant)
- }
+ }
}
// checking for a valid return value.
func TestUpdateHostRecordSet(t *testing.T) {
- rec1 := Host{
+ rec1 := Host{
Name: "@",
Type: "T",
Address: "foo",
MXPref: 1,
TTL: 1,
- }
- rec2 := Host{
+ }
+ rec2 := Host{
Name: "www",
Type: "T",
Address: "bar",
MXPref: 1,
TTL: 1,
- }
- updatedRec2 := Host{
- Name: "www",
- Type: "T",
- Address: "new",
- MXPref: 10,
- TTL: 99,
- }
- recs := []Host{rec1, rec2}
- want := []Host{rec1, updatedRec2}
- // update recs in place
+ }
+ updatedRec2 := Host{
+ Name: "www",
+ Type: "T",
+ Address: "new",
+ MXPref: 10,
+ TTL: 99,
+ }
+ recs := []Host{rec1, rec2}
+ want := []Host{rec1, updatedRec2}
+ // update recs in place
updateHostRecordSet(recs, updatedRec2)
- if !reflect.DeepEqual(recs, want) {
- t.Fatalf(`have %v, want %v`, recs, want)
- }
+ if !reflect.DeepEqual(recs, want) {
+ t.Fatalf(`have %v, want %v`, recs, want)
+ }
}
// checking for a valid return value.
func TestUpdateHostRecordSetNoOp(t *testing.T) {
- rec1 := Host{
+ rec1 := Host{
Name: "@",
Type: "T",
Address: "foo",
MXPref: 1,
TTL: 1,
- }
- rec2 := Host{
+ }
+ rec2 := Host{
Name: "www",
Type: "T",
Address: "bar",
MXPref: 1,
TTL: 1,
- }
- updatedRec2 := Host{
- Name: "www",
- Type: "A",
- Address: "new",
- MXPref: 10,
- TTL: 99,
- }
- recs := []Host{rec1, rec2}
- want := []Host{rec1, rec2}
- // update recs in place
+ }
+ updatedRec2 := Host{
+ Name: "www",
+ Type: "A",
+ Address: "new",
+ MXPref: 10,
+ TTL: 99,
+ }
+ recs := []Host{rec1, rec2}
+ want := []Host{rec1, rec2}
+ // update recs in place
updateHostRecordSet(recs, updatedRec2)
- if !reflect.DeepEqual(recs, want) {
- t.Fatalf(`have %v, want %v`, recs, want)
- }
+ if !reflect.DeepEqual(recs, want) {
+ t.Fatalf(`have %v, want %v`, recs, want)
+ }
+}
+
+func TestUrlEncodeHosts(t *testing.T) {
+ rec1 := Host{
+ Name: "@",
+ Type: "T",
+ Address: "foo",
+ MXPref: 1,
+ TTL: 1,
+ }
+ rec2 := Host{
+ Name: "www",
+ Type: "T",
+ Address: "bar",
+ MXPref: 1,
+ TTL: 42,
+ }
+ rec3 := Host{
+ Name: "www",
+ Type: "A",
+ Address: "biz",
+ MXPref: 10,
+ TTL: 19,
+ }
+ recs := []Host{rec1, rec2, rec3}
+ have := urlEncodeHosts(recs)
+ want := fmt.Sprintf("&HostName1=%s&RecordType1=%s&Address1=%s&MXPref1=%d&TTL1=%d",
+ rec1.Name,
+ rec1.Type,
+ rec1.Address,
+ rec1.MXPref,
+ rec1.TTL,
+ )
+ want += fmt.Sprintf("&HostName2=%s&RecordType2=%s&Address2=%s&MXPref2=%d&TTL2=%d",
+ rec2.Name,
+ rec2.Type,
+ rec2.Address,
+ rec2.MXPref,
+ rec2.TTL,
+ )
+ want += fmt.Sprintf("&HostName3=%s&RecordType3=%s&Address3=%s&MXPref3=%d&TTL3=%d",
+ rec3.Name,
+ rec3.Type,
+ rec3.Address,
+ rec3.MXPref,
+ rec3.TTL,
+ )
+ if have != want {
+ t.Fatalf(`have %s, want %s`, have, want)
+ }
+}
+
+func TestMakeReqGetHosts(t *testing.T) {
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ wantUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "gethosts"
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.RequestURI != wantUri {
+ t.Fatalf("have %s, want %s", r.RequestURI, wantUri)
+ }
+ }))
+ defer server.Close()
+ makeReq(server.URL+mockBaseUrl, "gethosts")
+}
+
+func TestMakeReqSetHosts(t *testing.T) {
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ wantUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "sethosts"
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.RequestURI != wantUri {
+ t.Fatalf("have %s, want %s", r.RequestURI, wantUri)
+ }
+ }))
+ defer server.Close()
+ makeReq(server.URL+mockBaseUrl, "sethosts")
+}
+
+func TestMakeReqWrongEndpoint(t *testing.T) {
+ _, have := makeReq("someurl", "foobar")
+ want := "Error: Invalid request type specified."
+
+ if have.Error() != want {
+ t.Fatalf("have %s, want: %s", have, want)
+ }
+}
+
+func TestGetAllRecordsValidXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ xmlRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ <host HostId="12" Name="@" Type="A" Address="1.2.3.4" MXPref="10" TTL="1800" />
+ <host HostId="14" Name="www" Type="A" Address="122.23.3.7" MXPref="10" TTL="1800" />
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+ want := []Host{Host{
+ Name: "@",
+ Type: "A",
+ Address: "1.2.3.4",
+ MXPref: 10,
+ TTL: 1800,
+ }, Host{
+ Name: "www",
+ Type: "A",
+ Address: "122.23.3.7",
+ MXPref: 10,
+ TTL: 1800,
+ }}
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(xmlRes))
+ }))
+ defer server.Close()
+ have, err := getAllRecords(server.URL + mockBaseUrl)
+ if !reflect.DeepEqual(have, want) || err != nil {
+ t.Fatalf("have %v, %v, want %v, nil", have, err, want)
+ }
+}
+
+func TestGetAllRecordsValidXMLResponseNoHosts(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ xmlRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(xmlRes))
+ }))
+ defer server.Close()
+ have, err := getAllRecords(server.URL + mockBaseUrl)
+ if have != nil || err != nil {
+ t.Fatalf("have %v, %v, want nil, nil", have, err)
+ }
+}
+
+func TestGetAllRecordsErrorXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ xmlRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse Status="ERROR">
+ <Errors>
+ <Error Number="0">Error message</Error>
+ </Errors>
+ </ApiResponse>
+ `
+ want := "Error: Api request failed. Exiting."
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(xmlRes))
+ }))
+ defer server.Close()
+ have, err := getAllRecords(server.URL + mockBaseUrl)
+ if have != nil || err.Error() != want {
+ t.Fatalf("have %s, want %s", err, want)
+ }
+}
+
+func TestGetAllRecordsBadXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ xmlRes := "<>"
+ want := "Error while parsing XML response."
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(xmlRes))
+ }))
+ defer server.Close()
+ have, err := getAllRecords(server.URL + mockBaseUrl)
+ if have != nil || err.Error() != want {
+ t.Fatalf("have %s, want %s", err, want)
+ }
+}
+
+func TestAddRecordValidXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ getUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "gethosts"
+ setUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "sethosts"
+
+ xmlGetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ <host HostId="12" Name="@" Type="A" Address="1.2.3.4" MXPref="10" TTL="1800" />
+ <host HostId="14" Name="www" Type="A" Address="122.23.3.7" MXPref="10" TTL="1800" />
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+
+ xmlSetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="https://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.setHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.setHosts">
+ <DomainDNSSetHostsResult Domain="domain51.com" IsSuccess="true" />
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+ newHost := Host{
+ Name: "all",
+ Type: "TXT",
+ Address: "foobar",
+ MXPref: 42,
+ TTL: 4242,
+ }
+ newHosts := []Host{Host{
+ Name: "@",
+ Type: "A",
+ Address: "1.2.3.4",
+ MXPref: 10,
+ TTL: 1800,
+ }, Host{
+ Name: "www",
+ Type: "A",
+ Address: "122.23.3.7",
+ MXPref: 10,
+ TTL: 1800,
+ }, newHost}
+ setUri += urlEncodeHosts(newHosts)
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.RequestURI {
+ case getUri:
+ w.Write([]byte(xmlGetRes))
+ case setUri:
+ w.Write([]byte(xmlSetRes))
+ }
+ }))
+ defer server.Close()
+ err := addRecord(server.URL+mockBaseUrl, newHost.Name, newHost.Type, newHost.Address, newHost.MXPref, newHost.TTL)
+ if err != nil {
+ t.Fatalf("have %v, want nil", err)
+ }
+}
+
+func TestAddRecordInValidXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ getUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "gethosts"
+ setUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "sethosts"
+
+ xmlGetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ <host HostId="12" Name="@" Type="A" Address="1.2.3.4" MXPref="10" TTL="1800" />
+ <host HostId="14" Name="www" Type="A" Address="122.23.3.7" MXPref="10" TTL="1800" />
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+
+ xmlSetRes := `<>`
+ newHost := Host{
+ Name: "all",
+ Type: "TXT",
+ Address: "foobar",
+ MXPref: 42,
+ TTL: 4242,
+ }
+ newHosts := []Host{Host{
+ Name: "@",
+ Type: "A",
+ Address: "1.2.3.4",
+ MXPref: 10,
+ TTL: 1800,
+ }, Host{
+ Name: "www",
+ Type: "A",
+ Address: "122.23.3.7",
+ MXPref: 10,
+ TTL: 1800,
+ }, newHost}
+ setUri += urlEncodeHosts(newHosts)
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.RequestURI {
+ case getUri:
+ w.Write([]byte(xmlGetRes))
+ case setUri:
+ w.Write([]byte(xmlSetRes))
+ }
+ }))
+ defer server.Close()
+ errHave := addRecord(server.URL+mockBaseUrl, newHost.Name, newHost.Type, newHost.Address, newHost.MXPref, newHost.TTL)
+ errWant := "Error while parsing XML response."
+ if errHave.Error() != errWant {
+ t.Fatalf("have %v, want %s", errHave, errWant)
+ }
+}
+
+func TestAddRecordErrorXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ getUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "gethosts"
+ setUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "sethosts"
+
+ xmlGetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ <host HostId="12" Name="@" Type="A" Address="1.2.3.4" MXPref="10" TTL="1800" />
+ <host HostId="14" Name="www" Type="A" Address="122.23.3.7" MXPref="10" TTL="1800" />
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+ xmlSetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse Status="ERROR">
+ <Errors>
+ <Error Number="0">Error message</Error>
+ </Errors>
+ </ApiResponse>
+ `
+ newHost := Host{
+ Name: "all",
+ Type: "TXT",
+ Address: "foobar",
+ MXPref: 42,
+ TTL: 4242,
+ }
+ newHosts := []Host{Host{
+ Name: "@",
+ Type: "A",
+ Address: "1.2.3.4",
+ MXPref: 10,
+ TTL: 1800,
+ }, Host{
+ Name: "www",
+ Type: "A",
+ Address: "122.23.3.7",
+ MXPref: 10,
+ TTL: 1800,
+ }, newHost}
+ setUri += urlEncodeHosts(newHosts)
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.RequestURI {
+ case getUri:
+ w.Write([]byte(xmlGetRes))
+ case setUri:
+ w.Write([]byte(xmlSetRes))
+ }
+ }))
+ defer server.Close()
+ errHave := addRecord(server.URL+mockBaseUrl, newHost.Name, newHost.Type, newHost.Address, newHost.MXPref, newHost.TTL)
+ errWant := "Error: Api request failed."
+ if errHave.Error() != errWant {
+ t.Fatalf("have %v, want %s", errHave, errWant)
+ }
+}
+
+func TestUpdateRecordValidXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ getUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "gethosts"
+ setUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "sethosts"
+
+ xmlGetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ <host HostId="12" Name="@" Type="A" Address="1.2.3.4" MXPref="10" TTL="1800" />
+ <host HostId="14" Name="www" Type="A" Address="122.23.3.7" MXPref="10" TTL="1800" />
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+ xmlSetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="https://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.setHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.setHosts">
+ <DomainDNSSetHostsResult Domain="domain51.com" IsSuccess="true" />
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+ newHost := Host{
+ Name: "www",
+ Type: "A",
+ Address: "foobar",
+ MXPref: 42,
+ TTL: 4242,
+ }
+ newHosts := []Host{Host{
+ Name: "@",
+ Type: "A",
+ Address: "1.2.3.4",
+ MXPref: 10,
+ TTL: 1800,
+ }, Host{
+ Name: "www",
+ Type: "A",
+ Address: "122.23.3.7",
+ MXPref: 10,
+ TTL: 1800,
+ }}
+ updateHostRecordSet(newHosts, newHost)
+ setUri += urlEncodeHosts(newHosts)
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.RequestURI {
+ case getUri:
+ w.Write([]byte(xmlGetRes))
+ case setUri:
+ w.Write([]byte(xmlSetRes))
+ }
+ }))
+ defer server.Close()
+ err := updateRecord(server.URL+mockBaseUrl, newHost.Name, newHost.Type, newHost.Address, newHost.MXPref, newHost.TTL)
+ if err != nil {
+ t.Fatalf("have %v, want nil", err)
+ }
+}
+
+func TestUpdateRecordInValidXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ getUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "gethosts"
+ setUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "sethosts"
+
+ xmlGetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ <host HostId="12" Name="@" Type="A" Address="1.2.3.4" MXPref="10" TTL="1800" />
+ <host HostId="14" Name="www" Type="A" Address="122.23.3.7" MXPref="10" TTL="1800" />
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+
+ xmlSetRes := `<>`
+ newHost := Host{
+ Name: "www",
+ Type: "A",
+ Address: "foobar",
+ MXPref: 42,
+ TTL: 4242,
+ }
+ newHosts := []Host{Host{
+ Name: "@",
+ Type: "A",
+ Address: "1.2.3.4",
+ MXPref: 10,
+ TTL: 1800,
+ }, Host{
+ Name: "www",
+ Type: "A",
+ Address: "122.23.3.7",
+ MXPref: 10,
+ TTL: 1800,
+ }}
+ updateHostRecordSet(newHosts, newHost)
+ setUri += urlEncodeHosts(newHosts)
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.RequestURI {
+ case getUri:
+ w.Write([]byte(xmlGetRes))
+ case setUri:
+ w.Write([]byte(xmlSetRes))
+ }
+ }))
+ defer server.Close()
+ errHave := updateRecord(server.URL+mockBaseUrl, newHost.Name, newHost.Type, newHost.Address, newHost.MXPref, newHost.TTL)
+ errWant := "Error while parsing XML response."
+ if errHave.Error() != errWant {
+ t.Fatalf("have %v, want %s", errHave, errWant)
+ }
+}
+
+func TestUpdateRecordErrorXMLResponse(t *testing.T) {
+
+ mockBaseUrl := "/xml.response?ApiUser=" + "myuser" +
+ "&ApiKey=" + "mykey" +
+ "&UserName=" + "myuser" +
+ "&ClientIp=" + "1.1.1.1" +
+ "&SLD=" + "mysite" +
+ "&TLD=" + "xyz"
+
+ getUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "gethosts"
+ setUri := mockBaseUrl + "&Command=namecheap.domains.dns." + "sethosts"
+
+ xmlGetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse xmlns="http://api.namecheap.com/xml.response" Status="OK">
+ <Errors />
+ <RequestedCommand>namecheap.domains.dns.getHosts</RequestedCommand>
+ <CommandResponse Type="namecheap.domains.dns.getHosts">
+ <DomainDNSGetHostsResult Domain="domain.com" IsUsingOurDNS="true">
+ <host HostId="12" Name="@" Type="A" Address="1.2.3.4" MXPref="10" TTL="1800" />
+ <host HostId="14" Name="www" Type="A" Address="122.23.3.7" MXPref="10" TTL="1800" />
+ </DomainDNSGetHostsResult>
+ </CommandResponse>
+ <Server>SERVER-NAME</Server>
+ <GMTTimeDifference>+5</GMTTimeDifference>
+ <ExecutionTime>32.76</ExecutionTime>
+ </ApiResponse>
+ `
+ xmlSetRes :=
+ `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <ApiResponse Status="ERROR">
+ <Errors>
+ <Error Number="0">Error message</Error>
+ </Errors>
+ </ApiResponse>
+ `
+ newHost := Host{
+ Name: "www",
+ Type: "A",
+ Address: "foobar",
+ MXPref: 42,
+ TTL: 4242,
+ }
+ newHosts := []Host{Host{
+ Name: "@",
+ Type: "A",
+ Address: "1.2.3.4",
+ MXPref: 10,
+ TTL: 1800,
+ }, Host{
+ Name: "www",
+ Type: "A",
+ Address: "122.23.3.7",
+ MXPref: 10,
+ TTL: 1800,
+ }}
+ updateHostRecordSet(newHosts, newHost)
+ setUri += urlEncodeHosts(newHosts)
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.RequestURI {
+ case getUri:
+ w.Write([]byte(xmlGetRes))
+ case setUri:
+ w.Write([]byte(xmlSetRes))
+ }
+ }))
+ defer server.Close()
+ errHave := updateRecord(server.URL+mockBaseUrl, newHost.Name, newHost.Type, newHost.Address, newHost.MXPref, newHost.TTL)
+ errWant := "Error: Api request failed."
+ if errHave.Error() != errWant {
+ t.Fatalf("have %v, want %s", errHave, errWant)
+ }
}