Skip to content

Commit 2460b08

Browse files
committed
test: httpx
1 parent 80c9c06 commit 2460b08

File tree

7 files changed

+241
-12
lines changed

7 files changed

+241
-12
lines changed

httpx/httpx.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ func IsURL(path string) bool {
2626
if u.Scheme == "" {
2727
return false
2828
}
29+
if u.Scheme != "http" && u.Scheme != "https" {
30+
return false
31+
}
2932
return true
3033
}
3134

@@ -72,6 +75,16 @@ func GetBody(url string, accept string) (io.ReadCloser, error) {
7275
return resp.Body, nil
7376
}
7477

78+
// GetBytes issues a GET request and decodes the response as bytes.
79+
func GetBytes(url string) ([]byte, error) {
80+
body, err := GetBody(url, "*/*")
81+
if err != nil {
82+
return nil, err
83+
}
84+
defer body.Close()
85+
return io.ReadAll(body)
86+
}
87+
7588
// GetJSON issues a GET request and decodes the response as JSON.
7689
func GetJSON[T any](url string) (*T, error) {
7790
body, err := GetBody(url, "application/json")
@@ -93,13 +106,3 @@ func GetJSON[T any](url string) (*T, error) {
93106

94107
return &val, nil
95108
}
96-
97-
// GetBytes issues a GET request and decodes the response as bytes.
98-
func GetBytes(url string) ([]byte, error) {
99-
body, err := GetBody(url, "*/*")
100-
if err != nil {
101-
return nil, err
102-
}
103-
defer body.Close()
104-
return io.ReadAll(body)
105-
}

httpx/httpx_test.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package httpx
2+
3+
import (
4+
"io"
5+
"testing"
6+
)
7+
8+
func TestIsURL(t *testing.T) {
9+
tests := []struct {
10+
url string
11+
ok bool
12+
}{
13+
{"https://antonz.org/sqlpkg.json", true},
14+
{"https://github.com/nalgeon/sqlpkg/raw/main/pkg/sqlite/stmt.json", true},
15+
{"https://raw.githubusercontent.com/nalgeon/sqlpkg/main/pkg/sqlite/stmt.json", true},
16+
{"./testdata/sqlpkg.json", false},
17+
{"/Users/anton/sqlpkg.json", false},
18+
{"file:///Users/anton/sqlpkg.json", false},
19+
}
20+
for _, test := range tests {
21+
ok := IsURL(test.url)
22+
if ok != test.ok {
23+
t.Errorf("IsURL(%s): expected %v, got %v", test.url, test.ok, ok)
24+
}
25+
}
26+
}
27+
28+
func TestHostname(t *testing.T) {
29+
tests := []struct {
30+
url string
31+
host string
32+
}{
33+
{"https://antonz.org/sqlpkg.json", "antonz.org"},
34+
{"https://github.com/nalgeon/sqlpkg/raw/main/pkg/sqlite/stmt.json", "github.com"},
35+
{"https://raw.githubusercontent.com/nalgeon/sqlpkg/main/pkg/sqlite/stmt.json", "raw.githubusercontent.com"},
36+
{"./testdata/sqlpkg.json", ""},
37+
{"/Users/anton/sqlpkg.json", ""},
38+
{"file:///Users/anton/sqlpkg.json", ""},
39+
}
40+
for _, test := range tests {
41+
host := Hostname(test.url)
42+
if host != test.host {
43+
t.Errorf("Hostname(%s): expected %v, got %v", test.url, test.host, host)
44+
}
45+
}
46+
}
47+
48+
func TestExists(t *testing.T) {
49+
srv := MockServer()
50+
defer srv.Close()
51+
52+
t.Run("exists", func(t *testing.T) {
53+
ok := Exists(srv.URL + "/sqlpkg.json")
54+
if !ok {
55+
t.Errorf("Exists: unexpected %v", ok)
56+
}
57+
})
58+
t.Run("does not exist", func(t *testing.T) {
59+
ok := Exists(srv.URL + "/missing.json")
60+
if ok {
61+
t.Errorf("Exists: unexpected %v", ok)
62+
}
63+
})
64+
}
65+
66+
func TestGetBody(t *testing.T) {
67+
srv := MockServer()
68+
defer srv.Close()
69+
70+
t.Run("success", func(t *testing.T) {
71+
body, err := GetBody(srv.URL+"/example.txt", "text/plain")
72+
if err != nil {
73+
t.Errorf("GetBody: unexpected error %v", err)
74+
}
75+
defer body.Close()
76+
77+
data, err := io.ReadAll(body)
78+
if err != nil {
79+
t.Errorf("io.ReadAll: unexpected error %v", err)
80+
}
81+
82+
if string(data) != "example.txt" {
83+
t.Errorf("GetBody: unexpected value %q", string(data))
84+
}
85+
})
86+
t.Run("failure", func(t *testing.T) {
87+
_, err := GetBody(srv.URL+"/missing.txt", "text/plain")
88+
if err == nil {
89+
t.Error("GetBody: expected error, got nil")
90+
}
91+
})
92+
}
93+
94+
func TestGetBytes(t *testing.T) {
95+
srv := MockServer()
96+
defer srv.Close()
97+
98+
t.Run("success", func(t *testing.T) {
99+
data, err := GetBytes(srv.URL + "/example.txt")
100+
if err != nil {
101+
t.Errorf("GetBytes: unexpected error %v", err)
102+
}
103+
if string(data) != "example.txt" {
104+
t.Errorf("GetBytes: unexpected value %q", string(data))
105+
}
106+
})
107+
t.Run("failure", func(t *testing.T) {
108+
_, err := GetBytes(srv.URL + "/missing.txt")
109+
if err == nil {
110+
t.Error("GetBytes: expected error, got nil")
111+
}
112+
})
113+
}
114+
115+
func TestGetJSON(t *testing.T) {
116+
srv := MockServer()
117+
defer srv.Close()
118+
119+
t.Run("success", func(t *testing.T) {
120+
type Example struct{ Body string }
121+
ex, err := GetJSON[Example](srv.URL + "/example.json")
122+
if err != nil {
123+
t.Errorf("GetJSON: unexpected error %v", err)
124+
}
125+
if ex.Body != "example.txt" {
126+
t.Errorf("GetJSON: unexpected value %q", ex.Body)
127+
}
128+
})
129+
t.Run("failure", func(t *testing.T) {
130+
type Example struct{ Body string }
131+
_, err := GetJSON[Example](srv.URL + "/example.txt")
132+
if err == nil {
133+
t.Error("GetJSON: expected error, got nil")
134+
}
135+
})
136+
}

httpx/mock.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"net/http"
9+
"net/http/httptest"
910
"os"
1011
"path"
1112
"path/filepath"
@@ -24,8 +25,7 @@ type MockClient struct {
2425
dir string
2526
}
2627

27-
// NewFileClient creates a new MockClient and installs it
28-
// instead of the default one.
28+
// Mock creates a new MockClient and installs it instead of the default one.
2929
func Mock(path ...string) *MockClient {
3030
dir := filepath.Join("testdata", filepath.Join(path...))
3131
c := &MockClient{dir: dir}
@@ -68,3 +68,31 @@ func respond(cType string, data []byte) io.Reader {
6868
}
6969
return &buf
7070
}
71+
72+
// MockServer creates a mock HTTP server and installs its client
73+
// instead of the default one. Serves responses from the file system
74+
// instead of remote calls. Should be used for testing purposes only.
75+
func MockServer() *httptest.Server {
76+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
77+
filename := filepath.Join("testdata", path.Base(r.URL.Path))
78+
79+
data, err := os.ReadFile(filename)
80+
if err != nil {
81+
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
82+
return
83+
}
84+
85+
cType, ok := contentTypes[path.Ext(filename)]
86+
if !ok {
87+
cType = "application/octet-stream"
88+
}
89+
90+
w.Header().Set("content-type", cType)
91+
_, err = w.Write(data)
92+
if err != nil {
93+
panic(err)
94+
}
95+
}))
96+
client = srv.Client()
97+
return srv
98+
}

httpx/mock_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package httpx
2+
3+
import "testing"
4+
5+
func TestMockClient(t *testing.T) {
6+
Mock()
7+
{
8+
const url = "https://antonz.org/example.txt"
9+
ok := Exists(url)
10+
if !ok {
11+
t.Errorf("Exists(%s) expected true, got false", url)
12+
}
13+
}
14+
{
15+
const url = "https://antonz.org/missing.txt"
16+
ok := Exists(url)
17+
if ok {
18+
t.Errorf("Exists(%s) expected false, got true", url)
19+
}
20+
}
21+
{
22+
const url = "https://antonz.org/example.txt"
23+
data, err := GetBytes(url)
24+
if err != nil {
25+
t.Errorf("GetBytes: unexpected error %v", err)
26+
}
27+
if string(data) != "example.txt" {
28+
t.Errorf("GetBytes: unexpected value %q", string(data))
29+
}
30+
}
31+
{
32+
const url = "https://antonz.org/missing.txt"
33+
_, err := GetBytes(url)
34+
if err == nil {
35+
t.Error("GetBytes: expected error, got nil")
36+
}
37+
}
38+
}

httpx/testdata/example.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"body": "example.txt"
3+
}

httpx/testdata/example.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
example.txt

httpx/testdata/sqlpkg.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"owner": "nalgeon",
3+
"name": "example",
4+
"version": "0.1.0",
5+
"homepage": "https://github.com/nalgeon/sqlite-example/blob/main/README.md",
6+
"repository": "https://github.com/nalgeon/sqlite-example",
7+
"authors": ["Anton Zhiyanov"],
8+
"license": "MIT",
9+
"description": "Example extension.",
10+
"keywords": ["sqlite-example"],
11+
"assets": {
12+
"path": "{repository}/releases/download/{version}",
13+
"files": {
14+
"darwin-amd64": "example-macos-{version}-x86.zip",
15+
"darwin-arm64": "example-macos-{version}-arm64.zip",
16+
"linux-amd64": "example-linux-{version}-x86.zip",
17+
"windows-amd64": "example-win-{version}-x64.zip"
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)