Skip to content

Commit 27c85b6

Browse files
committed
record req/resps in fake data provider
1 parent d629208 commit 27c85b6

File tree

3 files changed

+160
-18
lines changed

3 files changed

+160
-18
lines changed

framework/components/fake/fake.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"github.com/gin-gonic/gin"
66
"github.com/smartcontractkit/chainlink-testing-framework/framework"
7-
"net/http"
87
"os"
98
)
109

@@ -22,7 +21,7 @@ type Output struct {
2221
BaseURLDocker string `toml:"base_url_docker"`
2322
}
2423

25-
func FakeJSON(path string, response gin.H, statusCode int) error {
24+
func JSON(path string, response map[string]any, statusCode int) error {
2625
if Service == nil {
2726
return fmt.Errorf("mock service is not initialized, please set up NewFakeDataProvider in your tests")
2827
}
@@ -32,23 +31,13 @@ func FakeJSON(path string, response gin.H, statusCode int) error {
3231
return nil
3332
}
3433

35-
func runMocks(in *Input) {
36-
router := gin.Default()
37-
router.GET("/mock1", func(c *gin.Context) {
38-
c.JSON(http.StatusOK, gin.H{
39-
"message": "This is a GET request response from the mock service.",
40-
})
41-
})
42-
router.POST("/mock2", func(c *gin.Context) {
43-
c.JSON(http.StatusOK, gin.H{
44-
"message": "This is a POST request response from the mock service.",
45-
})
46-
})
47-
_ = router.Run(fmt.Sprintf(":%d", in.Port))
48-
}
49-
34+
// NewFakeDataProvider creates new fake data provider
5035
func NewFakeDataProvider(in *Input) (*Output, error) {
51-
go runMocks(in)
36+
Service = gin.Default()
37+
Service.Use(recordMiddleware())
38+
go func() {
39+
_ = Service.Run(fmt.Sprintf(":%d", in.Port))
40+
}()
5241
out := &Output{
5342
BaseURLHost: fmt.Sprintf("http://localhost:%d", in.Port),
5443
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package fake
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"github.com/gin-gonic/gin"
7+
"io/ioutil"
8+
"net/http"
9+
)
10+
11+
var (
12+
R = NewRecords()
13+
)
14+
15+
// Record is a request and response data
16+
type Record struct {
17+
Method string `json:"method"`
18+
Path string `json:"path"`
19+
Headers http.Header `json:"headers"`
20+
ReqBody string `json:"req_body"`
21+
ResBody string `json:"res_body"`
22+
Status int `json:"status"`
23+
}
24+
25+
type Records struct {
26+
r map[string]*Record
27+
}
28+
29+
func NewRecords() *Records {
30+
return &Records{make(map[string]*Record)}
31+
}
32+
33+
func (r *Records) Get(path string) (*Record, error) {
34+
rec, ok := r.r[path]
35+
if !ok {
36+
return nil, fmt.Errorf("no record was found for path: %s", path)
37+
}
38+
return rec, nil
39+
}
40+
41+
// CustomResponseWriter wraps gin.ResponseWriter to capture response data
42+
type CustomResponseWriter struct {
43+
gin.ResponseWriter
44+
body *bytes.Buffer
45+
}
46+
47+
// Write captures response data
48+
func (w *CustomResponseWriter) Write(data []byte) (int, error) {
49+
w.body.Write(data) // Capture response data
50+
return w.ResponseWriter.Write(data)
51+
}
52+
53+
// Middleware to record both requests and responses
54+
func recordMiddleware() gin.HandlerFunc {
55+
return func(c *gin.Context) {
56+
// Capture request data
57+
var reqBodyBytes []byte
58+
if c.Request.Body != nil {
59+
reqBodyBytes, _ = ioutil.ReadAll(c.Request.Body)
60+
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(reqBodyBytes))
61+
}
62+
reqBody := string(reqBodyBytes)
63+
64+
// Create custom response writer
65+
customWriter := &CustomResponseWriter{
66+
ResponseWriter: c.Writer,
67+
body: bytes.NewBufferString(""),
68+
}
69+
c.Writer = customWriter
70+
71+
// Process request
72+
c.Next()
73+
74+
// Capture response data
75+
resBody := customWriter.body.String()
76+
status := c.Writer.Status()
77+
R.r[c.Request.URL.Path] = &Record{
78+
Method: c.Request.Method,
79+
Path: c.Request.URL.Path,
80+
Headers: c.Request.Header,
81+
ReqBody: reqBody,
82+
ResBody: resBody,
83+
Status: status,
84+
}
85+
}
86+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package fake_test
2+
3+
import (
4+
"github.com/go-resty/resty/v2"
5+
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/fake"
6+
"github.com/stretchr/testify/require"
7+
"net/http"
8+
"testing"
9+
)
10+
11+
func TestRecord(t *testing.T) {
12+
cfg := &fake.Input{
13+
Port: 9111,
14+
}
15+
out, err := fake.NewFakeDataProvider(cfg)
16+
require.NoError(t, err)
17+
r := resty.New().SetBaseURL(out.BaseURLHost)
18+
19+
t.Run("can mock a response with JSON", func(t *testing.T) {
20+
apiPath := "/fake/api/1"
21+
err = fake.JSON(apiPath, map[string]any{
22+
"status": "ok",
23+
}, 200)
24+
require.NoError(t, err)
25+
var respBody struct {
26+
Status string `json:"status"`
27+
}
28+
resp, err := r.R().SetResult(&respBody).Get(apiPath)
29+
require.NoError(t, err)
30+
require.Equal(t, 200, resp.StatusCode())
31+
require.Equal(t, "ok", respBody.Status)
32+
})
33+
34+
t.Run("can record request/response and access it", func(t *testing.T) {
35+
apiPath := "/fake/api/2"
36+
err = fake.JSON(apiPath, map[string]any{
37+
"status": "ok",
38+
}, 200)
39+
require.NoError(t, err)
40+
reqBody := struct {
41+
SomeData string `json:"some_data"`
42+
}{
43+
SomeData: "some_data",
44+
}
45+
var respBody struct {
46+
Status string `json:"status"`
47+
}
48+
_, err := r.R().SetBody(reqBody).SetResult(&respBody).Post(apiPath)
49+
require.NoError(t, err)
50+
51+
// get request and response
52+
recordedData, err := fake.R.Get(apiPath)
53+
require.Equal(t, &fake.Record{
54+
Method: "POST",
55+
Path: apiPath,
56+
Headers: http.Header{
57+
"Accept-Encoding": []string{"gzip"},
58+
"Content-Type": []string{"application/json"},
59+
"Content-Length": []string{"25"},
60+
"User-Agent": []string{"go-resty/2.15.3 (https://github.com/go-resty/resty)"},
61+
},
62+
ReqBody: `{"some_data":"some_data"}`,
63+
ResBody: `{"status":"ok"}`,
64+
Status: 200,
65+
}, recordedData)
66+
})
67+
}

0 commit comments

Comments
 (0)