Code refactor #66
| @ -29,11 +29,6 @@ var ( | |||||||
| 	translationDir = path.Join(scriptDir, "locale") | 	translationDir = path.Join(scriptDir, "locale") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type FSData struct { |  | ||||||
| 	Path string |  | ||||||
| 	St   *state.State |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // FlagManager handles centralized flag management
 | // FlagManager handles centralized flag management
 | ||||||
| type FlagManager struct { | type FlagManager struct { | ||||||
| 	parser *asm.FlagParser | 	parser *asm.FlagParser | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ import ( | |||||||
| 	"git.defalsify.org/vise.git/db" | 	"git.defalsify.org/vise.git/db" | ||||||
| 	"git.defalsify.org/vise.git/resource" | 	"git.defalsify.org/vise.git/resource" | ||||||
| 	"git.defalsify.org/vise.git/state" | 	"git.defalsify.org/vise.git/state" | ||||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/ussd/mocks" | 	"git.grassecon.net/urdt/ussd/internal/mocks" | ||||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | 	"git.grassecon.net/urdt/ussd/internal/models" | ||||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||||
| 	"github.com/alecthomas/assert/v2" | 	"github.com/alecthomas/assert/v2" | ||||||
|  | |||||||
| @ -32,6 +32,7 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) | 		logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) | ||||||
| 		ash.writeError(w, 400, err) | 		ash.writeError(w, 400, err) | ||||||
|  | 		return | ||||||
| 	} | 	} | ||||||
| 	rqs.Config = cfg | 	rqs.Config = cfg | ||||||
| 	rqs.Input, err = rp.GetInput(req) | 	rqs.Input, err = rp.GetInput(req) | ||||||
| @ -41,16 +42,14 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	rqs, err = ash.Process(rqs) | 	rqs, err = ash.Process(rqs)  | ||||||
| 	switch err { | 	switch err { | ||||||
| 	case handlers.ErrStorage: | 	case nil: // set code to 200 if no err
 | ||||||
| 		code = 500 | 		code = 200 | ||||||
| 	case handlers.ErrEngineInit: | 	case handlers.ErrStorage, handlers.ErrEngineInit, handlers.ErrEngineExec, handlers.ErrEngineType: | ||||||
| 		code = 500 |  | ||||||
| 	case handlers.ErrEngineExec: |  | ||||||
| 		code = 500 | 		code = 500 | ||||||
| 	default: | 	default: | ||||||
| 		code = 200 | 		code = 500 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if code != 200 { | 	if code != 200 { | ||||||
|  | |||||||
							
								
								
									
										449
									
								
								internal/http/http_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										449
									
								
								internal/http/http_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,449 @@ | |||||||
|  | package http | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"io" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"git.defalsify.org/vise.git/engine" | ||||||
|  | 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||||
|  | 	"git.grassecon.net/urdt/ussd/internal/mocks/httpmocks" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // invalidRequestType is a custom type to test invalid request scenarios
 | ||||||
|  | type invalidRequestType struct{} | ||||||
|  | 
 | ||||||
|  | // errorReader is a helper type that always returns an error when Read is called
 | ||||||
|  | type errorReader struct{} | ||||||
|  | 
 | ||||||
|  | func (e *errorReader) Read(p []byte) (n int, err error) { | ||||||
|  | 	return 0, errors.New("read error") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewATSessionHandler(t *testing.T) { | ||||||
|  | 	mockHandler := &httpmocks.MockRequestHandler{} | ||||||
|  | 	ash := NewATSessionHandler(mockHandler) | ||||||
|  | 
 | ||||||
|  | 	if ash == nil { | ||||||
|  | 		t.Fatal("NewATSessionHandler returned nil") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if ash.SessionHandler == nil { | ||||||
|  | 		t.Fatal("SessionHandler is nil") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestATSessionHandler_ServeHTTP(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name           string | ||||||
|  | 		setupMocks     func(*httpmocks.MockRequestHandler, *httpmocks.MockRequestParser, *httpmocks.MockEngine) | ||||||
|  | 		formData       url.Values | ||||||
|  | 		expectedStatus int | ||||||
|  | 		expectedBody   string | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "Successful request", | ||||||
|  | 			setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) { | ||||||
|  | 				mrp.GetSessionIdFunc = func(rq any) (string, error) { | ||||||
|  | 					req := rq.(*http.Request) | ||||||
|  | 					return req.FormValue("phoneNumber"), nil | ||||||
|  | 				} | ||||||
|  | 				mrp.GetInputFunc = func(rq any) ([]byte, error) { | ||||||
|  | 					req := rq.(*http.Request) | ||||||
|  | 					text := req.FormValue("text") | ||||||
|  | 					parts := strings.Split(text, "*") | ||||||
|  | 					return []byte(parts[len(parts)-1]), nil | ||||||
|  | 				} | ||||||
|  | 				mh.ProcessFunc = func(rqs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 					rqs.Continue = true | ||||||
|  | 					rqs.Engine = me | ||||||
|  | 					return rqs, nil | ||||||
|  | 				} | ||||||
|  | 				mh.GetConfigFunc = func() engine.Config { return engine.Config{} } | ||||||
|  | 				mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp } | ||||||
|  | 				mh.OutputFunc = func(rs handlers.RequestSession) (handlers.RequestSession, error) { return rs, nil } | ||||||
|  | 				mh.ResetFunc = func(rs handlers.RequestSession) (handlers.RequestSession, error) { return rs, nil } | ||||||
|  | 				me.WriteResultFunc = func(context.Context, io.Writer) (int, error) { return 0, nil } | ||||||
|  | 			}, | ||||||
|  | 			formData: url.Values{ | ||||||
|  | 				"phoneNumber": []string{"+1234567890"}, | ||||||
|  | 				"text":        []string{"1*2*3"}, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: http.StatusOK, | ||||||
|  | 			expectedBody:   "CON ", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "GetSessionId error", | ||||||
|  | 			setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) { | ||||||
|  | 				mrp.GetSessionIdFunc = func(rq any) (string, error) { | ||||||
|  | 					return "", errors.New("no phone number found") | ||||||
|  | 				} | ||||||
|  | 				mh.GetConfigFunc = func() engine.Config { return engine.Config{} } | ||||||
|  | 				mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp } | ||||||
|  | 			}, | ||||||
|  | 			formData: url.Values{ | ||||||
|  | 				"text": []string{"1*2*3"}, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: http.StatusBadRequest, | ||||||
|  | 			expectedBody:   "", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "GetInput error", | ||||||
|  | 			setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) { | ||||||
|  | 				mrp.GetSessionIdFunc = func(rq any) (string, error) { | ||||||
|  | 					req := rq.(*http.Request) | ||||||
|  | 					return req.FormValue("phoneNumber"), nil | ||||||
|  | 				} | ||||||
|  | 				mrp.GetInputFunc = func(rq any) ([]byte, error) { | ||||||
|  | 					return nil, errors.New("no input found") | ||||||
|  | 				} | ||||||
|  | 				mh.GetConfigFunc = func() engine.Config { return engine.Config{} } | ||||||
|  | 				mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp } | ||||||
|  | 			}, | ||||||
|  | 			formData: url.Values{ | ||||||
|  | 				"phoneNumber": []string{"+1234567890"}, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: http.StatusBadRequest, | ||||||
|  | 			expectedBody:   "", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "Process error", | ||||||
|  | 			setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) { | ||||||
|  | 				mrp.GetSessionIdFunc = func(rq any) (string, error) { | ||||||
|  | 					req := rq.(*http.Request) | ||||||
|  | 					return req.FormValue("phoneNumber"), nil | ||||||
|  | 				} | ||||||
|  | 				mrp.GetInputFunc = func(rq any) ([]byte, error) { | ||||||
|  | 					req := rq.(*http.Request) | ||||||
|  | 					text := req.FormValue("text") | ||||||
|  | 					parts := strings.Split(text, "*") | ||||||
|  | 					return []byte(parts[len(parts)-1]), nil | ||||||
|  | 				} | ||||||
|  | 				mh.ProcessFunc = func(rqs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 					return rqs, handlers.ErrStorage | ||||||
|  | 				} | ||||||
|  | 				mh.GetConfigFunc = func() engine.Config { return engine.Config{} } | ||||||
|  | 				mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp } | ||||||
|  | 			}, | ||||||
|  | 			formData: url.Values{ | ||||||
|  | 				"phoneNumber": []string{"+1234567890"}, | ||||||
|  | 				"text":        []string{"1*2*3"}, | ||||||
|  | 			}, | ||||||
|  | 			expectedStatus: http.StatusInternalServerError, | ||||||
|  | 			expectedBody:   "", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  | 			mockHandler := &httpmocks.MockRequestHandler{} | ||||||
|  | 			mockRequestParser := &httpmocks.MockRequestParser{} | ||||||
|  | 			mockEngine := &httpmocks.MockEngine{} | ||||||
|  | 			tt.setupMocks(mockHandler, mockRequestParser, mockEngine) | ||||||
|  | 
 | ||||||
|  | 			ash := NewATSessionHandler(mockHandler) | ||||||
|  | 
 | ||||||
|  | 			req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tt.formData.Encode())) | ||||||
|  | 			req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||||||
|  | 			w := httptest.NewRecorder() | ||||||
|  | 
 | ||||||
|  | 			ash.ServeHTTP(w, req) | ||||||
|  | 
 | ||||||
|  | 			if w.Code != tt.expectedStatus { | ||||||
|  | 				t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if tt.expectedBody != "" && w.Body.String() != tt.expectedBody { | ||||||
|  | 				t.Errorf("Expected body %q, got %q", tt.expectedBody, w.Body.String()) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestATSessionHandler_Output(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name           string | ||||||
|  | 		input          handlers.RequestSession | ||||||
|  | 		expectedPrefix string | ||||||
|  | 		expectedError  bool | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "Continue true", | ||||||
|  | 			input: handlers.RequestSession{ | ||||||
|  | 				Continue: true, | ||||||
|  | 				Engine: &httpmocks.MockEngine{ | ||||||
|  | 					WriteResultFunc: func(context.Context, io.Writer) (int, error) { | ||||||
|  | 						return 0, nil | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Writer: &httpmocks.MockWriter{}, | ||||||
|  | 			}, | ||||||
|  | 			expectedPrefix: "CON ", | ||||||
|  | 			expectedError:  false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "Continue false", | ||||||
|  | 			input: handlers.RequestSession{ | ||||||
|  | 				Continue: false, | ||||||
|  | 				Engine: &httpmocks.MockEngine{ | ||||||
|  | 					WriteResultFunc: func(context.Context, io.Writer) (int, error) { | ||||||
|  | 						return 0, nil | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Writer: &httpmocks.MockWriter{}, | ||||||
|  | 			}, | ||||||
|  | 			expectedPrefix: "END ", | ||||||
|  | 			expectedError:  false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "WriteResult error", | ||||||
|  | 			input: handlers.RequestSession{ | ||||||
|  | 				Continue: true, | ||||||
|  | 				Engine: &httpmocks.MockEngine{ | ||||||
|  | 					WriteResultFunc: func(context.Context, io.Writer) (int, error) { | ||||||
|  | 						return 0, errors.New("write error") | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				Writer: &httpmocks.MockWriter{}, | ||||||
|  | 			}, | ||||||
|  | 			expectedPrefix: "CON ", | ||||||
|  | 			expectedError:  true, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  | 			ash := &ATSessionHandler{} | ||||||
|  | 			_, err := ash.Output(tt.input) | ||||||
|  | 
 | ||||||
|  | 			if tt.expectedError && err == nil { | ||||||
|  | 				t.Error("Expected an error, but got nil") | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if !tt.expectedError && err != nil { | ||||||
|  | 				t.Errorf("Unexpected error: %v", err) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			mw := tt.input.Writer.(*httpmocks.MockWriter) | ||||||
|  | 			if !mw.WriteStringCalled { | ||||||
|  | 				t.Error("WriteString was not called") | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if mw.WrittenString != tt.expectedPrefix { | ||||||
|  | 				t.Errorf("Expected prefix %q, got %q", tt.expectedPrefix, mw.WrittenString) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestSessionHandler_ServeHTTP(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name           string | ||||||
|  | 		sessionID      string | ||||||
|  | 		input          []byte | ||||||
|  | 		parserErr      error | ||||||
|  | 		processErr     error | ||||||
|  | 		outputErr      error | ||||||
|  | 		resetErr       error | ||||||
|  | 		expectedStatus int | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name:           "Success", | ||||||
|  | 			sessionID:      "123", | ||||||
|  | 			input:          []byte("test input"), | ||||||
|  | 			expectedStatus: http.StatusOK, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:           "Missing Session ID", | ||||||
|  | 			sessionID:      "", | ||||||
|  | 			parserErr:      handlers.ErrSessionMissing, | ||||||
|  | 			expectedStatus: http.StatusBadRequest, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:           "Process Error", | ||||||
|  | 			sessionID:      "123", | ||||||
|  | 			input:          []byte("test input"), | ||||||
|  | 			processErr:     handlers.ErrStorage, | ||||||
|  | 			expectedStatus: http.StatusInternalServerError, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:           "Output Error", | ||||||
|  | 			sessionID:      "123", | ||||||
|  | 			input:          []byte("test input"), | ||||||
|  | 			outputErr:      errors.New("output error"), | ||||||
|  | 			expectedStatus: http.StatusOK, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:           "Reset Error", | ||||||
|  | 			sessionID:      "123", | ||||||
|  | 			input:          []byte("test input"), | ||||||
|  | 			resetErr:       errors.New("reset error"), | ||||||
|  | 			expectedStatus: http.StatusOK, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  | 			mockRequestParser := &httpmocks.MockRequestParser{ | ||||||
|  | 				GetSessionIdFunc: func(any) (string, error) { | ||||||
|  | 					return tt.sessionID, tt.parserErr | ||||||
|  | 				}, | ||||||
|  | 				GetInputFunc: func(any) ([]byte, error) { | ||||||
|  | 					return tt.input, nil | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			mockRequestHandler := &httpmocks.MockRequestHandler{ | ||||||
|  | 				ProcessFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 					return rs, tt.processErr | ||||||
|  | 				}, | ||||||
|  | 				OutputFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 					return rs, tt.outputErr | ||||||
|  | 				}, | ||||||
|  | 				ResetFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 					return rs, tt.resetErr | ||||||
|  | 				}, | ||||||
|  | 				GetRequestParserFunc: func() handlers.RequestParser { | ||||||
|  | 					return mockRequestParser | ||||||
|  | 				}, | ||||||
|  | 				GetConfigFunc: func() engine.Config { | ||||||
|  | 					return engine.Config{} | ||||||
|  | 				}, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			sessionHandler := ToSessionHandler(mockRequestHandler) | ||||||
|  | 
 | ||||||
|  | 			req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(tt.input)) | ||||||
|  | 			req.Header.Set("X-Vise-Session", tt.sessionID) | ||||||
|  | 
 | ||||||
|  | 			rr := httptest.NewRecorder() | ||||||
|  | 
 | ||||||
|  | 			sessionHandler.ServeHTTP(rr, req) | ||||||
|  | 
 | ||||||
|  | 			if status := rr.Code; status != tt.expectedStatus { | ||||||
|  | 				t.Errorf("handler returned wrong status code: got %v want %v", | ||||||
|  | 					status, tt.expectedStatus) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestSessionHandler_writeError(t *testing.T) { | ||||||
|  | 	handler := &SessionHandler{} | ||||||
|  | 	mockWriter := &httpmocks.MockWriter{} | ||||||
|  | 	err := errors.New("test error") | ||||||
|  | 
 | ||||||
|  | 	handler.writeError(mockWriter, http.StatusBadRequest, err) | ||||||
|  | 
 | ||||||
|  | 	if mockWriter.WrittenString != "" { | ||||||
|  | 		t.Errorf("Expected empty body, got %s", mockWriter.WrittenString) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestDefaultRequestParser_GetSessionId(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name          string | ||||||
|  | 		request       any | ||||||
|  | 		expectedID    string | ||||||
|  | 		expectedError error | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "Valid Session ID", | ||||||
|  | 			request: func() *http.Request { | ||||||
|  | 				req := httptest.NewRequest(http.MethodPost, "/", nil) | ||||||
|  | 				req.Header.Set("X-Vise-Session", "123456") | ||||||
|  | 				return req | ||||||
|  | 			}(), | ||||||
|  | 			expectedID:    "123456", | ||||||
|  | 			expectedError: nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:          "Missing Session ID", | ||||||
|  | 			request:       httptest.NewRequest(http.MethodPost, "/", nil), | ||||||
|  | 			expectedID:    "", | ||||||
|  | 			expectedError: handlers.ErrSessionMissing, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:          "Invalid Request Type", | ||||||
|  | 			request:       invalidRequestType{}, | ||||||
|  | 			expectedID:    "", | ||||||
|  | 			expectedError: handlers.ErrInvalidRequest, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	parser := &DefaultRequestParser{} | ||||||
|  | 
 | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  | 			id, err := parser.GetSessionId(tt.request) | ||||||
|  | 
 | ||||||
|  | 			if id != tt.expectedID { | ||||||
|  | 				t.Errorf("Expected session ID %s, got %s", tt.expectedID, id) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if err != tt.expectedError { | ||||||
|  | 				t.Errorf("Expected error %v, got %v", tt.expectedError, err) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestDefaultRequestParser_GetInput(t *testing.T) { | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name          string | ||||||
|  | 		request       any | ||||||
|  | 		expectedInput []byte | ||||||
|  | 		expectedError error | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "Valid Input", | ||||||
|  | 			request: func() *http.Request { | ||||||
|  | 				return httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString("test input")) | ||||||
|  | 			}(), | ||||||
|  | 			expectedInput: []byte("test input"), | ||||||
|  | 			expectedError: nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:          "Empty Input", | ||||||
|  | 			request:       httptest.NewRequest(http.MethodPost, "/", nil), | ||||||
|  | 			expectedInput: []byte{}, | ||||||
|  | 			expectedError: nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:          "Invalid Request Type", | ||||||
|  | 			request:       invalidRequestType{}, | ||||||
|  | 			expectedInput: nil, | ||||||
|  | 			expectedError: handlers.ErrInvalidRequest, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "Read Error", | ||||||
|  | 			request: func() *http.Request { | ||||||
|  | 				return httptest.NewRequest(http.MethodPost, "/", &errorReader{}) | ||||||
|  | 			}(), | ||||||
|  | 			expectedInput: nil, | ||||||
|  | 			expectedError: errors.New("read error"), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	parser := &DefaultRequestParser{} | ||||||
|  | 
 | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  | 			input, err := parser.GetInput(tt.request) | ||||||
|  | 
 | ||||||
|  | 			if !bytes.Equal(input, tt.expectedInput) { | ||||||
|  | 				t.Errorf("Expected input %s, got %s", tt.expectedInput, input) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if err != tt.expectedError && (err == nil || err.Error() != tt.expectedError.Error()) { | ||||||
|  | 				t.Errorf("Expected error %v, got %v", tt.expectedError, err) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								internal/mocks/httpmocks/enginemock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/mocks/httpmocks/enginemock.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | package httpmocks | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"io" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // MockEngine implements the engine.Engine interface for testing
 | ||||||
|  | type MockEngine struct { | ||||||
|  | 	InitFunc        func(context.Context) (bool, error) | ||||||
|  | 	ExecFunc        func(context.Context, []byte) (bool, error) | ||||||
|  | 	WriteResultFunc func(context.Context, io.Writer) (int, error) | ||||||
|  | 	FinishFunc      func() error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockEngine) Init(ctx context.Context) (bool, error) { | ||||||
|  | 	return m.InitFunc(ctx) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockEngine) Exec(ctx context.Context, input []byte) (bool, error) { | ||||||
|  | 	return m.ExecFunc(ctx, input) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockEngine) WriteResult(ctx context.Context, w io.Writer) (int, error) { | ||||||
|  | 	return m.WriteResultFunc(ctx, w) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockEngine) Finish() error { | ||||||
|  | 	return m.FinishFunc() | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								internal/mocks/httpmocks/requesthandlermock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								internal/mocks/httpmocks/requesthandlermock.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | package httpmocks | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"git.defalsify.org/vise.git/engine" | ||||||
|  | 	"git.defalsify.org/vise.git/persist" | ||||||
|  | 	"git.defalsify.org/vise.git/resource" | ||||||
|  | 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // MockRequestHandler implements handlers.RequestHandler interface for testing
 | ||||||
|  | type MockRequestHandler struct { | ||||||
|  | 	ProcessFunc          func(handlers.RequestSession) (handlers.RequestSession, error) | ||||||
|  | 	GetConfigFunc        func() engine.Config | ||||||
|  | 	GetEngineFunc        func(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine | ||||||
|  | 	OutputFunc           func(rs handlers.RequestSession) (handlers.RequestSession, error) | ||||||
|  | 	ResetFunc            func(rs handlers.RequestSession) (handlers.RequestSession, error) | ||||||
|  | 	ShutdownFunc         func() | ||||||
|  | 	GetRequestParserFunc func() handlers.RequestParser | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestHandler) Process(rqs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 	return m.ProcessFunc(rqs) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestHandler) GetConfig() engine.Config { | ||||||
|  | 	return m.GetConfigFunc() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestHandler) GetEngine(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine { | ||||||
|  | 	return m.GetEngineFunc(cfg, rs, pe) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestHandler) Output(rs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 	return m.OutputFunc(rs) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestHandler) Reset(rs handlers.RequestSession) (handlers.RequestSession, error) { | ||||||
|  | 	return m.ResetFunc(rs) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestHandler) Shutdown() { | ||||||
|  | 	m.ShutdownFunc() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestHandler) GetRequestParser() handlers.RequestParser { | ||||||
|  | 	return m.GetRequestParserFunc() | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								internal/mocks/httpmocks/requestparsermock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								internal/mocks/httpmocks/requestparsermock.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | package httpmocks | ||||||
|  | 
 | ||||||
|  | // MockRequestParser implements the handlers.RequestParser interface for testing
 | ||||||
|  | type MockRequestParser struct { | ||||||
|  | 	GetSessionIdFunc func(any) (string, error) | ||||||
|  | 	GetInputFunc     func(any) ([]byte, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestParser) GetSessionId(rq any) (string, error) { | ||||||
|  | 	return m.GetSessionIdFunc(rq) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockRequestParser) GetInput(rq any) ([]byte, error) { | ||||||
|  | 	return m.GetInputFunc(rq) | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								internal/mocks/httpmocks/writermock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								internal/mocks/httpmocks/writermock.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | package httpmocks | ||||||
|  | 
 | ||||||
|  | import "net/http" | ||||||
|  | 
 | ||||||
|  | // MockWriter implements a mock io.Writer for testing
 | ||||||
|  | type MockWriter struct { | ||||||
|  | 	WriteStringCalled bool | ||||||
|  | 	WrittenString     string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockWriter) Write(p []byte) (n int, err error) { | ||||||
|  | 	return len(p), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockWriter) WriteString(s string) (n int, err error) { | ||||||
|  | 	m.WriteStringCalled = true | ||||||
|  | 	m.WrittenString = s | ||||||
|  | 	return len(s), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockWriter) Header() http.Header { | ||||||
|  | 	return http.Header{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *MockWriter) WriteHeader(statusCode int) {} | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user