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
3 changes: 3 additions & 0 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type NFContext struct {
Fortunes []string
FortuneMutex sync.RWMutex

AttendanceData []string

TimeZoneData map[string]string
}

Expand Down Expand Up @@ -85,6 +87,7 @@ func InitNfContext() {
"Henry": "Henderson",
"Martha": "Marriott",
}
nfContext.AttendanceData = []string{}

nfContext.MessageRecord = []string{}

Expand Down
51 changes: 51 additions & 0 deletions internal/sbi/api_attendance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package sbi

import (
"io"
"net/http"

"github.com/Alonza0314/nf-example/internal/logger"
"github.com/gin-gonic/gin"
)

func (s *Server) getAttendanceRoute() []Route {
return []Route{
{
Name: "Get Attendance",
Method: http.MethodGet,
Pattern: "/",
APIFunc: s.GetAttendance,
},
// curl -X GET http://127.0.0.163:8000/attendance/ -w "\n"
{
Name: "Post Attendance",
Method: http.MethodPost,
Pattern: "/",
APIFunc: s.PostAttendance,
},
// curl -X POST http://127.0.0.163:8000/attendance/ -d 'John' -w "\n"
}
}

func (s *Server) GetAttendance(c *gin.Context) {
logger.SBILog.Infof("In HTTPGetAttandence")

s.Processor().ReturnAttendance(c)
}

func (s *Server) PostAttendance(c *gin.Context) {
logger.SBILog.Infof("In HTTPPostAttendance")

body, err := io.ReadAll(c.Request.Body)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "failed to read body"})
return
}

targetName := string(body)
if targetName == "" {
c.String(http.StatusBadRequest, "error: no name provided")
return
}
s.Processor().PostAttendance(c, targetName)
}
49 changes: 49 additions & 0 deletions internal/sbi/api_attendance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package sbi_test

import (
"net/http"
"net/http/httptest"
"strings"
"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_Attendance(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("No attendance name provided", func(t *testing.T) {
const EXPECTED_STATUS = http.StatusBadRequest
const EXPECTED_BODY = "error: no name provided"
httpRecorder := httptest.NewRecorder()
ginCtx, _ := gin.CreateTestContext(httpRecorder)
var err error
ginCtx.Request, err = http.NewRequest("POST", "/attendance", strings.NewReader(""))
if err != nil {
t.Errorf("Failed to create request: %s", err)
return
}

server.PostAttendance(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())
}
})
}
38 changes: 38 additions & 0 deletions internal/sbi/processor/attendance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package processor

import (
"net/http"

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

func (p *Processor) ReturnAttendance(c *gin.Context) {
con := p.Context()

if len(con.AttendanceData) == 0 {
c.String(http.StatusOK, "No attendance recorded")
return
} else {
names := ""
for _, name := range con.AttendanceData {
names += name + ", "
}
c.String(http.StatusOK, "Attendance: "+names[:len(names)-2])
return
}
}

func (p *Processor) PostAttendance(c *gin.Context, targetName string) {
con := p.Context()

for n := range con.AttendanceData {
if con.AttendanceData[n] == targetName {
c.String(http.StatusConflict, "Attendance already recorded: "+targetName)
return
}
}

con.AttendanceData = append(con.AttendanceData, targetName)

c.String(http.StatusOK, "Attendance recorded: "+targetName)
}
112 changes: 112 additions & 0 deletions internal/sbi/processor/attendance_test.go
Original file line number Diff line number Diff line change
@@ -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_ReturnAttendance(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("No Attendance Recorded", func(t *testing.T) {
const EXPECTED_STATUS = 200
const EXPECTED_BODY = "No attendance recorded"
processorNf.EXPECT().Context().Return(&nf_context.NFContext{
AttendanceData: []string{},
})

httpRecorder := httptest.NewRecorder()
ginCtx, _ := gin.CreateTestContext(httpRecorder)
processor.ReturnAttendance(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())
}
})

t.Run("Some Attendance Recorded", func(t *testing.T) {
const EXPECTED_STATUS = 200
const EXPECTED_BODY = "Attendance: Alice, Bob, Charlie"
processorNf.EXPECT().Context().Return(&nf_context.NFContext{
AttendanceData: []string{"Alice", "Bob", "Charlie"},
})
httpRecorder := httptest.NewRecorder()
ginCtx, _ := gin.CreateTestContext(httpRecorder)
processor.ReturnAttendance(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_PostAttandence(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("Post New Attendance", func(t *testing.T) {
const INPUT_NAME = "David"
const EXPECTED_STATUS = 200
const EXPECTED_BODY = "Attendance recorded: " + INPUT_NAME
processorNf.EXPECT().Context().Return(&nf_context.NFContext{
AttendanceData: []string{"Alice", "Bob", "Charlie"},
})
httpRecorder := httptest.NewRecorder()
ginCtx, _ := gin.CreateTestContext(httpRecorder)
processor.PostAttendance(ginCtx, INPUT_NAME)

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())
}
})

t.Run("Post Duplicate Attendance", func(t *testing.T) {
const INPUT_NAME = "Alice"
const EXPECTED_STATUS = 409
const EXPECTED_BODY = "Attendance already recorded: " + INPUT_NAME
processorNf.EXPECT().Context().Return(&nf_context.NFContext{
AttendanceData: []string{"Alice", "Bob", "Charlie"},
})
httpRecorder := httptest.NewRecorder()
ginCtx, _ := gin.CreateTestContext(httpRecorder)
processor.PostAttendance(ginCtx, INPUT_NAME)

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())
}
})
}
2 changes: 2 additions & 0 deletions internal/sbi/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func newRouter(s *Server) *gin.Engine {
spyFamilyGroup := router.Group("/spyfamily")
applyRoutes(spyFamilyGroup, s.getSpyFamilyRoute())

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

Expand Down
Loading