refactor: internal package

This commit is contained in:
Anton
2023-12-04 23:40:41 +05:00
parent 05654bd6fa
commit ad79565a93
51 changed files with 39 additions and 39 deletions

19
internal/httpx/httpx.go Normal file
View File

@@ -0,0 +1,19 @@
// Package httpx provides helper functions for making HTTP requests.
package httpx
import (
"net/http"
"time"
)
var client = Client(&http.Client{Timeout: 5 * time.Second})
// Client is something that can send HTTP requests.
type Client interface {
Do(req *http.Request) (*http.Response, error)
}
// Do sends an HTTP request and returns an HTTP response.
func Do(req *http.Request) (*http.Response, error) {
return client.Do(req)
}

View File

@@ -0,0 +1,40 @@
package httpx
import (
"net/http"
"testing"
)
func TestDo(t *testing.T) {
srv := MockServer()
defer srv.Close()
t.Run("ok", func(t *testing.T) {
uri := srv.URL + "/example.json"
req, _ := http.NewRequest(http.MethodGet, uri, nil)
resp, err := Do(req)
if err != nil {
t.Errorf("Do: unexpected error %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("Do: expected status=%d, got %v", http.StatusOK, resp.StatusCode)
}
})
t.Run("not found", func(t *testing.T) {
uri := srv.URL + "/not-found.json"
req, _ := http.NewRequest(http.MethodGet, uri, nil)
resp, err := Do(req)
if err != nil {
t.Errorf("Do: unexpected error %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("Do: expected status=%d, got %v", http.StatusNotFound, resp.StatusCode)
}
})
}

96
internal/httpx/mock.go Normal file
View File

@@ -0,0 +1,96 @@
package httpx
import (
"bufio"
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"path"
"path/filepath"
)
var contentTypes = map[string]string{
".json": "application/json",
".txt": "text/plain",
}
// MockClient serves responses from the file system instead of remote calls.
// Should be used for testing purposes only.
type MockClient struct {
dir string
}
// Mock creates a new MockClient and installs it instead of the default one.
func Mock(path ...string) *MockClient {
dir := filepath.Join("testdata", filepath.Join(path...))
c := &MockClient{dir: dir}
client = c
return c
}
// Do serves the file according to the request URL.
func (c *MockClient) Do(req *http.Request) (*http.Response, error) {
filename := filepath.Join(c.dir, path.Base(req.URL.Path))
data, err := os.ReadFile(filename)
if err != nil {
resp := http.Response{
Status: http.StatusText(http.StatusNotFound),
StatusCode: http.StatusNotFound,
}
return &resp, nil
}
cType, ok := contentTypes[path.Ext(filename)]
if !ok {
cType = "application/octet-stream"
}
rdr := respond(cType, data)
resp, err := http.ReadResponse(bufio.NewReader(rdr), req)
if err != nil {
panic(err)
}
return resp, nil
}
func respond(cType string, data []byte) io.Reader {
buf := bytes.Buffer{}
buf.WriteString("HTTP/1.1 200 OK\n")
buf.WriteString(fmt.Sprintf("Content-Type: %s\n\n", cType))
_, err := buf.Write(data)
if err != nil {
panic(err)
}
return &buf
}
// MockServer creates a mock HTTP server and installs its client
// instead of the default one. Serves responses from the file system
// instead of remote calls. Should be used for testing purposes only.
func MockServer() *httptest.Server {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
filename := filepath.Join("testdata", path.Base(r.URL.Path))
data, err := os.ReadFile(filename)
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
cType, ok := contentTypes[path.Ext(filename)]
if !ok {
cType = "application/octet-stream"
}
w.Header().Set("content-type", cType)
_, err = w.Write(data)
if err != nil {
panic(err)
}
}))
client = srv.Client()
return srv
}

View File

@@ -0,0 +1,34 @@
package httpx
import (
"io"
"net/http"
"testing"
)
func TestMockClient(t *testing.T) {
Mock()
const url = "https://codapi.org/example.txt"
req, _ := http.NewRequest("GET", url, nil)
resp, err := Do(req)
if err != nil {
t.Errorf("Do: unexpected error %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("Do: expected status code %d, got %d", http.StatusOK, resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("io.ReadAll: unexpected error %v", err)
}
want := "hello"
if string(body) != want {
t.Errorf("Do: expected %v, got %v", want, string(body))
}
}

1
internal/httpx/testdata/example.json vendored Normal file
View File

@@ -0,0 +1 @@
{ "name": "alice" }

1
internal/httpx/testdata/example.txt vendored Normal file
View File

@@ -0,0 +1 @@
hello