diff --git a/app/webmention/handler.go b/app/webmention/handler.go index ec30a81..5a2f158 100644 --- a/app/webmention/handler.go +++ b/app/webmention/handler.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/wgroeneveld/go-jamming/common" + "github.com/wgroeneveld/go-jamming/rest" ) func HandleGet(conf *common.Config) http.HandlerFunc { @@ -21,26 +22,31 @@ func HandlePut(conf *common.Config) http.HandlerFunc { } func HandlePost(conf *common.Config) http.HandlerFunc { + httpClient := &rest.HttpClient{} + return func(w http.ResponseWriter, r *http.Request) { r.ParseForm() if !validate(r, r.Header, conf) { - common.BadRequest(w) + rest.BadRequest(w) return } target := r.FormValue("target") - if !isValidTargetUrl(target) { - common.BadRequest(w) + if !isValidTargetUrl(target, httpClient) { + rest.BadRequest(w) return } - wm := &webmention{ + wm := webmention{ source: r.FormValue("source"), target: target, - } + } + recv := &receiver{ + restClient: httpClient, + } - go wm.receive() - common.Accept(w) + go recv.receive(wm) + rest.Accept(w) } } diff --git a/app/webmention/receive.go b/app/webmention/receive.go index c4e076a..5d21452 100644 --- a/app/webmention/receive.go +++ b/app/webmention/receive.go @@ -4,7 +4,7 @@ package webmention import ( "fmt" - "github.com/wgroeneveld/go-jamming/common" + "github.com/wgroeneveld/go-jamming/rest" "github.com/rs/zerolog/log" ) @@ -18,23 +18,29 @@ func (wm *webmention) String() string { return fmt.Sprintf("source: %s, target: %s", wm.source, wm.target) } -func (wm *webmention) receive() { +// used as a "class" to iject dependencies, just to be able to test. Do NOT like htis. +// Is there a better way? e.g. in validate, I just pass rest.Client as an arg. Not great either. +type receiver struct { + restClient rest.Client +} + +func (recv *receiver) receive(wm webmention) { log.Info().Str("webmention", wm.String()).Msg("OK: looks valid") - body, geterr := common.Get(wm.source) + body, geterr := recv.restClient.GetBody(wm.source) if geterr != nil { log.Warn().Str("source", wm.source).Msg(" ABORT: invalid url") - wm.deletePossibleOlderWebmention() + recv.deletePossibleOlderWebmention(wm) return } - wm.processSourceBody(body) + recv.processSourceBody(body, wm) } -func (wm *webmention) deletePossibleOlderWebmention() { +func (recv *receiver) deletePossibleOlderWebmention(wm webmention) { } -func (wm *webmention) processSourceBody(body string) { +func (recv *receiver) processSourceBody(body string, wm webmention) { } diff --git a/app/webmention/validate.go b/app/webmention/validate.go index 50c6e6d..9bfbbb1 100644 --- a/app/webmention/validate.go +++ b/app/webmention/validate.go @@ -3,9 +3,9 @@ package webmention import ( "strings" - "net/http" "github.com/wgroeneveld/go-jamming/common" + "github.com/wgroeneveld/go-jamming/rest" "github.com/rs/zerolog/log" ) @@ -24,16 +24,8 @@ func isValidDomain(url string, conf *common.Config) bool { return false } -// great, these are needed to do the structural typing for the tests... -type httpReq interface { - FormValue(key string) string -} -type httpHeader interface { - Get(key string) string -} - -func isValidTargetUrl(url string) bool { - _, err := http.Get(url) +func isValidTargetUrl(url string, httpClient rest.Client) bool { + _, err := httpClient.Get(url) if err != nil { log.Warn().Str("target", url).Msg("Invalid target URL") return false @@ -41,7 +33,7 @@ func isValidTargetUrl(url string) bool { return true } -func validate(r httpReq, h httpHeader, conf *common.Config) bool { +func validate(r rest.HttpReq, h rest.HttpHeader, conf *common.Config) bool { return h.Get("Content-Type") == "application/x-www-form-urlencoded" && isValidUrl(r.FormValue("source")) && isValidUrl(r.FormValue("target")) && diff --git a/app/webmention/validate_test.go b/app/webmention/validate_test.go index f6893dd..37b88dc 100644 --- a/app/webmention/validate_test.go +++ b/app/webmention/validate_test.go @@ -3,6 +3,8 @@ package webmention import ( "testing" + "errors" + "net/http" "github.com/wgroeneveld/go-jamming/common" ) @@ -109,7 +111,34 @@ func TestValidate(t *testing.T) { t.Fatalf("got %v, want %v", actual, tc.expected) } }) - } - - + } +} + +type restClientMock struct { +} + +func (client *restClientMock) Get(url string) (*http.Response, error) { + if url == "failing" { + return nil, errors.New("whoops") + } + return nil, nil +} +func (client *restClientMock) GetBody(url string) (string, error) { + return "", nil +} + +func TestIsValidTargetUrlFalseIfGetFails(t *testing.T) { + client := &restClientMock{} + result := isValidTargetUrl("failing", client) + if result != false { + t.Fatalf("expected to fail") + } +} + +func TestIsValidTargetUrlTrueIfGetSucceeds(t *testing.T) { + client := &restClientMock{} + result := isValidTargetUrl("valid stuff!", client) + if result != true { + t.Fatalf("expected to succeed") + } } diff --git a/common/httputils.go b/rest/client.go similarity index 61% rename from common/httputils.go rename to rest/client.go index edb7149..fe391e2 100644 --- a/common/httputils.go +++ b/rest/client.go @@ -1,23 +1,22 @@ -package common +package rest import ( "fmt" "net/http" - "io/ioutil" + "io/ioutil" ) -func BadRequest(w http.ResponseWriter) { - http.Error(w, "400 bad request", http.StatusBadRequest) +type Client interface { + Get(url string) (*http.Response, error) + GetBody(url string) (string, error) } -func Accept(w http.ResponseWriter) { - w.WriteHeader(202) - w.Write([]byte("Thanks, bro. Will send these webmentions soon, pinky swear!")) +type HttpClient struct { } // something like this? https://freshman.tech/snippets/go/http-response-to-string/ -func Get(url string) (string, error) { +func (client *HttpClient) GetBody(url string) (string, error) { resp, geterr := http.Get(url) if geterr != nil { return "", geterr @@ -35,3 +34,8 @@ func Get(url string) (string, error) { return string(body), nil } + + +func (client *HttpClient) Get(url string) (*http.Response, error) { + return http.Get(url) +} diff --git a/rest/utils.go b/rest/utils.go new file mode 100644 index 0000000..3ca5212 --- /dev/null +++ b/rest/utils.go @@ -0,0 +1,15 @@ + +package rest + +import ( + "net/http" +) + +func BadRequest(w http.ResponseWriter) { + http.Error(w, "400 bad request", http.StatusBadRequest) +} + +func Accept(w http.ResponseWriter) { + w.WriteHeader(202) + w.Write([]byte("Thanks, bro. Will send these webmentions soon, pinky swear!")) +} diff --git a/rest/wrappers.go b/rest/wrappers.go new file mode 100644 index 0000000..a3f9387 --- /dev/null +++ b/rest/wrappers.go @@ -0,0 +1,10 @@ + +package rest + +// great, these are needed to do the structural typing for the tests... +type HttpReq interface { + FormValue(key string) string +} +type HttpHeader interface { + Get(key string) string +}