Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions internal/sbi/api_foodpicker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package sbi

import (
"math/rand"
"net/http"
"sync"

"github.com/gin-gonic/gin"
)

var (
foodList = []string{"Beef Noodle", "Burger", "Bento", "McDonalds", "Luway"}
mu sync.Mutex // To ensure thread-safe operations on the food list
)

func (s *Server) getFoodPickerRoutes() []Route {
return []Route{
{
Name: "FoodPicker GET",
Method: http.MethodGet,
Pattern: "",
APIFunc: func(c *gin.Context) {
mu.Lock()
defer mu.Unlock()

randomFood := foodList[rand.Intn(len(foodList))]
c.JSON(http.StatusOK, gin.H{"lunch/dinner pick": randomFood})
},
// curl -X GET http://127.0.0.163:8000/foodpicker -w "\n"
},
{
Name: "FoodPicker POST",
Method: http.MethodPost,
Pattern: "",
APIFunc: func(c *gin.Context) {
var newFood struct {
Name string `json:"name"`
}
if err := c.ShouldBindJSON(&newFood); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

mu.Lock()
defer mu.Unlock()

foodList = append(foodList, newFood.Name)
c.JSON(http.StatusOK, gin.H{"message": "Food added successfully", "foodList": foodList})
},
// curl -X POST http://127.0.0.163:8000/foodpicker \
// -H "Content-Type: application/json" \
// -d '{"name":"McDonalds"}' -w "\n"
},
}
}

// foodpicker.go
func (s *Server) GetFoodPickerRoutes() []Route {
return s.getFoodPickerRoutes()
}
121 changes: 121 additions & 0 deletions internal/sbi/api_foodpicker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package sbi_test

import (
"bytes"
"encoding/json"
"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 setupFoodPickerServer(t *testing.T) *sbi.Server {
t.Helper()
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()

return sbi.NewServer(nfApp, "")
}

func Test_FoodPickerGET(t *testing.T) {
server := setupFoodPickerServer(t)

httpRecorder := httptest.NewRecorder()
ginCtx, _ := gin.CreateTestContext(httpRecorder)

req, err := http.NewRequest(http.MethodGet, "/foodpicker", nil)
if err != nil {
t.Fatalf("Failed to create GET request: %v", err)
}
ginCtx.Request = req

// Call the GET handler directly
for _, route := range server.GetFoodPickerRoutes() {
if route.Method == http.MethodGet {
route.APIFunc(ginCtx)
}
}

if httpRecorder.Code != http.StatusOK {
t.Errorf("Expected status 200 OK, got %d", httpRecorder.Code)
}

var body map[string]string
if err = json.Unmarshal(httpRecorder.Body.Bytes(), &body); err != nil {
t.Errorf("Failed to parse response JSON: %v", err)
}

if _, ok := body["lunch/dinner pick"]; !ok {
t.Errorf("Expected key 'lunch/dinner pick' in response body")
}
}

func Test_FoodPickerPOST(t *testing.T) {
server := setupFoodPickerServer(t)

httpRecorder := httptest.NewRecorder()
ginCtx, _ := gin.CreateTestContext(httpRecorder)

payload := map[string]string{"name": "Sushi"}
jsonData, err := json.Marshal(payload)
if err != nil {
t.Fatalf("Failed to marshal payload: %v", err)
}

req, err := http.NewRequest(http.MethodPost, "/foodpicker", bytes.NewBuffer(jsonData))
if err != nil {
t.Fatalf("Failed to create POST request: %v", err)
}
req.Header.Set("Content-Type", "application/json")
ginCtx.Request = req

// Call the POST handler directly
for _, route := range server.GetFoodPickerRoutes() {
if route.Method == http.MethodPost {
route.APIFunc(ginCtx)
}
}

if httpRecorder.Code != http.StatusOK {
t.Errorf("Expected status 200 OK, got %d", httpRecorder.Code)
}

var resp map[string]interface{}
if err = json.Unmarshal(httpRecorder.Body.Bytes(), &resp); err != nil {
t.Errorf("Failed to parse response JSON: %v", err)
}

if resp["message"] != "Food added successfully" {
t.Errorf("Expected success message, got %v", resp["message"])
}

// Optional: Check if "Sushi" is present in returned list
list, ok := resp["foodList"].([]interface{})
if !ok {
t.Errorf("Expected foodList array in response")
} else {
found := false
for _, f := range list {
if f == "Sushi" {
found = true
break
}
}
if !found {
t.Errorf("Expected 'Sushi' to be in foodList")
}
}
}
3 changes: 3 additions & 0 deletions internal/sbi/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func newRouter(s *Server) *gin.Engine {
spyFamilyGroup := router.Group("/spyfamily")
applyRoutes(spyFamilyGroup, s.getSpyFamilyRoute())

foodPickerGroup := router.Group("/foodpicker") // lab6
applyRoutes(foodPickerGroup, s.getFoodPickerRoutes())

taskGroup := router.Group("/task")
applyRoutes(taskGroup, s.getTaskRoute())

Expand Down
Loading