From 2e9a330c7c05991b1f6a23af9e41f1cbe8ff4176 Mon Sep 17 00:00:00 2001 From: Feng Tu Date: Sat, 20 Sep 2025 18:20:05 +0800 Subject: [PATCH 1/8] fix: lint config --- .golangci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 2f97f11..2a050fb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -53,7 +53,14 @@ linters: goconst: min-len: 5 min-occurrences: 3 - ignore-strings-values: get|post|put|delete|patch|options|head + ignore-string-values: + - get + - post + - put + - delete + - patch + - options + - head gocritic: disabled-checks: - regexpMust From 214af24d19c11001ee6bbef7d524c18371979c60 Mon Sep 17 00:00:00 2001 From: solar224 Date: Fri, 19 Sep 2025 21:03:51 +0800 Subject: [PATCH 2/8] fix: resolve race condition in task API using mutex and atomic counter --- go.mod | 4 ++ go.sum | 2 + internal/context/context.go | 14 +++- internal/sbi/api_task.go | 32 +++++++++ internal/sbi/processor/task_handler.go | 35 +++++++++ internal/sbi/processor/task_handler_test.go | 78 +++++++++++++++++++++ internal/sbi/router.go | 3 + 7 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 internal/sbi/api_task.go create mode 100644 internal/sbi/processor/task_handler.go create mode 100644 internal/sbi/processor/task_handler_test.go diff --git a/go.mod b/go.mod index b773f7f..af04145 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,14 @@ module github.com/Alonza0314/nf-example go 1.24 require ( + github.com/andy89923/nf-example v0.0.0-20240927075836-30d774dac5e6 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/free5gc/openapi v1.2.0 github.com/free5gc/util v1.1.1 github.com/gin-gonic/gin v1.10.0 github.com/google/uuid v1.6.0 github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.9.0 github.com/urfave/cli v1.22.15 go.uber.org/mock v0.4.0 gopkg.in/yaml.v2 v2.4.0 @@ -20,6 +22,7 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -35,6 +38,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index 2695ffb..7507502 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/andy89923/nf-example v0.0.0-20240927075836-30d774dac5e6 h1:ZycD/1pQBcFvDQtkuK1pRG17814RHCbDiqpKXxKkK9c= +github.com/andy89923/nf-example v0.0.0-20240927075836-30d774dac5e6/go.mod h1:SKI4gLqkQtc6nENl6bM6ZHdeqDJDllVWkOErueva88M= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= diff --git a/internal/context/context.go b/internal/context/context.go index 2bfe8b2..a500c02 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -2,14 +2,19 @@ package context import ( "os" + "sync" "github.com/Alonza0314/nf-example/internal/logger" "github.com/Alonza0314/nf-example/pkg/factory" - "github.com/google/uuid" - "github.com/free5gc/openapi/models" + "github.com/google/uuid" ) +type Task struct { + ID int `json:"id"` + Name string `json:"name"` +} + type NFContext struct { NfId string Name string @@ -18,6 +23,9 @@ type NFContext struct { SBIPort int SpyFamilyData map[string]string + Tasks []Task + TaskMutex sync.RWMutex + NextTaskID uint64 } var nfContext = NFContext{} @@ -57,6 +65,8 @@ func InitNfContext() { "Henry": "Henderson", "Martha": "Marriott", } + nfContext.Tasks = make([]Task, 0) + nfContext.NextTaskID = 0 } func GetSelf() *NFContext { diff --git a/internal/sbi/api_task.go b/internal/sbi/api_task.go new file mode 100644 index 0000000..8733e31 --- /dev/null +++ b/internal/sbi/api_task.go @@ -0,0 +1,32 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (s *Server) HTTPCreateNewTask(c *gin.Context) { + s.Processor().CreateNewTask(c) +} + +func (s *Server) HTTPGetAllTasks(c *gin.Context) { + s.Processor().GetAllTasks(c) +} + +func (s *Server) getTaskRoute() []Route { + return []Route{ + { + Name: "Get All Tasks", + Method: http.MethodGet, + Pattern: "/tasks", + APIFunc: s.HTTPGetAllTasks, + }, + { + Name: "Create New Task", + Method: http.MethodPost, + Pattern: "/tasks", + APIFunc: s.HTTPCreateNewTask, + }, + } +} diff --git a/internal/sbi/processor/task_handler.go b/internal/sbi/processor/task_handler.go new file mode 100644 index 0000000..19fa9bc --- /dev/null +++ b/internal/sbi/processor/task_handler.go @@ -0,0 +1,35 @@ +package processor + +import ( + "net/http" + "sync/atomic" + + "github.com/Alonza0314/nf-example/internal/context" + "github.com/gin-gonic/gin" +) + +func (p *Processor) CreateNewTask(c *gin.Context) { + var newTask context.Task + if err := c.ShouldBindJSON(&newTask); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) + return + } + + // next ID + newID := atomic.AddUint64(&p.Context().NextTaskID, 1) + newTask.ID = int(newID) + + p.Context().TaskMutex.Lock() + p.Context().Tasks = append(p.Context().Tasks, newTask) + p.Context().TaskMutex.Unlock() + + c.JSON(http.StatusCreated, newTask) +} + +func (p *Processor) GetAllTasks(c *gin.Context) { + p.Context().TaskMutex.RLock() + tasksCopy := make([]context.Task, len(p.Context().Tasks)) + copy(tasksCopy, p.Context().Tasks) + p.Context().TaskMutex.RUnlock() + c.JSON(http.StatusOK, tasksCopy) +} diff --git a/internal/sbi/processor/task_handler_test.go b/internal/sbi/processor/task_handler_test.go new file mode 100644 index 0000000..bd8c8f0 --- /dev/null +++ b/internal/sbi/processor/task_handler_test.go @@ -0,0 +1,78 @@ +package processor_test + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "sync" + "testing" + + "github.com/Alonza0314/nf-example/internal/context" + "github.com/Alonza0314/nf-example/internal/sbi/processor" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func Test_TaskHandlers(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockNfApp := processor.NewMockProcessorNf(mockCtrl) + + nfContext := &context.NFContext{ + Tasks: make([]context.Task, 0), + TaskMutex: sync.RWMutex{}, + NextTaskID: 0, + } + mockNfApp.EXPECT().Context().Return(nfContext).AnyTimes() + + proc, err := processor.NewProcessor(mockNfApp) + assert.NoError(t, err) + + t.Run("Create Task", func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + + taskData := map[string]string{"name": "Test Task"} + + body, err := json.Marshal(taskData) + assert.NoError(t, err) + + ginCtx.Request, err = http.NewRequest(http.MethodPost, "/task/tasks", bytes.NewReader(body)) + assert.NoError(t, err) + + ginCtx.Request.Header.Set("Content-Type", "application/json") + + proc.CreateNewTask(ginCtx) + + assert.Equal(t, http.StatusCreated, httpRecorder.Code) + + var createdTask context.Task + err = json.Unmarshal(httpRecorder.Body.Bytes(), &createdTask) + assert.NoError(t, err) + assert.Equal(t, "Test Task", createdTask.Name) + assert.Equal(t, 1, createdTask.ID) + }) + + t.Run("Get All Tasks", func(t *testing.T) { + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + + ginCtx.Request, err = http.NewRequest(http.MethodGet, "/task/tasks", nil) + assert.NoError(t, err) + + proc.GetAllTasks(ginCtx) + + assert.Equal(t, http.StatusOK, httpRecorder.Code) + + var tasks []context.Task + err = json.Unmarshal(httpRecorder.Body.Bytes(), &tasks) + assert.NoError(t, err) + assert.Len(t, tasks, 1) + assert.Equal(t, "Test Task", tasks[0].Name) + }) +} diff --git a/internal/sbi/router.go b/internal/sbi/router.go index b62139f..39d9508 100644 --- a/internal/sbi/router.go +++ b/internal/sbi/router.go @@ -45,6 +45,9 @@ func newRouter(s *Server) *gin.Engine { spyFamilyGroup := router.Group("/spyfamily") applyRoutes(spyFamilyGroup, s.getSpyFamilyRoute()) + taskGroup := router.Group("/task") + applyRoutes(taskGroup, s.getTaskRoute()) + return router } From 851e5df6a44d67e7061fb6415400d5bd0f9cecea Mon Sep 17 00:00:00 2001 From: solar224 Date: Sat, 20 Sep 2025 16:13:01 +0800 Subject: [PATCH 3/8] fix: correct go.mod dependency and resolve lint errors --- go.mod | 1 - go.sum | 2 -- internal/context/context.go | 3 ++- internal/sbi/processor/task_handler_test.go | 4 ++-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index af04145..ff87899 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/Alonza0314/nf-example go 1.24 require ( - github.com/andy89923/nf-example v0.0.0-20240927075836-30d774dac5e6 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/free5gc/openapi v1.2.0 github.com/free5gc/util v1.1.1 diff --git a/go.sum b/go.sum index 7507502..2695ffb 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,4 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/andy89923/nf-example v0.0.0-20240927075836-30d774dac5e6 h1:ZycD/1pQBcFvDQtkuK1pRG17814RHCbDiqpKXxKkK9c= -github.com/andy89923/nf-example v0.0.0-20240927075836-30d774dac5e6/go.mod h1:SKI4gLqkQtc6nENl6bM6ZHdeqDJDllVWkOErueva88M= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= diff --git a/internal/context/context.go b/internal/context/context.go index a500c02..a57d1e6 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -6,8 +6,9 @@ import ( "github.com/Alonza0314/nf-example/internal/logger" "github.com/Alonza0314/nf-example/pkg/factory" - "github.com/free5gc/openapi/models" "github.com/google/uuid" + + "github.com/free5gc/openapi/models" ) type Task struct { diff --git a/internal/sbi/processor/task_handler_test.go b/internal/sbi/processor/task_handler_test.go index bd8c8f0..93af29b 100644 --- a/internal/sbi/processor/task_handler_test.go +++ b/internal/sbi/processor/task_handler_test.go @@ -38,8 +38,8 @@ func Test_TaskHandlers(t *testing.T) { ginCtx, _ := gin.CreateTestContext(httpRecorder) taskData := map[string]string{"name": "Test Task"} - - body, err := json.Marshal(taskData) + var body []byte + body, err = json.Marshal(taskData) assert.NoError(t, err) ginCtx.Request, err = http.NewRequest(http.MethodPost, "/task/tasks", bytes.NewReader(body)) From b3fb2504a1014823d53f64d156cca74ace6dd4d3 Mon Sep 17 00:00:00 2001 From: chchen7 Date: Sat, 20 Sep 2025 02:03:27 +0800 Subject: [PATCH 4/8] feat : add message api service --- internal/context/context.go | 4 +++ internal/sbi/api_message.go | 58 +++++++++++++++++++++++++++++++ internal/sbi/processor/message.go | 32 +++++++++++++++++ internal/sbi/router.go | 6 ++++ 4 files changed, 100 insertions(+) create mode 100644 internal/sbi/api_message.go create mode 100644 internal/sbi/processor/message.go diff --git a/internal/context/context.go b/internal/context/context.go index 2bfe8b2..8ac9ea5 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -2,6 +2,7 @@ package context import ( "os" + "sync" "github.com/Alonza0314/nf-example/internal/logger" "github.com/Alonza0314/nf-example/pkg/factory" @@ -18,6 +19,8 @@ type NFContext struct { SBIPort int SpyFamilyData map[string]string + MessageRecord []string + MessageMu sync.Mutex } var nfContext = NFContext{} @@ -57,6 +60,7 @@ func InitNfContext() { "Henry": "Henderson", "Martha": "Marriott", } + nfContext.MessageRecord = []string{} } func GetSelf() *NFContext { diff --git a/internal/sbi/api_message.go b/internal/sbi/api_message.go new file mode 100644 index 0000000..f5765e9 --- /dev/null +++ b/internal/sbi/api_message.go @@ -0,0 +1,58 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (s *Server) getMessageRoute() []Route { + return []Route{ + { + Name: "get messages", + Method: http.MethodGet, + Pattern: "/", + APIFunc: s.getMessageRecord, + // Use + // curl -X GET http://127.0.0.163:8000/message/ -w "\n" + }, + } +} + +func (s *Server) putMessageRoute() []Route { + return []Route{ + { + Name: "add message", + Method: http.MethodPut, + Pattern: "/:Message", + APIFunc: s.addNewMessage, + // Use + // curl -X PUT http://127.0.0.163:8000/message/yourmessage -w "\n" + // add "yourmessage" to message record + }, + { + // empty input handle, will not accept + Name: "empty input", + Method: http.MethodPut, + Pattern: "/", + APIFunc: s.noMessageHandler, + }, + } +} + +func (s *Server) addNewMessage(c *gin.Context) { + + newMessage := c.Param("Message") + if newMessage == "" { + s.noMessageHandler(c) + return + } + s.Processor().AddNewMessage(c, newMessage) +} +func (s *Server) noMessageHandler(c *gin.Context) { + c.String(http.StatusBadRequest, "No message provided") +} + +func (s *Server) getMessageRecord(c *gin.Context) { + s.Processor().GetMessageRecord(c) +} diff --git a/internal/sbi/processor/message.go b/internal/sbi/processor/message.go new file mode 100644 index 0000000..3d025a0 --- /dev/null +++ b/internal/sbi/processor/message.go @@ -0,0 +1,32 @@ +package processor + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +func (p *Processor) AddNewMessage(c *gin.Context, newMessage string) { + p.Context().MessageMu.Lock() + defer p.Context().MessageMu.Unlock() + //addd message + p.Context().MessageRecord = append(p.Context().MessageRecord, newMessage) + c.String(http.StatusOK, "add a new message!") +} +func (p *Processor) GetMessageRecord(c *gin.Context) { + p.Context().MessageMu.Lock() + defer p.Context().MessageMu.Unlock() + + //no content + if len(p.Context().MessageRecord) == 0 { + c.String(http.StatusOK, "no message now, add some messagess!") + return + } + //get record + Record := "" + for _, s := range p.Context().MessageRecord { + Record += fmt.Sprintf("%s\n", s) + } + c.String(http.StatusOK, Record) +} diff --git a/internal/sbi/router.go b/internal/sbi/router.go index b62139f..9763a40 100644 --- a/internal/sbi/router.go +++ b/internal/sbi/router.go @@ -42,6 +42,12 @@ func newRouter(s *Server) *gin.Engine { defaultGroup := router.Group("/default") applyRoutes(defaultGroup, s.getDefaultRoute()) + addMessageGroup := router.Group("/message") + applyRoutes(addMessageGroup, s.putMessageRoute()) + + getMessageGroup := router.Group("/message") + applyRoutes(getMessageGroup, s.getMessageRoute()) + spyFamilyGroup := router.Group("/spyfamily") applyRoutes(spyFamilyGroup, s.getSpyFamilyRoute()) From ea2a053d5b705f151985377cd4b691fc85385dcf Mon Sep 17 00:00:00 2001 From: chchen7 Date: Sat, 20 Sep 2025 15:51:31 +0800 Subject: [PATCH 5/8] fix: intergrate PUT and GET in a same group --- internal/sbi/api_message.go | 8 ++------ internal/sbi/router.go | 7 ++----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/internal/sbi/api_message.go b/internal/sbi/api_message.go index f5765e9..befd1aa 100644 --- a/internal/sbi/api_message.go +++ b/internal/sbi/api_message.go @@ -6,7 +6,7 @@ import ( "github.com/gin-gonic/gin" ) -func (s *Server) getMessageRoute() []Route { +func (s *Server) myPutGetMessageRoute() []Route { return []Route{ { Name: "get messages", @@ -15,12 +15,8 @@ func (s *Server) getMessageRoute() []Route { APIFunc: s.getMessageRecord, // Use // curl -X GET http://127.0.0.163:8000/message/ -w "\n" + // return all added message }, - } -} - -func (s *Server) putMessageRoute() []Route { - return []Route{ { Name: "add message", Method: http.MethodPut, diff --git a/internal/sbi/router.go b/internal/sbi/router.go index 9763a40..07b422e 100644 --- a/internal/sbi/router.go +++ b/internal/sbi/router.go @@ -42,11 +42,8 @@ func newRouter(s *Server) *gin.Engine { defaultGroup := router.Group("/default") applyRoutes(defaultGroup, s.getDefaultRoute()) - addMessageGroup := router.Group("/message") - applyRoutes(addMessageGroup, s.putMessageRoute()) - - getMessageGroup := router.Group("/message") - applyRoutes(getMessageGroup, s.getMessageRoute()) + myPutGetMessageGroup := router.Group("/message") + applyRoutes(myPutGetMessageGroup, s.myPutGetMessageRoute()) spyFamilyGroup := router.Group("/spyfamily") applyRoutes(spyFamilyGroup, s.getSpyFamilyRoute()) From 5a7139a7782af9dc68208b40f9c0aea8ed88a461 Mon Sep 17 00:00:00 2001 From: chchen7 Date: Sat, 20 Sep 2025 17:54:19 +0800 Subject: [PATCH 6/8] feat: add unit test for message api --- internal/sbi/api_message.go | 8 +- internal/sbi/api_message_test.go | 52 ++++++++++++ internal/sbi/processor/message_test.go | 112 +++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 internal/sbi/api_message_test.go create mode 100644 internal/sbi/processor/message_test.go diff --git a/internal/sbi/api_message.go b/internal/sbi/api_message.go index befd1aa..2d05eba 100644 --- a/internal/sbi/api_message.go +++ b/internal/sbi/api_message.go @@ -12,7 +12,7 @@ func (s *Server) myPutGetMessageRoute() []Route { Name: "get messages", Method: http.MethodGet, Pattern: "/", - APIFunc: s.getMessageRecord, + APIFunc: s.HTTPGetMessageRecord, // Use // curl -X GET http://127.0.0.163:8000/message/ -w "\n" // return all added message @@ -21,7 +21,7 @@ func (s *Server) myPutGetMessageRoute() []Route { Name: "add message", Method: http.MethodPut, Pattern: "/:Message", - APIFunc: s.addNewMessage, + APIFunc: s.HTTPAddNewMessage, // Use // curl -X PUT http://127.0.0.163:8000/message/yourmessage -w "\n" // add "yourmessage" to message record @@ -36,7 +36,7 @@ func (s *Server) myPutGetMessageRoute() []Route { } } -func (s *Server) addNewMessage(c *gin.Context) { +func (s *Server) HTTPAddNewMessage(c *gin.Context) { newMessage := c.Param("Message") if newMessage == "" { @@ -49,6 +49,6 @@ func (s *Server) noMessageHandler(c *gin.Context) { c.String(http.StatusBadRequest, "No message provided") } -func (s *Server) getMessageRecord(c *gin.Context) { +func (s *Server) HTTPGetMessageRecord(c *gin.Context) { s.Processor().GetMessageRecord(c) } diff --git a/internal/sbi/api_message_test.go b/internal/sbi/api_message_test.go new file mode 100644 index 0000000..cf6adaa --- /dev/null +++ b/internal/sbi/api_message_test.go @@ -0,0 +1,52 @@ +package sbi_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/Alonza0314/nf-example/internal/sbi" + "github.com/Alonza0314/nf-example/pkg/factory" + "github.com/gin-gonic/gin" + "go.uber.org/mock/gomock" +) + +func Test_PUTWithEmptyInput(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + nfApp := sbi.NewMocknfApp(mockCtrl) + nfApp.EXPECT().Config().Return(&factory.Config{ + Configuration: &factory.Configuration{ + Sbi: &factory.Sbi{ + Port: 8000, + }, + }, + }).AnyTimes() + server := sbi.NewServer(nfApp, "") + + t.Run("Add Message That Empty", func(t *testing.T) { + const EXPECTED_STATUS = 400 + const EXPECTED_BODY = "No message provided" + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + + var err error + ginCtx.Request, err = http.NewRequest("PUT", "/message/", nil) + if err != nil { + t.Errorf("Failed to create request: %s", err) + return + } + + server.HTTPAddNewMessage(ginCtx) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + + if httpRecorder.Body.String() != EXPECTED_BODY { + t.Errorf("Expected body %s, got %s", EXPECTED_BODY, httpRecorder.Body.String()) + } + }) +} diff --git a/internal/sbi/processor/message_test.go b/internal/sbi/processor/message_test.go new file mode 100644 index 0000000..9af5c12 --- /dev/null +++ b/internal/sbi/processor/message_test.go @@ -0,0 +1,112 @@ +package processor_test + +import ( + "net/http/httptest" + "testing" + + nf_context "github.com/Alonza0314/nf-example/internal/context" + "github.com/Alonza0314/nf-example/internal/sbi/processor" + "github.com/gin-gonic/gin" + gomock "go.uber.org/mock/gomock" +) + +func Test_AddNewMessage(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + processorNf := processor.NewMockProcessorNf(mockCtrl) + processor, err := processor.NewProcessor(processorNf) + if err != nil { + t.Errorf("Failed to create processor: %s", err) + return + } + + t.Run("Add Message That Not Empty", func(t *testing.T) { + const INPUT_MESSAGE = "ABC" + const EXPECTED_STATUS = 200 + const EXPECTED_BODY = "add a new message!" + + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + MessageRecord: []string{}, + }).AnyTimes() + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.AddNewMessage(ginCtx, INPUT_MESSAGE) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + + if httpRecorder.Body.String() != EXPECTED_BODY { + t.Errorf("Expected body %s, got %s", EXPECTED_BODY, httpRecorder.Body.String()) + } + }) +} + +func Test_GetMessageNotEmpty(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + processorNf := processor.NewMockProcessorNf(mockCtrl) + processor, err := processor.NewProcessor(processorNf) + if err != nil { + t.Errorf("Failed to create processor: %s", err) + return + } + + t.Run("Get Message That Not Empty", func(t *testing.T) { + const EXPECTED_STATUS = 200 + const EXPECTED_BODY = "ABC\n123\n" + + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + MessageRecord: []string{ + "ABC", + "123", + }, + }).AnyTimes() + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.GetMessageRecord(ginCtx) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + + if httpRecorder.Body.String() != EXPECTED_BODY { + t.Errorf("Expected body %s, got %s", EXPECTED_BODY, httpRecorder.Body.String()) + } + }) +} +func Test_GetMessageEmpty(t *testing.T) { + gin.SetMode(gin.TestMode) + + mockCtrl := gomock.NewController(t) + processorNf := processor.NewMockProcessorNf(mockCtrl) + processor, err := processor.NewProcessor(processorNf) + if err != nil { + t.Errorf("Failed to create processor: %s", err) + return + } + t.Run("Get Message That Empty", func(t *testing.T) { + const EXPECTED_STATUS = 200 + const EXPECTED_BODY = "no message now, add some messagess!" + + processorNf.EXPECT().Context().Return(&nf_context.NFContext{ + MessageRecord: []string{}, + }).AnyTimes() + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + processor.GetMessageRecord(ginCtx) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + + if httpRecorder.Body.String() != EXPECTED_BODY { + t.Errorf("Expected body %s, got %s", EXPECTED_BODY, httpRecorder.Body.String()) + } + }) +} From 142cb0a9266e0e864beeafe7d145bea73fe69401 Mon Sep 17 00:00:00 2001 From: chchen7 Date: Sat, 20 Sep 2025 22:14:05 +0800 Subject: [PATCH 7/8] fix: some typo that fail CI test --- internal/sbi/api_message.go | 1 - internal/sbi/processor/message.go | 6 +++--- internal/sbi/processor/message_test.go | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/internal/sbi/api_message.go b/internal/sbi/api_message.go index 2d05eba..a395182 100644 --- a/internal/sbi/api_message.go +++ b/internal/sbi/api_message.go @@ -37,7 +37,6 @@ func (s *Server) myPutGetMessageRoute() []Route { } func (s *Server) HTTPAddNewMessage(c *gin.Context) { - newMessage := c.Param("Message") if newMessage == "" { s.noMessageHandler(c) diff --git a/internal/sbi/processor/message.go b/internal/sbi/processor/message.go index 3d025a0..0a76175 100644 --- a/internal/sbi/processor/message.go +++ b/internal/sbi/processor/message.go @@ -10,7 +10,7 @@ import ( func (p *Processor) AddNewMessage(c *gin.Context, newMessage string) { p.Context().MessageMu.Lock() defer p.Context().MessageMu.Unlock() - //addd message + // add message p.Context().MessageRecord = append(p.Context().MessageRecord, newMessage) c.String(http.StatusOK, "add a new message!") } @@ -18,12 +18,12 @@ func (p *Processor) GetMessageRecord(c *gin.Context) { p.Context().MessageMu.Lock() defer p.Context().MessageMu.Unlock() - //no content + // no content if len(p.Context().MessageRecord) == 0 { c.String(http.StatusOK, "no message now, add some messagess!") return } - //get record + // get record Record := "" for _, s := range p.Context().MessageRecord { Record += fmt.Sprintf("%s\n", s) diff --git a/internal/sbi/processor/message_test.go b/internal/sbi/processor/message_test.go index 9af5c12..255669a 100644 --- a/internal/sbi/processor/message_test.go +++ b/internal/sbi/processor/message_test.go @@ -33,7 +33,7 @@ func Test_AddNewMessage(t *testing.T) { httpRecorder := httptest.NewRecorder() ginCtx, _ := gin.CreateTestContext(httpRecorder) processor.AddNewMessage(ginCtx, INPUT_MESSAGE) - + if httpRecorder.Code != EXPECTED_STATUS { t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) } @@ -69,7 +69,7 @@ func Test_GetMessageNotEmpty(t *testing.T) { httpRecorder := httptest.NewRecorder() ginCtx, _ := gin.CreateTestContext(httpRecorder) processor.GetMessageRecord(ginCtx) - + if httpRecorder.Code != EXPECTED_STATUS { t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) } @@ -100,7 +100,7 @@ func Test_GetMessageEmpty(t *testing.T) { httpRecorder := httptest.NewRecorder() ginCtx, _ := gin.CreateTestContext(httpRecorder) processor.GetMessageRecord(ginCtx) - + if httpRecorder.Code != EXPECTED_STATUS { t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) } From b566e5e981efd29914288afbece78f9919ac59e7 Mon Sep 17 00:00:00 2001 From: chchen7 Date: Sat, 20 Sep 2025 23:39:14 +0800 Subject: [PATCH 8/8] fix: typo that cause CI fail --- internal/context/context.go | 8 +++----- internal/sbi/api_message.go | 1 + internal/sbi/processor/message.go | 1 + internal/sbi/processor/message_test.go | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/context/context.go b/internal/context/context.go index 6a3770d..0f5999b 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -28,10 +28,9 @@ type NFContext struct { MessageRecord []string MessageMu sync.Mutex - Tasks []Task - TaskMutex sync.RWMutex - NextTaskID uint64 - + Tasks []Task + TaskMutex sync.RWMutex + NextTaskID uint64 } var nfContext = NFContext{} @@ -76,7 +75,6 @@ func InitNfContext() { nfContext.Tasks = make([]Task, 0) nfContext.NextTaskID = 0 - } func GetSelf() *NFContext { diff --git a/internal/sbi/api_message.go b/internal/sbi/api_message.go index a395182..02ba3e9 100644 --- a/internal/sbi/api_message.go +++ b/internal/sbi/api_message.go @@ -44,6 +44,7 @@ func (s *Server) HTTPAddNewMessage(c *gin.Context) { } s.Processor().AddNewMessage(c, newMessage) } + func (s *Server) noMessageHandler(c *gin.Context) { c.String(http.StatusBadRequest, "No message provided") } diff --git a/internal/sbi/processor/message.go b/internal/sbi/processor/message.go index 0a76175..b2154ff 100644 --- a/internal/sbi/processor/message.go +++ b/internal/sbi/processor/message.go @@ -14,6 +14,7 @@ func (p *Processor) AddNewMessage(c *gin.Context, newMessage string) { p.Context().MessageRecord = append(p.Context().MessageRecord, newMessage) c.String(http.StatusOK, "add a new message!") } + func (p *Processor) GetMessageRecord(c *gin.Context) { p.Context().MessageMu.Lock() defer p.Context().MessageMu.Unlock() diff --git a/internal/sbi/processor/message_test.go b/internal/sbi/processor/message_test.go index 255669a..f970891 100644 --- a/internal/sbi/processor/message_test.go +++ b/internal/sbi/processor/message_test.go @@ -79,6 +79,7 @@ func Test_GetMessageNotEmpty(t *testing.T) { } }) } + func Test_GetMessageEmpty(t *testing.T) { gin.SetMode(gin.TestMode)