From 0bc2b58026e54c536d08ea4a2a1b7cd209f10169 Mon Sep 17 00:00:00 2001 From: roundspring2003 Date: Tue, 7 Oct 2025 08:59:15 +0000 Subject: [PATCH 1/6] feat: Add onepunchman POST /echo API --- internal/sbi/api_onepunchman.go | 40 +++++++++++++++++++++++++++++++++ internal/sbi/router.go | 3 +++ 2 files changed, 43 insertions(+) create mode 100644 internal/sbi/api_onepunchman.go diff --git a/internal/sbi/api_onepunchman.go b/internal/sbi/api_onepunchman.go new file mode 100644 index 0000000..cfd1b9e --- /dev/null +++ b/internal/sbi/api_onepunchman.go @@ -0,0 +1,40 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (s *Server) getOnePunchManRoute() []Route { + return []Route{ + { + Name: "I'm bald, and stronger", + Method: http.MethodGet, + Pattern: "/", + APIFunc: func(c *gin.Context) { + c.JSON(http.StatusOK, "I'm bald, and stronger") + }, + // Use + // curl -X GET http://127.0.0.163:8000/onepunchman/ -w "\n" + }, + { + Name: "Echo POST", + Method: http.MethodPost, + Pattern: "/echo", + APIFunc: func(c *gin.Context) { + // 定義一個 map 接收 JSON + var requestData map[string]interface{} + if err := c.ShouldBindJSON(&requestData); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{ + "message": "Received your data!", + "data": requestData, + }) + }, + // curl -X POST http://127.0.0.163:8000/onepunchman/echo -H "Content-Type: application/json" -d '{"name":"Saitama","power":100}' + }, + } +} diff --git a/internal/sbi/router.go b/internal/sbi/router.go index 6631416..2ce7d17 100644 --- a/internal/sbi/router.go +++ b/internal/sbi/router.go @@ -61,6 +61,9 @@ func newRouter(s *Server) *gin.Engine { fortuneGroup := router.Group("/fortune") applyRoutes(fortuneGroup, s.getFortuneRoute()) + onePunchManGroup := router.Group("/onepunchman") + applyRoutes(onePunchManGroup, s.getOnePunchManRoute()) + return router } From eca38349d3d5addc7cb2fa731b998a5e84179021 Mon Sep 17 00:00:00 2001 From: roundspring2003 Date: Wed, 8 Oct 2025 06:03:33 +0000 Subject: [PATCH 2/6] fix: add onepunchman test --- internal/sbi/api_onepunchman.go | 25 ++++----- internal/sbi/api_onepunchman_test.go | 80 ++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 internal/sbi/api_onepunchman_test.go diff --git a/internal/sbi/api_onepunchman.go b/internal/sbi/api_onepunchman.go index cfd1b9e..d095756 100644 --- a/internal/sbi/api_onepunchman.go +++ b/internal/sbi/api_onepunchman.go @@ -22,19 +22,20 @@ func (s *Server) getOnePunchManRoute() []Route { Name: "Echo POST", Method: http.MethodPost, Pattern: "/echo", - APIFunc: func(c *gin.Context) { - // 定義一個 map 接收 JSON - var requestData map[string]interface{} - if err := c.ShouldBindJSON(&requestData); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - c.JSON(http.StatusOK, gin.H{ - "message": "Received your data!", - "data": requestData, - }) - }, + APIFunc: s.HTTPOnePunchManEcho, // curl -X POST http://127.0.0.163:8000/onepunchman/echo -H "Content-Type: application/json" -d '{"name":"Saitama","power":100}' }, } } +func (s *Server) HTTPOnePunchManEcho(c *gin.Context) { + // 定義一個 map 接收 JSON + var requestData map[string]interface{} + if err := c.ShouldBindJSON(&requestData); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{ + "message": "Received your data!", + "data": requestData, + }) +} diff --git a/internal/sbi/api_onepunchman_test.go b/internal/sbi/api_onepunchman_test.go new file mode 100644 index 0000000..206589c --- /dev/null +++ b/internal/sbi/api_onepunchman_test.go @@ -0,0 +1,80 @@ +package sbi_test + +import ( + "bytes" + "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_OnePunchManAPI(t *testing.T) { + gin.SetMode(gin.TestMode) + + // 建立 Mock nfApp + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + nfApp := sbi.NewMocknfApp(mockCtrl) + nfApp.EXPECT().Config().Return(&factory.Config{ + Configuration: &factory.Configuration{ + Sbi: &factory.Sbi{Port: 8000}, + }, + }).AnyTimes() + + // 建立整個伺服器 + server := sbi.NewServer(nfApp, "") + + // ----------------------- + // 測試 POST /onepunchman/echo + // ----------------------- + t.Run("POST /onepunchman/echo", func(t *testing.T) { + const EXPECTED_STATUS = http.StatusOK + const EXPECTED_BODY = `{"data":{"name":"Saitama","power":100},"message":"Received your data!"}` + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + body := bytes.NewBuffer([]byte(`{"name":"Saitama","power":100}`)) + + var err error + ginCtx.Request, err = http.NewRequest("POST", "/onepunchman/echo", body) + if err != nil { + t.Errorf("Failed to create request: %s", err) + return + } + server.HTTPOnePunchManEcho(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()) + } + }) + + // ----------------------- + // 測試 POST /onepunchman/echo 傳錯格式 + // ----------------------- + t.Run("POST /onepunchman/echo invalid json", func(t *testing.T) { + const EXPECTED_STATUS = http.StatusBadRequest + + httpRecorder := httptest.NewRecorder() + ginCtx, _ := gin.CreateTestContext(httpRecorder) + + body := bytes.NewBuffer([]byte(`invalid_json`)) + var err error + ginCtx.Request, err = http.NewRequest("POST", "/onepunchman/echo", body) + if err != nil { + t.Errorf("Failed to create request: %s", err) + return + } + server.HTTPOnePunchManEcho(ginCtx) + + if httpRecorder.Code != EXPECTED_STATUS { + t.Errorf("Expected status code %d, got %d", EXPECTED_STATUS, httpRecorder.Code) + } + }) +} From f0cba2ec0d136703cc3e92981ad39d48705aebe1 Mon Sep 17 00:00:00 2001 From: roundspring2003 Date: Wed, 15 Oct 2025 07:41:23 +0000 Subject: [PATCH 3/6] fix: fix errors --- internal/sbi/api_onepunchman.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/sbi/api_onepunchman.go b/internal/sbi/api_onepunchman.go index d095756..41dedcf 100644 --- a/internal/sbi/api_onepunchman.go +++ b/internal/sbi/api_onepunchman.go @@ -23,7 +23,9 @@ func (s *Server) getOnePunchManRoute() []Route { Method: http.MethodPost, Pattern: "/echo", APIFunc: s.HTTPOnePunchManEcho, - // curl -X POST http://127.0.0.163:8000/onepunchman/echo -H "Content-Type: application/json" -d '{"name":"Saitama","power":100}' + // curl -X POST http://127.0.0.163:8000/onepunchman/echo \ + // -H "Content-Type: application/json" \ + // -d '{"name":"Saitama","power":100}' }, } } From adc5d5620407926afce498c355a4a75ec093152b Mon Sep 17 00:00:00 2001 From: roundspring2003 Date: Wed, 15 Oct 2025 08:00:41 +0000 Subject: [PATCH 4/6] chore(commit): fix previous commit messages to follow conventional commits From 8625ba333d8eb2379021f39ac3ead7e33b4dd2b7 Mon Sep 17 00:00:00 2001 From: roundspring2003 Date: Mon, 27 Oct 2025 12:34:51 +0000 Subject: [PATCH 5/6] fix: stasify the rules --- internal/sbi/api_onepunchman.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/sbi/api_onepunchman.go b/internal/sbi/api_onepunchman.go index 41dedcf..65cf7c1 100644 --- a/internal/sbi/api_onepunchman.go +++ b/internal/sbi/api_onepunchman.go @@ -29,6 +29,7 @@ func (s *Server) getOnePunchManRoute() []Route { }, } } + func (s *Server) HTTPOnePunchManEcho(c *gin.Context) { // 定義一個 map 接收 JSON var requestData map[string]interface{} From d9fe3fa85b37637f18dfaf181301e5282b3d0b5c Mon Sep 17 00:00:00 2001 From: roundspring2003 Date: Mon, 27 Oct 2025 13:16:59 +0000 Subject: [PATCH 6/6] fix: fix linter error --- internal/sbi/router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/sbi/router.go b/internal/sbi/router.go index 5c89ea7..2584eaf 100644 --- a/internal/sbi/router.go +++ b/internal/sbi/router.go @@ -68,7 +68,7 @@ func newRouter(s *Server) *gin.Engine { onePunchManGroup := router.Group("/onepunchman") applyRoutes(onePunchManGroup, s.getOnePunchManRoute()) - timeZoneGroup := router.Group("/timezone") + timeZoneGroup := router.Group("/timezone") applyRoutes(timeZoneGroup, s.getTimeZoneRoute()) return router