diff --git a/internal/sbi/api_onepunchman.go b/internal/sbi/api_onepunchman.go new file mode 100644 index 0000000..65cf7c1 --- /dev/null +++ b/internal/sbi/api_onepunchman.go @@ -0,0 +1,44 @@ +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: 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) + } + }) +} diff --git a/internal/sbi/router.go b/internal/sbi/router.go index 49ee563..2584eaf 100644 --- a/internal/sbi/router.go +++ b/internal/sbi/router.go @@ -65,6 +65,9 @@ func newRouter(s *Server) *gin.Engine { fortuneGroup := router.Group("/fortune") applyRoutes(fortuneGroup, s.getFortuneRoute()) + onePunchManGroup := router.Group("/onepunchman") + applyRoutes(onePunchManGroup, s.getOnePunchManRoute()) + timeZoneGroup := router.Group("/timezone") applyRoutes(timeZoneGroup, s.getTimeZoneRoute())