Skip to content

Commit 7c1e18b

Browse files
authored
Merge branch 'development' into patch-2
2 parents c017371 + 9430945 commit 7c1e18b

File tree

8 files changed

+460
-144
lines changed

8 files changed

+460
-144
lines changed

examples/http-server/main_test.go

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@ import (
55
"encoding/json"
66
"io"
77
"net/http"
8+
"net/http/httptest"
89
"os"
910
"strconv"
11+
"strings"
1012
"testing"
1113
"time"
1214

1315
"github.com/go-redis/redismock/v9"
1416
"github.com/stretchr/testify/assert"
1517
"github.com/stretchr/testify/require"
18+
"go.uber.org/mock/gomock"
1619

1720
"gofr.dev/pkg/gofr"
1821
"gofr.dev/pkg/gofr/config"
1922
"gofr.dev/pkg/gofr/container"
2023
"gofr.dev/pkg/gofr/datasource/redis"
2124
"gofr.dev/pkg/gofr/logging"
25+
"gofr.dev/pkg/gofr/service"
2226
"gofr.dev/pkg/gofr/testutil"
2327
)
2428

@@ -140,7 +144,7 @@ func TestIntegration_SimpleAPIServer_Health(t *testing.T) {
140144
statusCode int
141145
}{
142146
{"health handler", "/.well-known/health", http.StatusOK}, // Health check should be added by the framework.
143-
{"favicon handler", "/favicon.ico", http.StatusOK}, //Favicon should be added by the framework.
147+
{"favicon handler", "/favicon.ico", http.StatusOK}, // Favicon should be added by the framework.
144148
}
145149

146150
for i, tc := range tests {
@@ -180,3 +184,140 @@ func TestRedisHandler(t *testing.T) {
180184
assert.Nil(t, resp)
181185
require.Error(t, err)
182186
}
187+
188+
// MockRequest implements the Request interface for testing
189+
type MockRequest struct {
190+
*http.Request
191+
params map[string]string
192+
}
193+
194+
func (m *MockRequest) HostName() string {
195+
if m.Request != nil {
196+
return m.Request.Host
197+
}
198+
199+
return ""
200+
}
201+
202+
func (m *MockRequest) Params(s string) []string {
203+
if m.Request != nil {
204+
return m.Request.URL.Query()[s]
205+
}
206+
207+
return nil
208+
}
209+
210+
func NewMockRequest(req *http.Request) *MockRequest {
211+
// Parse query parameters
212+
queryParams := make(map[string]string)
213+
for k, v := range req.URL.Query() {
214+
if len(v) > 0 {
215+
queryParams[k] = v[0]
216+
}
217+
}
218+
219+
return &MockRequest{
220+
Request: req,
221+
params: queryParams,
222+
}
223+
}
224+
225+
// Param returns URL query parameters
226+
func (m *MockRequest) Param(key string) string {
227+
return m.params[key]
228+
}
229+
230+
// PathParam returns URL path parameters
231+
func (m *MockRequest) PathParam(key string) string {
232+
return ""
233+
}
234+
235+
// Bind implements the Bind method required by the Request interface
236+
func (m *MockRequest) Bind(i any) error {
237+
return nil
238+
}
239+
240+
// createTestContext sets up a GoFr context for unit tests with a given URL and optional mock container.
241+
func createTestContext(method, url string, mockContainer *container.Container) *gofr.Context {
242+
req := httptest.NewRequest(method, url, nil)
243+
mockReq := NewMockRequest(req)
244+
245+
var c *container.Container
246+
if mockContainer != nil {
247+
c = mockContainer
248+
} else {
249+
c = &container.Container{Logger: logging.NewLogger(logging.DEBUG)}
250+
}
251+
252+
logger := c.Logger
253+
254+
return &gofr.Context{
255+
Context: req.Context(),
256+
Request: mockReq,
257+
Container: c,
258+
ContextLogger: *logging.NewContextLogger(req.Context(), logger),
259+
}
260+
}
261+
262+
func TestHelloHandler(t *testing.T) {
263+
// With name parameter
264+
ctx := createTestContext(http.MethodGet, "/hello?name=test", nil)
265+
resp, err := HelloHandler(ctx)
266+
assert.NoError(t, err)
267+
assert.Equal(t, "Hello test!", resp)
268+
269+
// Without name parameter
270+
ctx = createTestContext(http.MethodGet, "/hello", nil)
271+
resp, err = HelloHandler(ctx)
272+
assert.NoError(t, err)
273+
assert.Equal(t, "Hello World!", resp)
274+
}
275+
276+
func TestErrorHandler(t *testing.T) {
277+
ctx := createTestContext(http.MethodGet, "/error", nil)
278+
279+
resp, err := ErrorHandler(ctx)
280+
assert.Nil(t, resp)
281+
assert.Error(t, err)
282+
assert.Equal(t, "some error occurred", err.Error())
283+
}
284+
285+
func TestMysqlHandler(t *testing.T) {
286+
mockContainer, mocks := container.NewMockContainer(t)
287+
288+
// Setup SQL mock to return 4
289+
mocks.SQL.ExpectQuery("select 2+2").
290+
WillReturnRows(mocks.SQL.NewRows([]string{"value"}).AddRow(4))
291+
292+
ctx := createTestContext(http.MethodGet, "/mysql", mockContainer)
293+
294+
resp, err := MysqlHandler(ctx)
295+
assert.NoError(t, err)
296+
assert.Equal(t, 4, resp)
297+
}
298+
299+
func TestTraceHandler(t *testing.T) {
300+
mockContainer, mocks := container.NewMockContainer(t, container.WithMockHTTPService())
301+
302+
// Redis expectations
303+
mocks.Redis.EXPECT().Ping(gomock.Any()).Return(nil).Times(5)
304+
305+
// HTTP service mock
306+
httpService := mocks.HTTPService
307+
mockResp := &http.Response{
308+
StatusCode: http.StatusOK,
309+
Body: io.NopCloser(strings.NewReader(`{"data":"mock data"}`)),
310+
}
311+
httpService.EXPECT().Get(gomock.Any(), "redis", gomock.Any()).Return(mockResp, nil)
312+
313+
// Attach service to container
314+
mockContainer.Services = map[string]service.HTTP{
315+
"anotherService": httpService,
316+
}
317+
318+
ctx := createTestContext(http.MethodGet, "/trace", mockContainer)
319+
320+
resp, err := TraceHandler(ctx)
321+
assert.NoError(t, err)
322+
assert.Equal(t, "mock data", resp)
323+
}

examples/sample-cmd/main_test.go

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,76 @@ func TestCMDRun_ProgressContextCancelled(t *testing.T) {
9898
ctx, cancel := context.WithCancel(context.Background())
9999
cancel()
100100

101-
// add an already canceled context
101+
// Create a proper context with logger to avoid nil pointer dereference
102+
container := &container.Container{
103+
Logger: logging.NewMockLogger(logging.ERROR),
104+
}
105+
102106
res, err := progress(&gofr.Context{
103-
Context: ctx,
104-
Request: cmd.NewRequest([]string{"command", "spinner"}),
105-
Container: &container.Container{
106-
Logger: logging.NewMockLogger(logging.ERROR),
107-
},
108-
Out: terminal.New(),
107+
Context: ctx,
108+
Request: cmd.NewRequest([]string{"command", "progress"}),
109+
Container: container,
110+
Out: terminal.New(),
111+
ContextLogger: *logging.NewContextLogger(ctx, container.Logger),
109112
})
110113

111114
assert.Empty(t, res)
112115
assert.ErrorIs(t, err, context.Canceled)
113116
}
117+
118+
// TestCMDRunWithInvalidCommand tests that invalid commands return appropriate error
119+
func TestCMDRunWithInvalidCommand(t *testing.T) {
120+
expErr := "No Command Found!\n"
121+
os.Args = []string{"command", "invalid"}
122+
output := testutil.StderrOutputForFunc(main)
123+
124+
assert.Equal(t, expErr, output, "TEST Failed.\n")
125+
}
126+
127+
// TestCMDRunWithEmptyParams tests the params command with empty name parameter
128+
func TestCMDRunWithEmptyParams(t *testing.T) {
129+
expResp := "Hello !\n"
130+
os.Args = []string{"command", "params", "-name="}
131+
output := testutil.StdoutOutputForFunc(main)
132+
133+
assert.Contains(t, output, expResp, "TEST Failed.\n")
134+
}
135+
136+
// TestCMDRunHelpCommand tests the help functionality
137+
func TestCMDRunHelpCommand(t *testing.T) {
138+
testCases := []struct {
139+
args []string
140+
expected []string
141+
}{
142+
{[]string{"command", "help"}, []string{"Available commands:", "hello", "params", "spinner", "progress"}},
143+
{[]string{"command", "-h"}, []string{"Available commands:", "hello", "params", "spinner", "progress"}},
144+
{[]string{"command", "--help"}, []string{"Available commands:", "hello", "params", "spinner", "progress"}},
145+
}
146+
147+
for i, tc := range testCases {
148+
os.Args = tc.args
149+
output := testutil.StdoutOutputForFunc(main)
150+
151+
for _, expected := range tc.expected {
152+
assert.Contains(t, output, expected, "TEST[%d] Failed. Expected to contain: %s\n", i, expected)
153+
}
154+
}
155+
}
156+
157+
// TestCMDRunHelpForSpecificCommand tests help for specific commands
158+
func TestCMDRunHelpForSpecificCommand(t *testing.T) {
159+
testCases := []struct {
160+
args []string
161+
expected string
162+
}{
163+
{[]string{"command", "hello", "-h"}, "hello world option"},
164+
{[]string{"command", "hello", "--help"}, "hello world option"},
165+
}
166+
167+
for i, tc := range testCases {
168+
os.Args = tc.args
169+
output := testutil.StdoutOutputForFunc(main)
170+
171+
assert.Contains(t, output, tc.expected, "TEST[%d] Failed.\n", i)
172+
}
173+
}

examples/using-custom-metrics/main_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ func TestIntegration(t *testing.T) {
5454

5555
assert.Equal(t, http.StatusOK, resp.StatusCode, "TEST[%d], Failed.\n%s")
5656

57-
assert.Contains(t, strBody, `product_stock{otel_scope_name="using-metrics",otel_scope_version="v0.1.0"} 50`)
58-
assert.Contains(t, strBody, `total_credit_day_sale{otel_scope_name="using-metrics",otel_scope_version="v0.1.0",sale_type="credit"} 1000`)
59-
assert.Contains(t, strBody, `total_credit_day_sale{otel_scope_name="using-metrics",otel_scope_version="v0.1.0",sale_type="credit_return"} -1000`)
60-
assert.Contains(t, strBody, `transaction_success_total{otel_scope_name="using-metrics",otel_scope_version="v0.1.0"} 1`)
57+
assert.Contains(t, strBody, `product_stock{otel_scope_name="using-metrics",otel_scope_schema_url="",otel_scope_version="v0.1.0"} 50`)
58+
assert.Contains(t, strBody, `total_credit_day_sale{otel_scope_name="using-metrics",otel_scope_schema_url="",otel_scope_version="v0.1.0",sale_type="credit"} 1000`)
59+
assert.Contains(t, strBody, `total_credit_day_sale{otel_scope_name="using-metrics",otel_scope_schema_url="",otel_scope_version="v0.1.0",sale_type="credit_return"} -1000`)
60+
assert.Contains(t, strBody, `transaction_success{otel_scope_name="using-metrics",otel_scope_schema_url="",otel_scope_version="v0.1.0"} 1`)
6161
assert.Contains(t, strBody, "transaction_time")
6262
}

examples/using-http-service/main_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/stretchr/testify/assert"
1515
"github.com/stretchr/testify/require"
16+
"go.uber.org/mock/gomock"
1617

1718
"gofr.dev/pkg/gofr"
1819
"gofr.dev/pkg/gofr/container"
@@ -82,12 +83,15 @@ func TestHTTPHandlerURLError(t *testing.T) {
8283
fmt.Sprint("http://localhost:", port, "/handle"), bytes.NewBuffer([]byte(`{"key":"value"}`)))
8384
gofrReq := gofrHTTP.NewRequest(req)
8485

85-
mockContainer, _ := container.NewMockContainer(t)
86+
mockContainer, mocks := container.NewMockContainer(t)
8687

8788
ctx := &gofr.Context{Context: context.Background(), Request: gofrReq, Container: mockContainer}
8889

8990
ctx.Container.Services = map[string]service.HTTP{"cat-facts": service.NewHTTPService("http://invalid", ctx.Logger, mockContainer.Metrics())}
9091

92+
mocks.Metrics.EXPECT().RecordHistogram(gomock.Any(), "app_http_service_response", gomock.Any(), gomock.Any(),
93+
"http://invalid", "method", "GET", "status", gomock.Any())
94+
9195
resp, err := Handler(ctx)
9296

9397
assert.Nil(t, resp)

0 commit comments

Comments
 (0)