Skip to content

Commit f139c00

Browse files
committed
Unit tests
1 parent 733422b commit f139c00

File tree

1 file changed

+264
-0
lines changed

1 file changed

+264
-0
lines changed

pkg/dlx/handler_test.go

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
package dlx
2+
3+
import (
4+
"errors"
5+
"net/http"
6+
"net/http/httptest"
7+
"net/url"
8+
"strconv"
9+
"testing"
10+
"time"
11+
12+
"github.com/v3io/scaler/pkg/ingresscache"
13+
resourcescalerMock "github.com/v3io/scaler/pkg/resourcescaler/mock"
14+
"github.com/v3io/scaler/pkg/scalertypes"
15+
16+
"github.com/nuclio/logger"
17+
nucliozap "github.com/nuclio/zap"
18+
"github.com/stretchr/testify/mock"
19+
"github.com/stretchr/testify/suite"
20+
)
21+
22+
type HandlerTestSuite struct {
23+
suite.Suite
24+
logger logger.Logger
25+
starter *ResourceStarter
26+
scaler *resourcescalerMock.ResourceScaler
27+
httpServer *httptest.Server
28+
backendHost string
29+
backendPort int
30+
}
31+
32+
type ingressValue struct {
33+
host string
34+
path string
35+
targets []string
36+
}
37+
38+
func (suite *HandlerTestSuite) SetupSuite() {
39+
var err error
40+
suite.logger, err = nucliozap.NewNuclioZapTest("test")
41+
suite.Require().NoError(err)
42+
}
43+
44+
func (suite *HandlerTestSuite) SetupTest() {
45+
suite.scaler = &resourcescalerMock.ResourceScaler{}
46+
suite.starter = &ResourceStarter{
47+
logger: suite.logger,
48+
scaler: suite.scaler,
49+
resourceReadinessTimeout: 3 * time.Second,
50+
}
51+
allowedPaths := map[string]struct{}{
52+
// TODO - To fix this test for a valid path (i.e.- '/test/path'), the path suffix needs to be removed from h.parseTargetURL
53+
"//test/path/test/path": {},
54+
"//test/path/to/multiple/test/path/to/multiple": {},
55+
}
56+
// Start a test server that always returns 200
57+
suite.httpServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
58+
if _, exists := allowedPaths[r.URL.Path]; exists {
59+
w.WriteHeader(http.StatusOK)
60+
} else {
61+
w.WriteHeader(http.StatusBadRequest)
62+
}
63+
}))
64+
65+
backendURL, _ := url.Parse(suite.httpServer.URL)
66+
suite.backendHost = backendURL.Hostname()
67+
backendPort := backendURL.Port()
68+
if backendPort == "" {
69+
backendPort = "8080" // Default HTTP port
70+
}
71+
backendPortInt, err := strconv.Atoi(backendPort)
72+
suite.Require().NoError(err)
73+
suite.backendPort = backendPortInt
74+
}
75+
76+
func (suite *HandlerTestSuite) TearDownTest() {
77+
if suite.httpServer != nil {
78+
suite.httpServer.Close()
79+
}
80+
}
81+
82+
func (suite *HandlerTestSuite) TestHandleRequest() {
83+
for _, tc := range []struct {
84+
name string
85+
resolveServiceNameErr error
86+
initialCachedData *ingressValue
87+
reqHeaders map[string]string
88+
reqHost string
89+
reqPath string
90+
expectedStatus int
91+
}{
92+
{
93+
name: "No ingress headers, host and path found in ingress cache",
94+
resolveServiceNameErr: nil,
95+
initialCachedData: &ingressValue{
96+
host: "www.example.com",
97+
path: "/test/path",
98+
targets: []string{"test-targets-name-1"},
99+
},
100+
reqHost: "www.example.com",
101+
reqPath: "/test/path",
102+
expectedStatus: http.StatusOK,
103+
}, {
104+
name: "No ingress headers,multiple targets found in ingress cache",
105+
resolveServiceNameErr: nil,
106+
initialCachedData: &ingressValue{
107+
host: "www.example.com",
108+
path: "/test/path/to/multiple",
109+
targets: []string{"test-targets-name-1", "test-targets-name-2"},
110+
},
111+
reqHost: "www.example.com",
112+
reqPath: "/test/path/to/multiple",
113+
expectedStatus: http.StatusOK,
114+
},
115+
{
116+
name: "No ingress headers, not found in ingress cache",
117+
resolveServiceNameErr: nil,
118+
initialCachedData: nil,
119+
reqHost: "unknown",
120+
reqPath: "/notfound",
121+
expectedStatus: http.StatusBadRequest,
122+
},
123+
{
124+
name: "No ingress headers, scaler fails",
125+
resolveServiceNameErr: errors.New("fail"),
126+
initialCachedData: &ingressValue{
127+
host: "www.example.com",
128+
path: "/test/path",
129+
targets: []string{"test-targets-name-1"},
130+
},
131+
reqHost: "www.example.com",
132+
reqPath: "/test/path",
133+
expectedStatus: http.StatusInternalServerError,
134+
},
135+
} {
136+
suite.Run(tc.name, func() {
137+
// test case setup
138+
suite.scaler.ExpectedCalls = nil
139+
suite.scaler.On("ResolveServiceName", mock.Anything).Return(suite.backendHost, tc.resolveServiceNameErr)
140+
suite.scaler.On("SetScaleCtx", mock.Anything, mock.Anything, mock.Anything).Return(nil)
141+
testIngressCache := ingresscache.NewIngressCache(suite.logger)
142+
if tc.initialCachedData != nil {
143+
err := testIngressCache.Set(tc.initialCachedData.host, tc.initialCachedData.path, tc.initialCachedData.targets)
144+
suite.Require().NoError(err)
145+
}
146+
147+
testHandler := suite.createTestHandler(suite.backendPort, testIngressCache)
148+
testRequest := suite.createTestHTTPRequest(tc.reqHeaders, tc.reqHost, tc.reqPath)
149+
testResponse := httptest.NewRecorder()
150+
151+
// call the testHandler
152+
testHandler.handleRequest(testResponse, testRequest)
153+
154+
// validate the response
155+
suite.Require().Equal(tc.expectedStatus, testResponse.Code)
156+
suite.scaler.AssertExpectations(suite.T())
157+
})
158+
}
159+
}
160+
161+
func (suite *HandlerTestSuite) TestGetResourceNameAndPath() {
162+
for _, tc := range []struct {
163+
name string
164+
errMsg string
165+
initialCachedData *ingressValue
166+
reqHeaders map[string]string
167+
reqHost string
168+
reqPath string
169+
expectErr bool
170+
expectedPath string
171+
expectedResourceNames []string
172+
}{
173+
{
174+
name: "No ingress headers, host and path found in ingress cache",
175+
initialCachedData: &ingressValue{
176+
host: "www.example.com",
177+
path: "/test/path",
178+
targets: []string{"test-targets-name-1"},
179+
},
180+
reqHost: "www.example.com",
181+
reqPath: "/test/path",
182+
expectedPath: "/test/path",
183+
expectedResourceNames: []string{"test-targets-name-1"},
184+
}, {
185+
name: "Ingress headers, host and path did not found in ingress cache",
186+
reqHost: "www.example.com",
187+
reqPath: "/test/path",
188+
expectedPath: "/test/path",
189+
expectedResourceNames: []string{"test-targets-name-1"},
190+
reqHeaders: map[string]string{
191+
"X-Resource-Name": "test-targets-name-1",
192+
"X-Resource-Path": "/test/path",
193+
},
194+
}, {
195+
name: "Missing both ingress headers and host and path did not found in ingress cache",
196+
reqHost: "www.example.com",
197+
reqPath: "/test/path",
198+
expectErr: true,
199+
errMsg: "No target name header found",
200+
},
201+
} {
202+
suite.Run(tc.name, func() {
203+
// test case setup
204+
testIngressCache := ingresscache.NewIngressCache(suite.logger)
205+
if tc.initialCachedData != nil {
206+
err := testIngressCache.Set(tc.initialCachedData.host, tc.initialCachedData.path, tc.initialCachedData.targets)
207+
suite.Require().NoError(err)
208+
}
209+
210+
testHandler := suite.createTestHandler(suite.backendPort, testIngressCache)
211+
testRequest := suite.createTestHTTPRequest(tc.reqHeaders, tc.reqHost, tc.reqPath)
212+
resultPath, resultResourceNames, err := testHandler.getResourceNameAndPath(testRequest)
213+
214+
// validate the result
215+
if tc.expectErr {
216+
suite.Require().Error(err)
217+
suite.Require().ErrorContains(err, tc.errMsg)
218+
} else {
219+
suite.Require().NoError(err)
220+
suite.Require().Equal(tc.expectedPath, resultPath)
221+
suite.Require().Equal(tc.expectedResourceNames, resultResourceNames)
222+
}
223+
})
224+
}
225+
}
226+
227+
// --- HandlerTestSuite suite methods ---
228+
229+
func (suite *HandlerTestSuite) createTestHandler(targetPort int, cache ingresscache.IngressHostCacheReader) Handler {
230+
handler, err := NewHandler(
231+
suite.logger,
232+
suite.starter,
233+
suite.scaler,
234+
"X-Resource-Name",
235+
"X-Resource-Path",
236+
targetPort,
237+
scalertypes.MultiTargetStrategyPrimary,
238+
cache,
239+
)
240+
suite.Require().NoError(err)
241+
return handler
242+
}
243+
244+
func (suite *HandlerTestSuite) createTestHTTPRequest(
245+
reqHeaders map[string]string,
246+
reqHost string,
247+
reqPath string,
248+
) *http.Request {
249+
req := httptest.NewRequest("GET", "/", nil)
250+
if reqHost != "" {
251+
req.Host = reqHost
252+
}
253+
if reqPath != "" {
254+
req.URL.Path = reqPath
255+
}
256+
for k, v := range reqHeaders {
257+
req.Header.Set(k, v)
258+
}
259+
return req
260+
}
261+
262+
func TestHandlerTestSuite(t *testing.T) {
263+
suite.Run(t, new(HandlerTestSuite))
264+
}

0 commit comments

Comments
 (0)