attempt at primitive DI, mock own rest client

This commit is contained in:
Wouter Groeneveld 2021-04-08 09:54:47 +02:00
parent 95abdcc7f0
commit f82a581731
7 changed files with 99 additions and 37 deletions

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/wgroeneveld/go-jamming/common" "github.com/wgroeneveld/go-jamming/common"
"github.com/wgroeneveld/go-jamming/rest"
) )
func HandleGet(conf *common.Config) http.HandlerFunc { 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 { func HandlePost(conf *common.Config) http.HandlerFunc {
httpClient := &rest.HttpClient{}
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
if !validate(r, r.Header, conf) { if !validate(r, r.Header, conf) {
common.BadRequest(w) rest.BadRequest(w)
return return
} }
target := r.FormValue("target") target := r.FormValue("target")
if !isValidTargetUrl(target) { if !isValidTargetUrl(target, httpClient) {
common.BadRequest(w) rest.BadRequest(w)
return return
} }
wm := &webmention{ wm := webmention{
source: r.FormValue("source"), source: r.FormValue("source"),
target: target, target: target,
} }
recv := &receiver{
restClient: httpClient,
}
go wm.receive() go recv.receive(wm)
common.Accept(w) rest.Accept(w)
} }
} }

View File

@ -4,7 +4,7 @@ package webmention
import ( import (
"fmt" "fmt"
"github.com/wgroeneveld/go-jamming/common" "github.com/wgroeneveld/go-jamming/rest"
"github.com/rs/zerolog/log" "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) 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") 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 { if geterr != nil {
log.Warn().Str("source", wm.source).Msg(" ABORT: invalid url") log.Warn().Str("source", wm.source).Msg(" ABORT: invalid url")
wm.deletePossibleOlderWebmention() recv.deletePossibleOlderWebmention(wm)
return 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) {
} }

View File

@ -3,9 +3,9 @@ package webmention
import ( import (
"strings" "strings"
"net/http"
"github.com/wgroeneveld/go-jamming/common" "github.com/wgroeneveld/go-jamming/common"
"github.com/wgroeneveld/go-jamming/rest"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@ -24,16 +24,8 @@ func isValidDomain(url string, conf *common.Config) bool {
return false return false
} }
// great, these are needed to do the structural typing for the tests... func isValidTargetUrl(url string, httpClient rest.Client) bool {
type httpReq interface { _, err := httpClient.Get(url)
FormValue(key string) string
}
type httpHeader interface {
Get(key string) string
}
func isValidTargetUrl(url string) bool {
_, err := http.Get(url)
if err != nil { if err != nil {
log.Warn().Str("target", url).Msg("Invalid target URL") log.Warn().Str("target", url).Msg("Invalid target URL")
return false return false
@ -41,7 +33,7 @@ func isValidTargetUrl(url string) bool {
return true 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" && return h.Get("Content-Type") == "application/x-www-form-urlencoded" &&
isValidUrl(r.FormValue("source")) && isValidUrl(r.FormValue("source")) &&
isValidUrl(r.FormValue("target")) && isValidUrl(r.FormValue("target")) &&

View File

@ -3,6 +3,8 @@ package webmention
import ( import (
"testing" "testing"
"errors"
"net/http"
"github.com/wgroeneveld/go-jamming/common" "github.com/wgroeneveld/go-jamming/common"
) )
@ -109,7 +111,34 @@ func TestValidate(t *testing.T) {
t.Fatalf("got %v, want %v", actual, tc.expected) 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")
}
} }

View File

@ -1,23 +1,22 @@
package common package rest
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"io/ioutil" "io/ioutil"
) )
func BadRequest(w http.ResponseWriter) { type Client interface {
http.Error(w, "400 bad request", http.StatusBadRequest) Get(url string) (*http.Response, error)
GetBody(url string) (string, error)
} }
func Accept(w http.ResponseWriter) { type HttpClient struct {
w.WriteHeader(202)
w.Write([]byte("Thanks, bro. Will send these webmentions soon, pinky swear!"))
} }
// something like this? https://freshman.tech/snippets/go/http-response-to-string/ // 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) resp, geterr := http.Get(url)
if geterr != nil { if geterr != nil {
return "", geterr return "", geterr
@ -35,3 +34,8 @@ func Get(url string) (string, error) {
return string(body), nil return string(body), nil
} }
func (client *HttpClient) Get(url string) (*http.Response, error) {
return http.Get(url)
}

15
rest/utils.go Normal file
View File

@ -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!"))
}

10
rest/wrappers.go Normal file
View File

@ -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
}