Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update main #21

Merged
merged 10 commits into from
Jul 21, 2024
2 changes: 1 addition & 1 deletion database/db.connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func InitDatabase(conf *config.DbConfig, isDebug bool) (db *gorm.DB, err error)
return nil, err
}

err = db.AutoMigrate(&model.Group{}, &model.User{}, &model.Selection{}, &model.Stamp{}, &model.CheckIn{}, &model.Count{})
err = db.AutoMigrate(&model.Group{}, &model.User{}, &model.Selection{}, &model.Stamp{}, &model.CheckIn{}, &model.Count{}, &model.Answer{})
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ go 1.22.4
require (
github.com/golang/mock v1.6.0
github.com/google/uuid v1.6.0
github.com/isd-sgcu/rpkm67-go-proto v0.5.3
github.com/isd-sgcu/rpkm67-model v0.2.0
github.com/isd-sgcu/rpkm67-go-proto v0.5.4
github.com/isd-sgcu/rpkm67-model v0.2.1
github.com/joho/godotenv v1.5.1
github.com/redis/go-redis/v9 v9.5.3
github.com/stretchr/testify v1.9.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ github.com/isd-sgcu/rpkm67-go-proto v0.5.2 h1:CP9oXIa4MrJZd6ynHkVt18YPGlQU0bkDM1
github.com/isd-sgcu/rpkm67-go-proto v0.5.2/go.mod h1:w+UCeQnJ3wBuJ7Tyf8LiBiPZVb1KlecjMNCB7kBeL7M=
github.com/isd-sgcu/rpkm67-go-proto v0.5.3 h1:DMxo3vu5OB2RaODWQwIIFRTyPEyTNMvwmfDbVmVnmnM=
github.com/isd-sgcu/rpkm67-go-proto v0.5.3/go.mod h1:w+UCeQnJ3wBuJ7Tyf8LiBiPZVb1KlecjMNCB7kBeL7M=
github.com/isd-sgcu/rpkm67-go-proto v0.5.4 h1:XcbTKhQFGHiFf10kxsoK8oyZ2v1b2uQ2gmOmzI5sEYE=
github.com/isd-sgcu/rpkm67-go-proto v0.5.4/go.mod h1:w+UCeQnJ3wBuJ7Tyf8LiBiPZVb1KlecjMNCB7kBeL7M=
github.com/isd-sgcu/rpkm67-model v0.0.6 h1:pYlqOmeXGQIfHdOhyAta4kXkqnoLc4X3KWcAjPrAuds=
github.com/isd-sgcu/rpkm67-model v0.0.6/go.mod h1:dxgLSkrFpbQOXsrzqgepZoEOyZUIG2LBGtm5gsuBbVc=
github.com/isd-sgcu/rpkm67-model v0.0.7 h1:3b8gf1Ocg+Ky4xocKtCqVCB3rFDg90IgEXRwNmHt0OE=
Expand All @@ -35,6 +37,8 @@ github.com/isd-sgcu/rpkm67-model v0.1.0 h1:ML4C8cU7L8m53QuAiIkrykzQP9VYlsOWGrQO5
github.com/isd-sgcu/rpkm67-model v0.1.0/go.mod h1:dxgLSkrFpbQOXsrzqgepZoEOyZUIG2LBGtm5gsuBbVc=
github.com/isd-sgcu/rpkm67-model v0.2.0 h1:D2KytmevtV9/3FwfA7FiKo2UKa3jC8knZI97vwRuboA=
github.com/isd-sgcu/rpkm67-model v0.2.0/go.mod h1:dxgLSkrFpbQOXsrzqgepZoEOyZUIG2LBGtm5gsuBbVc=
github.com/isd-sgcu/rpkm67-model v0.2.1 h1:O6mZeZqDjGbiEJa5zzbf6cVwz4uVOtQTuAxnkLj+2oQ=
github.com/isd-sgcu/rpkm67-model v0.2.1/go.mod h1:dxgLSkrFpbQOXsrzqgepZoEOyZUIG2LBGtm5gsuBbVc=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
Expand Down
318 changes: 318 additions & 0 deletions internal/selection/test/selection.service_test.go
Original file line number Diff line number Diff line change
@@ -1 +1,319 @@
package test

import (
"context"
"testing"

"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/isd-sgcu/rpkm67-backend/config"
service "github.com/isd-sgcu/rpkm67-backend/internal/selection"
mock_cache "github.com/isd-sgcu/rpkm67-backend/mocks/cache"
mock_group "github.com/isd-sgcu/rpkm67-backend/mocks/group"
mock_selection "github.com/isd-sgcu/rpkm67-backend/mocks/selection"
proto "github.com/isd-sgcu/rpkm67-go-proto/rpkm67/backend/selection/v1"
"github.com/isd-sgcu/rpkm67-model/model"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

type SelectionServiceTestSuite struct {
suite.Suite
ctrl *gomock.Controller
mockRepo *mock_selection.MockRepository
mockCache *mock_cache.MockRepository
mockGroupRepo *mock_group.MockRepository
service proto.SelectionServiceServer
ctx context.Context
logger *zap.Logger
config *config.SelectionConfig
}

func TestSelectionServiceTestSuite(t *testing.T) {
suite.Run(t, new(SelectionServiceTestSuite))
}

func (s *SelectionServiceTestSuite) SetupTest() {
s.ctrl = gomock.NewController(s.T())
s.mockRepo = mock_selection.NewMockRepository(s.ctrl)
s.mockCache = mock_cache.NewMockRepository(s.ctrl)
s.logger = zap.NewNop()
s.config = &config.SelectionConfig{CacheTTL: 3600}
s.mockGroupRepo = mock_group.NewMockRepository(s.ctrl)
s.service = service.NewService(s.mockRepo, s.mockGroupRepo, s.mockCache, s.config, s.logger)
s.ctx = context.Background()
}

func (s *SelectionServiceTestSuite) TearDownTest() {
s.ctrl.Finish()
}

func (s *SelectionServiceTestSuite) TestCreate_Success() {
groupID := uuid.New().String()
baanID := "baan1"
order := int32(1)

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).Return(nil)
s.mockRepo.EXPECT().Create(gomock.Any()).Return(nil)

req := &proto.CreateSelectionRequest{
GroupId: groupID,
BaanId: baanID,
Order: order,
}

res, err := s.service.Create(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.Equal(groupID, res.Selection.GroupId)
s.Equal(baanID, res.Selection.BaanId)
s.Equal(order, res.Selection.Order)
}

func (s *SelectionServiceTestSuite) TestCreate_InvalidOrder() {
groupID := uuid.New().String()
req := &proto.CreateSelectionRequest{
GroupId: groupID,
BaanId: "baan1",
Order: 6,
}

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).Return(nil)

_, err := s.service.Create(s.ctx, req)

s.Error(err)
s.Equal(codes.Internal, status.Code(err))
s.Contains(err.Error(), "Order must be in range 1-5")
}

func (s *SelectionServiceTestSuite) TestCreate_DuplicateBaan() {
groupID := uuid.New().String()
baanID := "baan1"
parsedUUID := uuid.MustParse(groupID)
existingSelections := []model.Selection{
{GroupID: &parsedUUID, Baan: baanID, Order: 1},
}

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).SetArg(1, existingSelections).Return(nil)

req := &proto.CreateSelectionRequest{
GroupId: groupID,
BaanId: baanID,
Order: 2,
}

_, err := s.service.Create(s.ctx, req)

s.Error(err)
s.Equal(codes.Internal, status.Code(err))
s.Contains(err.Error(), "Can not create selection with same baan")
}

func (s *SelectionServiceTestSuite) TestCreate_InvalidGroupID() {
req := &proto.CreateSelectionRequest{
GroupId: "invalid-uuid",
BaanId: "baan1",
Order: 1,
}

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
_, err := s.service.Create(s.ctx, req)

s.Error(err)
s.Equal(codes.Internal, status.Code(err))
}

func (s *SelectionServiceTestSuite) TestFindByGroupId_Success() {
groupID := uuid.New().String()
parsedUUID := uuid.MustParse(groupID)
selections := []model.Selection{
{GroupID: &parsedUUID, Baan: "baan1", Order: 1},
{GroupID: &parsedUUID, Baan: "baan2", Order: 2},
}

s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).SetArg(1, selections).Return(nil)

req := &proto.FindByGroupIdSelectionRequest{GroupId: groupID}
res, err := s.service.FindByGroupId(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.Len(res.Selections, 2)
s.Equal(groupID, res.Selections[0].GroupId)
s.Equal("baan1", res.Selections[0].BaanId)
s.Equal(int32(1), res.Selections[0].Order)
}

func (s *SelectionServiceTestSuite) TestDelete_Success() {
groupID := uuid.New().String()
baanID := "baan1"

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().Delete(groupID, baanID).Return(nil)

req := &proto.DeleteSelectionRequest{GroupId: groupID, BaanId: baanID}
res, err := s.service.Delete(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.True(res.Success)
}

func (s *SelectionServiceTestSuite) TestCountByBaanId_CacheHit() {
cachedResponse := &proto.CountByBaanIdSelectionResponse{
BaanCounts: []*proto.BaanCount{
{BaanId: "baan1", Count: 5},
{BaanId: "baan2", Count: 3},
},
}

s.mockCache.EXPECT().GetValue("countByBaanId", gomock.Any()).SetArg(1, cachedResponse).Return(nil)

req := &proto.CountByBaanIdSelectionRequest{}
res, err := s.service.CountByBaanId(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.Equal(cachedResponse.BaanCounts, res.BaanCounts)
}

func (s *SelectionServiceTestSuite) TestCountByBaanId_CacheMiss() {
count := map[string]int{
"baan1": 5,
"baan2": 3,
}

s.mockCache.EXPECT().GetValue("countByBaanId", gomock.Any()).Return(status.Error(codes.NotFound, "cache miss"))
s.mockRepo.EXPECT().CountByBaanId().Return(count, nil)
s.mockCache.EXPECT().SetValue("countByBaanId", gomock.Any(), s.config.CacheTTL).Return(nil)

req := &proto.CountByBaanIdSelectionRequest{}
res, err := s.service.CountByBaanId(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.Len(res.BaanCounts, 2)
}

func (s *SelectionServiceTestSuite) TestUpdate_UpdateExistBaanNewOrderSuccess() {
groupID := uuid.New().String()
parsedUUID := uuid.MustParse(groupID)
baanID := "baan1"
order := int32(2)

oldSelections := []model.Selection{
{GroupID: &parsedUUID, Baan: baanID, Order: 1},
}

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).SetArg(1, oldSelections).Return(nil)
s.mockRepo.EXPECT().UpdateExistBaanNewOrder(gomock.Any()).Return(nil)

req := &proto.UpdateSelectionRequest{
GroupId: groupID,
BaanId: baanID,
Order: order,
}

res, err := s.service.Update(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.Equal(groupID, res.Selection.GroupId)
s.Equal(baanID, res.Selection.BaanId)
s.Equal(order, res.Selection.Order)
}

func (s *SelectionServiceTestSuite) TestUpdate_UpdateExistBaanExistOrderSuccess() {
groupID := uuid.New().String()
parsedUUID := uuid.MustParse(groupID)
baanID := "baan1"
order := int32(2)

oldSelections := []model.Selection{
{GroupID: &parsedUUID, Baan: baanID, Order: 1},
{GroupID: &parsedUUID, Baan: "baan2", Order: int(order)},
}

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).SetArg(1, oldSelections).Return(nil)
s.mockRepo.EXPECT().UpdateExistBaanExistOrder(gomock.Any()).Return(nil)

req := &proto.UpdateSelectionRequest{
GroupId: groupID,
BaanId: baanID,
Order: order,
}

res, err := s.service.Update(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.Equal(groupID, res.Selection.GroupId)
s.Equal(baanID, res.Selection.BaanId)
s.Equal(order, res.Selection.Order)
}

func (s *SelectionServiceTestSuite) TestUpdate_UpdateNewBaanExistOrderSuccess() {
groupID := uuid.New().String()
parsedUUID := uuid.MustParse(groupID)
baanID := "baan1"
order := int32(2)

oldSelections := []model.Selection{
{GroupID: &parsedUUID, Baan: "baan2", Order: int(order)},
}

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).SetArg(1, oldSelections).Return(nil)
s.mockRepo.EXPECT().UpdateNewBaanExistOrder(gomock.Any()).Return(nil)

req := &proto.UpdateSelectionRequest{
GroupId: groupID,
BaanId: baanID,
Order: order,
}

res, err := s.service.Update(s.ctx, req)

s.NoError(err)
s.NotNil(res)
s.Equal(groupID, res.Selection.GroupId)
s.Equal(baanID, res.Selection.BaanId)
s.Equal(order, res.Selection.Order)
}

func (s *SelectionServiceTestSuite) TestUpdate_InvalidScenario() {
groupID := uuid.New().String()
parsedUUID := uuid.MustParse(groupID)
baanID := "newBaan"
order := int32(3)

oldSelections := []model.Selection{
{GroupID: &parsedUUID, Baan: "baan1", Order: 1},
{GroupID: &parsedUUID, Baan: "baan2", Order: 2},
}

s.mockGroupRepo.EXPECT().FindOne(gomock.Any(), gomock.Any()).SetArg(1, model.Group{IsConfirmed: false}).Return(nil)
s.mockRepo.EXPECT().FindByGroupId(groupID, gomock.Any()).SetArg(1, oldSelections).Return(nil)

req := &proto.UpdateSelectionRequest{
GroupId: groupID,
BaanId: baanID,
Order: order,
}

res, err := s.service.Update(s.ctx, req)

s.Error(err)
s.Nil(res)
s.Equal(codes.Internal, status.Code(err))
s.Contains(err.Error(), "Invalid update scenario")
}
5 changes: 5 additions & 0 deletions internal/stamp/stamp.repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
type Repository interface {
FindByUserId(userId string, stamp *model.Stamp) error
StampByUserId(userId string, stamp *model.Stamp) error
CreateAnswer(answer *model.Answer) error
}

type repositoryImpl struct {
Expand All @@ -27,3 +28,7 @@ func (r *repositoryImpl) FindByUserId(userId string, stamp *model.Stamp) error {
func (r *repositoryImpl) StampByUserId(userId string, stamp *model.Stamp) error {
return r.Db.Model(stamp).Where("user_id = ?", userId).Updates(stamp).Error
}

func (r *repositoryImpl) CreateAnswer(answer *model.Answer) error {
return r.Db.Create(answer).Error
}
15 changes: 13 additions & 2 deletions internal/stamp/stamp.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,23 @@ func (s *serviceImpl) StampByUserId(_ context.Context, in *proto.StampByUserIdRe

actIdx, ok := s.activityIdToIdx[in.ActivityId]
if !ok {
return nil, status.Error(codes.Internal, errors.New("Invalid Activity ID").Error())
return nil, status.Error(codes.Internal, errors.New("invalid Activity ID").Error())
}

tempStrStamp := []byte(stamp.Stamp)
if tempStrStamp[actIdx] == '1' {
return nil, status.Error(codes.Internal, errors.New("Already stamped").Error())
return nil, status.Error(codes.Internal, errors.New("already stamped").Error())
}

if actIdx >= 9 {
ans := &model.Answer{
ActivityID: in.ActivityId,
Text: in.Answer,
}
if err := s.repo.CreateAnswer(ans); err != nil {
s.log.Named("StampByUserId").Error("CreateAnswer", zap.Error(err))
return nil, status.Error(codes.Internal, err.Error())
}
}

tempStrStamp[actIdx] = '1'
Expand Down
Loading
Loading