ref: 08c0de5cc37cd4e512268b8f72ec5a6c68cd5754
parent: 1cf2f3dc4fa81503485a73db21bfda6e965dee15
author: Cameron Moore <moorereason@gmail.com>
date: Mon May 1 18:41:08 EDT 2017
tpl/data: Clean up data namespace - Move the main GetCSV and GetJSON into data.go. - Add error returns to GetCSV and GetJSON. - Add http client to Namespace for test mocking. - Send accept headers on remote requests. Fixes #3395 - Return an error on non-2XX HTTP response codes and don't retry. - Move cache tests to cache_test.go.
--- /dev/null
+++ b/tpl/data/cache_test.go
@@ -1,0 +1,63 @@
+// Copyright 2017 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package data
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/spf13/afero"
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCache(t *testing.T) {+ t.Parallel()
+
+ fs := new(afero.MemMapFs)
+
+ for i, test := range []struct {+ path string
+ content []byte
+ ignore bool
+ }{+ {"http://Foo.Bar/foo_Bar-Foo", []byte(`T€st Content 123`), false},+ {"fOO,bar:foo%bAR", []byte(`T€st Content 123 fOO,bar:foo%bAR`), false},+ {"FOo/BaR.html", []byte(`FOo/BaR.html T€st Content 123`), false},+ {"трям/трям", []byte(`T€st трям/трям Content 123`), false},+ {"은행", []byte(`T€st C은행ontent 123`), false},+ {"Банковский кассир", []byte(`Банковский кассир T€st Content 123`), false},+ {"Банковский кассир", []byte(`Банковский кассир T€st Content 456`), true},+ } {+ msg := fmt.Sprintf("Test #%d: %v", i, test)+
+ cfg := viper.New()
+
+ c, err := getCache(test.path, fs, cfg, test.ignore)
+ assert.NoError(t, err, msg)
+ assert.Nil(t, c, msg)
+
+ err = writeCache(test.path, test.content, fs, cfg, test.ignore)
+ assert.NoError(t, err, msg)
+
+ c, err = getCache(test.path, fs, cfg, test.ignore)
+ assert.NoError(t, err, msg)
+
+ if test.ignore {+ assert.Nil(t, c, msg)
+ } else {+ assert.Equal(t, string(test.content), string(c))
+ }
+ }
+}
--- a/tpl/data/data.go
+++ b/tpl/data/data.go
@@ -13,12 +13,24 @@
package data
-import "github.com/spf13/hugo/deps"
+import (
+ "bytes"
+ "encoding/csv"
+ "encoding/json"
+ "errors"
+ "net/http"
+ "strings"
+ "time"
+ "github.com/spf13/hugo/deps"
+ jww "github.com/spf13/jwalterweatherman"
+)
+
// New returns a new instance of the data-namespaced template functions.
func New(deps *deps.Deps) *Namespace { return &Namespace{- deps: deps,
+ deps: deps,
+ client: http.DefaultClient,
}
}
@@ -25,4 +37,102 @@
// Namespace provides template functions for the "data" namespace.
type Namespace struct {deps *deps.Deps
+
+ client *http.Client
+}
+
+// GetCSV expects a data separator and one or n-parts of a URL to a resource which
+// can either be a local or a remote one.
+// The data separator can be a comma, semi-colon, pipe, etc, but only one character.
+// If you provide multiple parts for the URL they will be joined together to the final URL.
+// GetCSV returns nil or a slice slice to use in a short code.
+func (ns *Namespace) GetCSV(sep string, urlParts ...string) (d [][]string, err error) {+ url := strings.Join(urlParts, "")
+
+ var clearCacheSleep = func(i int, u string) {+ jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)+ time.Sleep(resSleep)
+ deleteCache(url, ns.deps.Fs.Source, ns.deps.Cfg)
+ }
+
+ for i := 0; i <= resRetries; i++ {+ var req *http.Request
+ req, err = http.NewRequest("GET", url, nil)+ if err != nil {+ jww.ERROR.Printf("Failed to create request for getJSON: %s", err)+ return nil, err
+ }
+
+ req.Header.Add("Accept", "text/csv")+ req.Header.Add("Accept", "text/plain")+
+ var c []byte
+ c, err = ns.getResource(req)
+ if err != nil {+ jww.ERROR.Printf("Failed to read csv resource %q with error message %s", url, err)+ return nil, err
+ }
+
+ if !bytes.Contains(c, []byte(sep)) {+ err = errors.New("Cannot find separator " + sep + " in CSV.")+ return
+ }
+
+ if d, err = parseCSV(c, sep); err != nil {+ jww.ERROR.Printf("Failed to parse csv file %s with error message %s", url, err)+ clearCacheSleep(i, url)
+ continue
+ }
+ break
+ }
+ return
+}
+
+// GetJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
+// If you provide multiple parts they will be joined together to the final URL.
+// GetJSON returns nil or parsed JSON to use in a short code.
+func (ns *Namespace) GetJSON(urlParts ...string) (v interface{}, err error) {+ url := strings.Join(urlParts, "")
+
+ for i := 0; i <= resRetries; i++ {+ var req *http.Request
+ req, err = http.NewRequest("GET", url, nil)+ if err != nil {+ jww.ERROR.Printf("Failed to create request for getJSON: %s", err)+ return nil, err
+ }
+
+ req.Header.Add("Accept", "application/json")+
+ var c []byte
+ c, err = ns.getResource(req)
+ if err != nil {+ jww.ERROR.Printf("Failed to get json resource %s with error message %s", url, err)+ return nil, err
+ }
+
+ err = json.Unmarshal(c, &v)
+ if err != nil {+ jww.ERROR.Printf("Cannot read json from resource %s with error message %s", url, err)+ jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)+ time.Sleep(resSleep)
+ deleteCache(url, ns.deps.Fs.Source, ns.deps.Cfg)
+ continue
+ }
+ break
+ }
+ return
+}
+
+// parseCSV parses bytes of CSV data into a slice slice string or an error
+func parseCSV(c []byte, sep string) ([][]string, error) {+ if len(sep) != 1 {+ return nil, errors.New("Incorrect length of csv separator: " + sep)+ }
+ b := bytes.NewReader(c)
+ r := csv.NewReader(b)
+ rSep := []rune(sep)
+ r.Comma = rSep[0]
+ r.FieldsPerRecord = 0
+ return r.ReadAll()
}
--- /dev/null
+++ b/tpl/data/data_test.go
@@ -1,0 +1,251 @@
+// Copyright 2017 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package data
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetCSV(t *testing.T) {+ t.Parallel()
+
+ ns := New(newDeps(viper.New()))
+
+ for i, test := range []struct {+ sep string
+ url string
+ content string
+ expect interface{}+ }{+ // Remotes
+ {+ ",",
+ `http://success/`,
+ "gomeetup,city\nyes,Sydney\nyes,San Francisco\nyes,Stockholm\n",
+ [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}},+ },
+ {+ ",",
+ `http://error.extra.field/`,
+ "gomeetup,city\nyes,Sydney\nyes,San Francisco\nyes,Stockholm,EXTRA\n",
+ false,
+ },
+ {+ ",",
+ `http://error.no.sep/`,
+ "gomeetup;city\nyes;Sydney\nyes;San Francisco\nyes;Stockholm\n",
+ false,
+ },
+ {+ ",",
+ `http://nofound/404`,
+ ``,
+ false,
+ },
+
+ // Locals
+ {+ ";",
+ "pass/semi",
+ "gomeetup;city\nyes;Sydney\nyes;San Francisco\nyes;Stockholm\n",
+ [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}},+ },
+ {+ ";",
+ "fail/no-file",
+ "",
+ false,
+ },
+ } {+ msg := fmt.Sprintf("Test %d", i)+
+ // Setup HTTP test server
+ var srv *httptest.Server
+ srv, ns.client = getTestServer(func(w http.ResponseWriter, r *http.Request) {+ if !haveHeader(r.Header, "Accept", "text/csv") && !haveHeader(r.Header, "Accept", "text/plain") {+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
+ return
+ }
+
+ if r.URL.Path == "/404" {+ http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
+ return
+ }
+
+ w.Header().Add("Content-type", "text/csv")+
+ w.Write([]byte(test.content))
+ })
+ defer func() { srv.Close() }()+
+ // Setup local test file for schema-less URLs
+ if !strings.Contains(test.url, ":") && !strings.HasPrefix(test.url, "fail/") {+ f, err := ns.deps.Fs.Source.Create(filepath.Join(ns.deps.Cfg.GetString("workingDir"), test.url))+ require.NoError(t, err, msg)
+ f.WriteString(test.content)
+ f.Close()
+ }
+
+ // Get on with it
+ got, err := ns.GetCSV(test.sep, test.url)
+
+ if _, ok := test.expect.(bool); ok {+ assert.Error(t, err, msg)
+ continue
+ }
+ require.NoError(t, err, msg)
+ require.NotNil(t, got, msg)
+
+ assert.EqualValues(t, test.expect, got, msg)
+ }
+}
+
+func TestGetJSON(t *testing.T) {+ t.Parallel()
+
+ ns := New(newDeps(viper.New()))
+
+ for i, test := range []struct {+ url string
+ content string
+ expect interface{}+ }{+ {+ `http://success/`,
+ `{"gomeetup":["Sydney","San Francisco","Stockholm"]}`,+ map[string]interface{}{"gomeetup": []interface{}{"Sydney", "San Francisco", "Stockholm"}},+ },
+ {+ `http://malformed/`,
+ `{gomeetup:["Sydney","San Francisco","Stockholm"]}`,+ false,
+ },
+ {+ `http://nofound/404`,
+ ``,
+ false,
+ },
+ // Locals
+ {+ "pass/semi",
+ `{"gomeetup":["Sydney","San Francisco","Stockholm"]}`,+ map[string]interface{}{"gomeetup": []interface{}{"Sydney", "San Francisco", "Stockholm"}},+ },
+ {+ "fail/no-file",
+ "",
+ false,
+ },
+ } {+ msg := fmt.Sprintf("Test %d", i)+
+ // Setup HTTP test server
+ var srv *httptest.Server
+ srv, ns.client = getTestServer(func(w http.ResponseWriter, r *http.Request) {+ if !haveHeader(r.Header, "Accept", "application/json") {+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
+ return
+ }
+
+ if r.URL.Path == "/404" {+ http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
+ return
+ }
+
+ w.Header().Add("Content-type", "application/json")+
+ w.Write([]byte(test.content))
+ })
+ defer func() { srv.Close() }()+
+ // Setup local test file for schema-less URLs
+ if !strings.Contains(test.url, ":") && !strings.HasPrefix(test.url, "fail/") {+ f, err := ns.deps.Fs.Source.Create(filepath.Join(ns.deps.Cfg.GetString("workingDir"), test.url))+ require.NoError(t, err, msg)
+ f.WriteString(test.content)
+ f.Close()
+ }
+
+ // Get on with it
+ got, err := ns.GetJSON(test.url)
+
+ if _, ok := test.expect.(bool); ok {+ assert.Error(t, err, msg)
+ continue
+ }
+ require.NoError(t, err, msg)
+ require.NotNil(t, got, msg)
+
+ assert.EqualValues(t, test.expect, got, msg)
+ }
+}
+
+func TestParseCSV(t *testing.T) {+ t.Parallel()
+
+ for i, test := range []struct {+ csv []byte
+ sep string
+ exp string
+ err bool
+ }{+ {[]byte("a,b,c\nd,e,f\n"), "", "", true},+ {[]byte("a,b,c\nd,e,f\n"), "~/", "", true},+ {[]byte("a,b,c\nd,e,f"), "|", "a,b,cd,e,f", false},+ {[]byte("q,w,e\nd,e,f"), ",", "qwedef", false},+ {[]byte("a|b|c\nd|e|f|g"), "|", "abcdefg", true},+ {[]byte("z|y|c\nd|e|f"), "|", "zycdef", false},+ } {+ msg := fmt.Sprintf("Test %d: %v", i, test)+
+ csv, err := parseCSV(test.csv, test.sep)
+ if test.err {+ assert.Error(t, err, msg)
+ continue
+ }
+ require.NoError(t, err, msg)
+
+ act := ""
+ for _, v := range csv {+ act = act + strings.Join(v, "")
+ }
+
+ assert.Equal(t, test.exp, act, msg)
+ }
+}
+
+func haveHeader(m http.Header, key, needle string) bool {+ var s []string
+ var ok bool
+
+ if s, ok = m[key]; !ok {+ return false
+ }
+
+ for _, v := range s {+ if v == needle {+ return true
+ }
+ }
+ return false
+}
--- a/tpl/data/resources.go
+++ b/tpl/data/resources.go
@@ -14,14 +14,10 @@
package data
import (
- "bytes"
- "encoding/csv"
- "encoding/json"
- "errors"
+ "fmt"
"io/ioutil"
"net/http"
"path/filepath"
- "strings"
"sync"
"time"
@@ -67,14 +63,16 @@
}
// getRemote loads the content of a remote file. This method is thread safe.
-func getRemote(url string, fs afero.Fs, cfg config.Provider, hc *http.Client) ([]byte, error) {+func getRemote(req *http.Request, fs afero.Fs, cfg config.Provider, hc *http.Client) ([]byte, error) {+ url := req.URL.String()
+
c, err := getCache(url, fs, cfg, cfg.GetBool("ignoreCache"))- if c != nil && err == nil {- return c, nil
- }
if err != nil {return nil, err
}
+ if c != nil {+ return c, nil
+ }
// avoid race condition with locks, block other goroutines if the current url is processing
remoteURLLock.URLLock(url)
@@ -82,27 +80,34 @@
// avoid multiple locks due to calling getCache twice
c, err = getCache(url, fs, cfg, cfg.GetBool("ignoreCache"))- if c != nil && err == nil {- return c, nil
- }
if err != nil {return nil, err
}
+ if c != nil {+ return c, nil
+ }
jww.INFO.Printf("Downloading: %s ...", url)- res, err := hc.Get(url)
+ res, err := hc.Do(req)
if err != nil {return nil, err
}
+
+ if res.StatusCode < 200 || res.StatusCode > 299 {+ return nil, fmt.Errorf("Failed to retrieve remote file: %s", http.StatusText(res.StatusCode))+ }
+
c, err = ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {return nil, err
}
+
err = writeCache(url, c, fs, cfg, cfg.GetBool("ignoreCache")) if err != nil {return nil, err
}
+
jww.INFO.Printf("... and cached to: %s", getCacheFileID(cfg, url))return c, nil
}
@@ -119,90 +124,11 @@
}
// getResource loads the content of a local or remote file
-func (ns *Namespace) getResource(url string) ([]byte, error) {- if url == "" {- return nil, nil
+func (ns *Namespace) getResource(req *http.Request) ([]byte, error) {+ switch req.URL.Scheme {+ case "":
+ return getLocal(req.URL.String(), ns.deps.Fs.Source, ns.deps.Cfg)
+ default:
+ return getRemote(req, ns.deps.Fs.Source, ns.deps.Cfg, ns.client)
}
- if strings.Contains(url, "://") {- return getRemote(url, ns.deps.Fs.Source, ns.deps.Cfg, http.DefaultClient)
- }
- return getLocal(url, ns.deps.Fs.Source, ns.deps.Cfg)
-}
-
-// GetJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
-// If you provide multiple parts they will be joined together to the final URL.
-// GetJSON returns nil or parsed JSON to use in a short code.
-func (ns *Namespace) GetJSON(urlParts ...string) interface{} {- var v interface{}- url := strings.Join(urlParts, "")
-
- for i := 0; i <= resRetries; i++ {- c, err := ns.getResource(url)
- if err != nil {- jww.ERROR.Printf("Failed to get json resource %s with error message %s", url, err)- return nil
- }
-
- err = json.Unmarshal(c, &v)
- if err != nil {- jww.ERROR.Printf("Cannot read json from resource %s with error message %s", url, err)- jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)- time.Sleep(resSleep)
- deleteCache(url, ns.deps.Fs.Source, ns.deps.Cfg)
- continue
- }
- break
- }
- return v
-}
-
-// parseCSV parses bytes of CSV data into a slice slice string or an error
-func parseCSV(c []byte, sep string) ([][]string, error) {- if len(sep) != 1 {- return nil, errors.New("Incorrect length of csv separator: " + sep)- }
- b := bytes.NewReader(c)
- r := csv.NewReader(b)
- rSep := []rune(sep)
- r.Comma = rSep[0]
- r.FieldsPerRecord = 0
- return r.ReadAll()
-}
-
-// GetCSV expects a data separator and one or n-parts of a URL to a resource which
-// can either be a local or a remote one.
-// The data separator can be a comma, semi-colon, pipe, etc, but only one character.
-// If you provide multiple parts for the URL they will be joined together to the final URL.
-// GetCSV returns nil or a slice slice to use in a short code.
-func (ns *Namespace) GetCSV(sep string, urlParts ...string) [][]string {- var d [][]string
- url := strings.Join(urlParts, "")
-
- var clearCacheSleep = func(i int, u string) {- jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)- time.Sleep(resSleep)
- deleteCache(url, ns.deps.Fs.Source, ns.deps.Cfg)
- }
-
- for i := 0; i <= resRetries; i++ {- c, err := ns.getResource(url)
-
- if err == nil && !bytes.Contains(c, []byte(sep)) {- err = errors.New("Cannot find separator " + sep + " in CSV.")- }
-
- if err != nil {- jww.ERROR.Printf("Failed to read csv resource %s with error message %s", url, err)- clearCacheSleep(i, url)
- continue
- }
-
- if d, err = parseCSV(c, sep); err != nil {- jww.ERROR.Printf("Failed to parse csv file %s with error message %s", url, err)- clearCacheSleep(i, url)
- continue
- }
- break
- }
- return d
}
--- a/tpl/data/resources_test.go
+++ b/tpl/data/resources_test.go
@@ -19,7 +19,6 @@
"net/http"
"net/http/httptest"
"net/url"
- "strings"
"sync"
"testing"
"time"
@@ -31,58 +30,9 @@
"github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-func TestScpCache(t *testing.T) {- t.Parallel()
-
- tests := []struct {- path string
- content []byte
- ignore bool
- }{- {"http://Foo.Bar/foo_Bar-Foo", []byte(`T€st Content 123`), false},- {"fOO,bar:foo%bAR", []byte(`T€st Content 123 fOO,bar:foo%bAR`), false},- {"FOo/BaR.html", []byte(`FOo/BaR.html T€st Content 123`), false},- {"трям/трям", []byte(`T€st трям/трям Content 123`), false},- {"은행", []byte(`T€st C은행ontent 123`), false},- {"Банковский кассир", []byte(`Банковский кассир T€st Content 123`), false},- {"Банковский кассир", []byte(`Банковский кассир T€st Content 456`), true},- }
-
- fs := new(afero.MemMapFs)
-
- for _, test := range tests {- cfg := viper.New()
- c, err := getCache(test.path, fs, cfg, test.ignore)
- if err != nil {- t.Errorf("Error getting cache: %s", err)- }
- if c != nil {- t.Errorf("There is content where there should not be anything: %s", string(c))- }
-
- err = writeCache(test.path, test.content, fs, cfg, test.ignore)
- if err != nil {- t.Errorf("Error writing cache: %s", err)- }
-
- c, err = getCache(test.path, fs, cfg, test.ignore)
- if err != nil {- t.Errorf("Error getting cache after writing: %s", err)- }
- if test.ignore {- if c != nil {- t.Errorf("Cache ignored but content is not nil: %s", string(c))- }
- } else {- if !bytes.Equal(c, test.content) {- t.Errorf("\nExpected: %s\nActual: %s\n", string(test.content), string(c))- }
- }
- }
-}
-
func TestScpGetLocal(t *testing.T) {t.Parallel()
v := viper.New()
@@ -146,7 +96,11 @@
}
for _, test := range tests {+ msg := fmt.Sprintf("%v", test)+ req, err := http.NewRequest("GET", test.path, nil)+ require.NoError(t, err, msg)
+
srv, cl := getTestServer(func(w http.ResponseWriter, r *http.Request) {w.Write(test.content)
})
@@ -154,25 +108,18 @@
cfg := viper.New()
- c, err := getRemote(test.path, fs, cfg, cl)
- if err != nil {- t.Errorf("Error getting resource content: %s", err)- }
- if !bytes.Equal(c, test.content) {- t.Errorf("\nNet Expected: %s\nNet Actual: %s\n", string(test.content), string(c))- }
- cc, cErr := getCache(test.path, fs, cfg, test.ignore)
- if cErr != nil {- t.Error(cErr)
- }
+ c, err := getRemote(req, fs, cfg, cl)
+ require.NoError(t, err, msg)
+ assert.Equal(t, string(test.content), string(c))
+
+ c, err = getCache(req.URL.String(), fs, cfg, test.ignore)
+ require.NoError(t, err, msg)
+
if test.ignore {- if cc != nil {- t.Errorf("Cache ignored but content is not nil: %s", string(cc))- }
+ assert.Empty(t, c, msg)
} else {- if !bytes.Equal(cc, test.content) {- t.Errorf("\nCache Expected: %s\nCache Actual: %s\n", string(test.content), string(cc))- }
+ assert.Equal(t, string(test.content), string(c))
+
}
}
}
@@ -179,16 +126,20 @@
func TestScpGetRemoteParallel(t *testing.T) {t.Parallel()
- fs := new(afero.MemMapFs)
+
+ ns := New(newDeps(viper.New()))
+
content := []byte(`T€st Content 123`)
- url := "http://Foo.Bar/foo_Bar-Foo"
srv, cl := getTestServer(func(w http.ResponseWriter, r *http.Request) {w.Write(content)
})
defer func() { srv.Close() }()- for _, ignoreCache := range []bool{false, true} {+ url := "http://Foo.Bar/foo_Bar-Foo"
+ req, err := http.NewRequest("GET", url, nil)+ require.NoError(t, err)
+ for _, ignoreCache := range []bool{false, true} {cfg := viper.New()
cfg.Set("ignoreCache", ignoreCache)@@ -199,13 +150,9 @@
go func(gor int) {defer wg.Done()
for j := 0; j < 10; j++ {- c, err := getRemote(url, fs, cfg, cl)
- if err != nil {- t.Errorf("Error getting resource content: %s", err)- }
- if !bytes.Equal(c, content) {- t.Errorf("\nNet Expected: %s\nNet Actual: %s\n", string(content), string(c))- }
+ c, err := getRemote(req, ns.deps.Fs.Source, ns.deps.Cfg, cl)
+ assert.NoError(t, err)
+ assert.Equal(t, string(content), string(c))
time.Sleep(23 * time.Millisecond)
}
@@ -213,137 +160,6 @@
}
wg.Wait()
- }
-
- t.Log("Done!")-}
-
-func TestParseCSV(t *testing.T) {- t.Parallel()
-
- tests := []struct {- csv []byte
- sep string
- exp string
- err bool
- }{- {[]byte("a,b,c\nd,e,f\n"), "", "", true},- {[]byte("a,b,c\nd,e,f\n"), "~/", "", true},- {[]byte("a,b,c\nd,e,f"), "|", "a,b,cd,e,f", false},- {[]byte("q,w,e\nd,e,f"), ",", "qwedef", false},- {[]byte("a|b|c\nd|e|f|g"), "|", "abcdefg", true},- {[]byte("z|y|c\nd|e|f"), "|", "zycdef", false},- }
- for _, test := range tests {- csv, err := parseCSV(test.csv, test.sep)
- if test.err && err == nil {- t.Error("Expecting an error")- }
- if test.err {- continue
- }
- if !test.err && err != nil {- t.Error(err)
- }
-
- act := ""
- for _, v := range csv {- act = act + strings.Join(v, "")
- }
-
- if act != test.exp {- t.Errorf("\nExpected: %s\nActual: %s\n%#v\n", test.exp, act, csv)- }
-
- }
-}
-
-func TestGetJSONFailParse(t *testing.T) {- t.Parallel()
-
- ns := New(newDeps(viper.New()))
-
- reqCount := 0
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {- if reqCount > 0 {- w.Header().Add("Content-type", "application/json")- fmt.Fprintln(w, `{"gomeetup":["Sydney", "San Francisco", "Stockholm"]}`)- } else {- w.WriteHeader(http.StatusInternalServerError)
- fmt.Fprintln(w, `ERROR 500`)
- }
- reqCount++
- }))
- defer ts.Close()
- url := ts.URL + "/test.json"
-
- want := map[string]interface{}{"gomeetup": []interface{}{"Sydney", "San Francisco", "Stockholm"}}- have := ns.GetJSON(url)
- assert.NotNil(t, have)
- if have != nil {- assert.EqualValues(t, want, have)
- }
-}
-
-func TestGetCSVFailParseSep(t *testing.T) {- t.Parallel()
-
- ns := New(newDeps(viper.New()))
-
- reqCount := 0
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {- if reqCount > 0 {- w.Header().Add("Content-type", "application/json")- fmt.Fprintln(w, `gomeetup,city`)
- fmt.Fprintln(w, `yes,Sydney`)
- fmt.Fprintln(w, `yes,San Francisco`)
- fmt.Fprintln(w, `yes,Stockholm`)
- } else {- w.WriteHeader(http.StatusInternalServerError)
- fmt.Fprintln(w, `ERROR 500`)
- }
- reqCount++
- }))
- defer ts.Close()
- url := ts.URL + "/test.csv"
-
- want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}- have := ns.GetCSV(",", url)- assert.NotNil(t, have)
- if have != nil {- assert.EqualValues(t, want, have)
- }
-}
-
-func TestGetCSVFailParse(t *testing.T) {- t.Parallel()
-
- ns := New(newDeps(viper.New()))
-
- reqCount := 0
- ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {- w.Header().Add("Content-type", "application/json")- if reqCount > 0 {- fmt.Fprintln(w, `gomeetup,city`)
- fmt.Fprintln(w, `yes,Sydney`)
- fmt.Fprintln(w, `yes,San Francisco`)
- fmt.Fprintln(w, `yes,Stockholm`)
- } else {- fmt.Fprintln(w, `gomeetup,city`)
- fmt.Fprintln(w, `yes,Sydney,Bondi,`) // wrong number of fields in line
- fmt.Fprintln(w, `yes,San Francisco`)
- fmt.Fprintln(w, `yes,Stockholm`)
- }
- reqCount++
- }))
- defer ts.Close()
- url := ts.URL + "/test.csv"
-
- want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}- have := ns.GetCSV(",", url)- assert.NotNil(t, have)
- if have != nil {- assert.EqualValues(t, want, have)
}
}
--
⑨