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

View File

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

View File

@ -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")) &&

View File

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

View File

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

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
}