Skip to content

Commit 552033d

Browse files
authored
Add more tests (#6)
1 parent 85ed361 commit 552033d

22 files changed

+1026
-29
lines changed

.github/workflows/test.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
go-version: ["1.21", "1.22", "1.23"]
13+
go-version: ["1.21", "1.22", "1.23", "1.24", "1.25"]
1414
include:
15-
- go-version: "1.23"
16-
upload-coverage: true
1715
- go-version: "1.21"
1816
upload-coverage: false
1917
- go-version: "1.22"
2018
upload-coverage: false
19+
- go-version: "1.23"
20+
upload-coverage: false
21+
- go-version: "1.24"
22+
upload-coverage: false
23+
- go-version: "1.25"
24+
upload-coverage: true
2125
steps:
2226
- uses: actions/checkout@v4
2327

.mockery.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ packages:
88
dir: .
99
mockname: mockDoer
1010
inpackage: true
11+
readCloser:
12+
config:
13+
filename: mock_read_closer.go
14+
dir: .
15+
mockname: mockReadCloser
16+
inpackage: true

client.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ type doer interface {
1212
Do(req *http.Request) (*http.Response, error)
1313
}
1414

15+
// readCloser is just an interface to generate mocks.
16+
//
17+
//nolint:unused
18+
type readCloser interface {
19+
io.ReadCloser
20+
}
21+
1522
// Client is a client for the Temp Mail API.
1623
type Client struct {
1724
// doer is an HTTP client.

client_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"testing"
1010

1111
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/mock"
1213
"github.com/stretchr/testify/require"
1314
)
1415

@@ -56,6 +57,15 @@ func TestNewRequest(t *testing.T) {
5657
require.NoError(t, err)
5758
assert.JSONEq(t, `{"domain":"example.com"}`, string(b))
5859
})
60+
61+
t.Run("JSON marshal error", func(t *testing.T) {
62+
c := newClient()
63+
// Create a value that cannot be marshaled to JSON
64+
invalidData := make(chan int)
65+
req, err := c.newRequest(context.Background(), http.MethodPost, "/v1/emails", invalidData)
66+
assert.Error(t, err)
67+
assert.Nil(t, req)
68+
})
5969
}
6070

6171
// readFile reads the file from the given path.
@@ -72,3 +82,124 @@ func newTestResponse(statusCode int, body []byte) *http.Response {
7282
Body: io.NopCloser(bytes.NewReader(body)),
7383
}
7484
}
85+
86+
func TestClient_do(t *testing.T) {
87+
t.Run("success with nil v", func(t *testing.T) {
88+
mDoer := newMockDoer(t)
89+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusOK, []byte{}), nil)
90+
91+
c := newClient()
92+
c.doer = mDoer
93+
req, err := c.newRequest(context.Background(), http.MethodGet, "/test", nil)
94+
require.NoError(t, err)
95+
resp, err := c.do(req, nil)
96+
require.NoError(t, err)
97+
assert.Equal(t, 200, resp.StatusCode)
98+
})
99+
100+
t.Run("error from rawDo", func(t *testing.T) {
101+
mDoer := newMockDoer(t)
102+
mDoer.EXPECT().Do(mock.Anything).Return(nil, assert.AnError)
103+
104+
c := newClient()
105+
c.doer = mDoer
106+
req, err := c.newRequest(context.Background(), http.MethodGet, "/test", nil)
107+
require.NoError(t, err)
108+
_, err = c.do(req, nil)
109+
assert.EqualError(t, err, assert.AnError.Error())
110+
})
111+
112+
t.Run("error from checkResponse", func(t *testing.T) {
113+
mDoer := newMockDoer(t)
114+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusBadRequest, readFile(t, "testdata/error_response.json")), nil)
115+
116+
c := newClient()
117+
c.doer = mDoer
118+
req, err := c.newRequest(context.Background(), http.MethodGet, "/test", nil)
119+
require.NoError(t, err)
120+
_, err = c.do(req, nil)
121+
require.Error(t, err)
122+
var httpErr *HTTPError
123+
assert.ErrorAs(t, err, &httpErr)
124+
assert.NotNil(t, httpErr.Response)
125+
assert.Equal(t, HTTPErrorError{
126+
Type: "request_error",
127+
Code: "not_found",
128+
Detail: "Attachment not found",
129+
}, httpErr.ErrorDetails)
130+
assert.Equal(t, HTTPErrorMeta{
131+
RequestID: "req_123456789",
132+
}, httpErr.Meta)
133+
})
134+
135+
t.Run("JSON decode error", func(t *testing.T) {
136+
mDoer := newMockDoer(t)
137+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(200, []byte("invalid json")), nil)
138+
139+
c := newClient()
140+
c.doer = mDoer
141+
req, err := c.newRequest(context.Background(), http.MethodGet, "/test", nil)
142+
require.NoError(t, err)
143+
var result map[string]interface{}
144+
_, err = c.do(req, &result)
145+
require.Error(t, err)
146+
assert.EqualError(t, err, "invalid character 'i' looking for beginning of value")
147+
})
148+
}
149+
150+
func TestClient_checkResponse(t *testing.T) {
151+
t.Run("success response", func(t *testing.T) {
152+
c := newClient()
153+
resp := newResponse(newTestResponse(http.StatusOK, []byte{}))
154+
err := c.checkResponse(resp)
155+
require.NoError(t, err)
156+
})
157+
158+
t.Run("error response", func(t *testing.T) {
159+
c := newClient()
160+
resp := newResponse(newTestResponse(http.StatusBadRequest, readFile(t, "testdata/error_response.json")))
161+
err := c.checkResponse(resp)
162+
require.Error(t, err)
163+
var httpErr *HTTPError
164+
require.ErrorAs(t, err, &httpErr)
165+
assert.Equal(t, 400, httpErr.Response.StatusCode)
166+
assert.Equal(t, "request_error", httpErr.ErrorDetails.Type)
167+
assert.Equal(t, "not_found", httpErr.ErrorDetails.Code)
168+
})
169+
170+
t.Run("error response with invalid JSON", func(t *testing.T) {
171+
c := newClient()
172+
resp := newResponse(newTestResponse(http.StatusBadGateway, []byte("invalid json")))
173+
err := c.checkResponse(resp)
174+
require.Error(t, err)
175+
assert.Errorf(t, err, "invalid character 'i' looking for beginning of value")
176+
})
177+
}
178+
179+
func TestClient_rawDo(t *testing.T) {
180+
t.Run("success", func(t *testing.T) {
181+
mDoer := newMockDoer(t)
182+
expectedResp := newTestResponse(http.StatusOK, []byte("test"))
183+
mDoer.EXPECT().Do(mock.Anything).Return(expectedResp, nil)
184+
185+
c := newClient()
186+
c.doer = mDoer
187+
req, err := c.newRequest(context.Background(), http.MethodGet, "/test", nil)
188+
require.NoError(t, err)
189+
resp, err := c.rawDo(req)
190+
require.NoError(t, err)
191+
assert.Equal(t, 200, resp.StatusCode)
192+
})
193+
194+
t.Run("error from doer", func(t *testing.T) {
195+
mDoer := newMockDoer(t)
196+
mDoer.EXPECT().Do(mock.Anything).Return(nil, assert.AnError)
197+
198+
c := newClient()
199+
c.doer = mDoer
200+
req, err := c.newRequest(context.Background(), http.MethodGet, "/test", nil)
201+
require.NoError(t, err)
202+
_, err = c.rawDo(req)
203+
assert.EqualError(t, err, assert.AnError.Error())
204+
})
205+
}

create_email_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package tempmail
22

33
import (
44
"context"
5+
"net/http"
56
"os"
67
"testing"
78
"time"
89

910
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/mock"
12+
"github.com/stretchr/testify/require"
1013
"github.com/stretchr/testify/suite"
1114
)
1215

@@ -60,3 +63,59 @@ func testRateLimit(t *testing.T, resp *Response) {
6063
assert.NotZero(t, resp.Rate.Used)
6164
assert.True(t, resp.Rate.Reset.After(time.Now()), "reset time should be in the future")
6265
}
66+
67+
func TestClient_CreateEmail(t *testing.T) {
68+
t.Run("success with empty options", func(t *testing.T) {
69+
mDoer := newMockDoer(t)
70+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusOK, readFile(t, "testdata/create_email.json")), nil)
71+
72+
c := newClient()
73+
c.doer = mDoer
74+
result, resp, err := c.CreateEmail(context.Background(), CreateEmailOptions{})
75+
require.NoError(t, err)
76+
assert.Equal(t, 200, resp.StatusCode)
77+
78+
expected := CreateEmailResponse{
79+
80+
TTL: 3600 * time.Second,
81+
}
82+
assert.Equal(t, expected, result)
83+
})
84+
85+
t.Run("success with custom options", func(t *testing.T) {
86+
mDoer := newMockDoer(t)
87+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusOK, readFile(t, "testdata/create_email.json")), nil)
88+
89+
c := newClient()
90+
c.doer = mDoer
91+
result, resp, err := c.CreateEmail(context.Background(), CreateEmailOptions{
92+
93+
DomainType: DomainTypePublic,
94+
Domain: "example.com",
95+
})
96+
require.NoError(t, err)
97+
assert.Equal(t, 200, resp.StatusCode)
98+
assert.NotEmpty(t, result.Email)
99+
assert.NotZero(t, result.TTL)
100+
})
101+
102+
t.Run("error from newRequest", func(t *testing.T) {
103+
c := newClient()
104+
_, _, err := c.CreateEmail(nil, CreateEmailOptions{})
105+
assert.EqualError(t, err, "net/http: nil Context")
106+
})
107+
108+
t.Run("error from do", func(t *testing.T) {
109+
mDoer := newMockDoer(t)
110+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusBadRequest, readFile(t, "testdata/error_response.json")), nil)
111+
112+
c := newClient()
113+
c.doer = mDoer
114+
_, _, err := c.CreateEmail(context.Background(), CreateEmailOptions{Email: "invalid"})
115+
require.Error(t, err)
116+
var httpErr *HTTPError
117+
require.ErrorAs(t, err, &httpErr)
118+
assert.Equal(t, "request_error", httpErr.ErrorDetails.Type)
119+
assert.Equal(t, "not_found", httpErr.ErrorDetails.Code)
120+
})
121+
}

delete_email_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package tempmail
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/mock"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestClient_DeleteEmail(t *testing.T) {
14+
t.Run("success", func(t *testing.T) {
15+
mDoer := newMockDoer(t)
16+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusOK, []byte("{}")), nil)
17+
18+
c := newClient()
19+
c.doer = mDoer
20+
resp, err := c.DeleteEmail(context.Background(), "[email protected]")
21+
require.NoError(t, err)
22+
assert.Equal(t, 200, resp.StatusCode)
23+
})
24+
25+
t.Run("error from newRequest", func(t *testing.T) {
26+
c := newClient()
27+
_, err := c.DeleteEmail(nil, "[email protected]")
28+
assert.EqualError(t, err, "net/http: nil Context")
29+
})
30+
31+
t.Run("error from do", func(t *testing.T) {
32+
mDoer := newMockDoer(t)
33+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusBadRequest, readFile(t, "testdata/error_response.json")), nil)
34+
35+
c := newClient()
36+
c.doer = mDoer
37+
_, err := c.DeleteEmail(context.Background(), "[email protected]")
38+
require.Error(t, err)
39+
var httpErr *HTTPError
40+
require.ErrorAs(t, err, &httpErr)
41+
assert.Equal(t, "request_error", httpErr.ErrorDetails.Type)
42+
assert.Equal(t, "not_found", httpErr.ErrorDetails.Code)
43+
})
44+
}

delete_message_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package tempmail
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/mock"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestClient_DeleteMessage(t *testing.T) {
14+
t.Run("success", func(t *testing.T) {
15+
mDoer := newMockDoer(t)
16+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusOK, []byte("{}")), nil)
17+
18+
c := newClient()
19+
c.doer = mDoer
20+
resp, err := c.DeleteMessage(context.Background(), "01JE97FT950QRPDYGDXJ4R43QR")
21+
require.NoError(t, err)
22+
assert.Equal(t, 200, resp.StatusCode)
23+
})
24+
25+
t.Run("error from newRequest", func(t *testing.T) {
26+
c := newClient()
27+
_, err := c.DeleteMessage(nil, "01JE97FT950QRPDYGDXJ4R43QR")
28+
assert.EqualError(t, err, "net/http: nil Context")
29+
})
30+
31+
t.Run("error from do", func(t *testing.T) {
32+
mDoer := newMockDoer(t)
33+
mDoer.EXPECT().Do(mock.Anything).Return(newTestResponse(http.StatusBadRequest, readFile(t, "testdata/error_response.json")), nil)
34+
35+
c := newClient()
36+
c.doer = mDoer
37+
_, err := c.DeleteMessage(context.Background(), "nonexistent_id")
38+
require.Error(t, err)
39+
var httpErr *HTTPError
40+
require.ErrorAs(t, err, &httpErr)
41+
assert.Equal(t, "request_error", httpErr.ErrorDetails.Type)
42+
assert.Equal(t, "not_found", httpErr.ErrorDetails.Code)
43+
})
44+
}

0 commit comments

Comments
 (0)