Skip to content

Commit

Permalink
feat: update cash with coupon validations
Browse files Browse the repository at this point in the history
  • Loading branch information
moul committed Mar 5, 2020
1 parent f64086a commit 0b68c71
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 169 deletions.
4 changes: 4 additions & 0 deletions api/errcode.proto
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ enum ErrCode {
ErrSaveAgent = 4058;
ErrListChallengeInstances = 4059;
ErrChallengeAdd = 4060;
ErrCouponAlreadyValidatedBySameTeam = 4061;
ErrCouponExpired = 4062;
ErrCouponNotFound = 4063;
ErrUserDoesNotBelongToTeam = 4064;

//// Pathwar Server (starting at 5001)

Expand Down
1 change: 0 additions & 1 deletion api/pwdb.proto
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@ message CouponValidation {
int64 coupon_id = 205 [(gogoproto.customname) = "CouponID", (gogoproto.moretags) = "sql:\"not null\" gorm:\"index\""];
}


message Achievement {
int64 id = 1 [(gogoproto.moretags) = "gorm:\"primary_key\"", (gogoproto.customname) = "ID"];
google.protobuf.Timestamp created_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
Expand Down
4 changes: 2 additions & 2 deletions docs/gen.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions go/gen.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

285 changes: 150 additions & 135 deletions go/pkg/errcode/errcode.pb.go

Large diffs are not rendered by default.

38 changes: 29 additions & 9 deletions go/pkg/pwapi/api_coupon-validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"

"github.com/jinzhu/gorm"
"pathwar.land/v2/go/pkg/errcode"
"pathwar.land/v2/go/pkg/pwdb"
)
Expand All @@ -19,6 +20,8 @@ func (svc *service) CouponValidate(ctx context.Context, in *CouponValidate_Input
return nil, fmt.Errorf("get userid from context: %w", err)
}

// FIXME: create transaction

// check if user belongs to team
// FIXME: or is admin
var team pwdb.Team
Expand All @@ -28,34 +31,47 @@ func (svc *service) CouponValidate(ctx context.Context, in *CouponValidate_Input
First(&team, in.TeamID).
Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
return nil, errcode.ErrUserDoesNotBelongToTeam.Wrap(err)
}

// fetch coupon
var coupon pwdb.Coupon
err = svc.db.
Where(pwdb.Coupon{Hash: in.Hash}).
Where(pwdb.Coupon{Hash: in.Hash, SeasonID: team.SeasonID}).
First(&coupon).
Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
return nil, errcode.ErrCouponNotFound.Wrap(err)
}

// is already validated by same team
var validations int32
err = svc.db.
Model(&pwdb.CouponValidation{}).
Where(&pwdb.CouponValidation{CouponID: coupon.ID, TeamID: team.ID}).
Count(&validations).
Error
if err != nil {
return nil, pwdb.GormToErrcode(err)
}
if validations > 0 {
return nil, errcode.ErrCouponAlreadyValidatedBySameTeam
}

// is expired
err = svc.db.
Model(&pwdb.CouponValidation{}).
Where(&pwdb.CouponValidation{CouponID: coupon.ID}).
Count(&validations).
Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
return nil, pwdb.GormToErrcode(err)
}
if validations >= coupon.MaxValidationCount {
return nil, errcode.TODO.Wrap(err)
return nil, errcode.ErrCouponExpired
}

// FIXME: validate team
// FIXME: already validated by this team
// FIXME: inacitve user/team

// create validation
Expand All @@ -67,10 +83,14 @@ func (svc *service) CouponValidate(ctx context.Context, in *CouponValidate_Input
}
err = svc.db.Create(&validation).Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
return nil, pwdb.GormToErrcode(err)
}

// FIXME: increment team.cash
// update team cash
err = svc.db.Model(&team).UpdateColumn("cash", gorm.Expr("cash + ?", coupon.Value)).Error
if err != nil {
return nil, pwdb.GormToErrcode(err)
}

// load it again with preload
err = svc.db.
Expand All @@ -80,7 +100,7 @@ func (svc *service) CouponValidate(ctx context.Context, in *CouponValidate_Input
First(&validation, validation.ID).
Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
return nil, pwdb.GormToErrcode(err)
}

ret := CouponValidate_Output{
Expand Down
45 changes: 25 additions & 20 deletions go/pkg/pwapi/api_coupon-validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,58 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"pathwar.land/v2/go/internal/testutil"
"pathwar.land/v2/go/pkg/errcode"
"pathwar.land/v2/go/pkg/pwdb"
)

func TestEngine_CouponValidate(t *testing.T) {
svc, cleanup := TestingService(t, ServiceOpts{Logger: testutil.Logger(t)})
defer cleanup()
ctx := testingSetContextToken(context.Background(), t)
db := testingSvcDB(t, svc)

// fetch user session
session, err := svc.UserGetSession(ctx, nil)
assert.NoError(t, err)
activeTeam := session.User.ActiveTeamMember.Team

var tests = []struct {
name string
input *CouponValidate_Input
expectedErr error
name string
input *CouponValidate_Input
expectedCash int64
expectedErr error
}{
{"nil", nil, errcode.ErrMissingInput},
{"empty", &CouponValidate_Input{}, errcode.ErrMissingInput},
// more invalid arguments
// test-coupon-1, invalid season
// {"invalid team ID", &CouponValidate_Input{CouponID: coupons.Items[0].ID, TeamID: 42}, ErrInvalidInput},
// {"valid 1", &CouponValidate_Input{CouponID: coupons.Items[0].ID, TeamID: activeTeam.ID}, nil},
// {"valid 2 (duplicate)", &CouponValidate_Input{CouponID: coupons.Items[0].ID, TeamID: activeTeam.ID}, errcode.TODO},
// FIXME: check for a team and a coupon in different seasons
// FIXME: check for a team from another user
// FIXME: check for a coupon in draft mode
{"test-coupon-1", &CouponValidate_Input{Hash: "test-coupon-1", TeamID: activeTeam.ID}, nil},
{"test-coupon-1-again", &CouponValidate_Input{Hash: "test-coupon-1", TeamID: activeTeam.ID}, errcode.TODO},
//{"test-coupon-2", &CouponValidate_Input{Hash: "test-coupon-2", TeamID: activeTeam.ID}, nil},
{"test-coupon-3-invalid-season", &CouponValidate_Input{Hash: "test-coupon-3", TeamID: activeTeam.ID}, errcode.TODO},
{"test-coupon-4", &CouponValidate_Input{Hash: "test-coupon-4", TeamID: activeTeam.ID}, nil},
{"nil", nil, 0, errcode.ErrMissingInput},
{"empty", &CouponValidate_Input{}, 0, errcode.ErrMissingInput},
{"invalid team ID", &CouponValidate_Input{Hash: "test-coupon-1", TeamID: 42}, 0, errcode.ErrUserDoesNotBelongToTeam},
{"test-coupon-1", &CouponValidate_Input{Hash: "test-coupon-1", TeamID: activeTeam.ID}, 42, nil},
{"test-coupon-1-again", &CouponValidate_Input{Hash: "test-coupon-1", TeamID: activeTeam.ID}, 42, errcode.ErrCouponAlreadyValidatedBySameTeam},
{"test-coupon-2-invalid-season", &CouponValidate_Input{Hash: "test-coupon-2", TeamID: activeTeam.ID}, 42, errcode.ErrCouponNotFound},
{"test-coupon-3-max-validation", &CouponValidate_Input{Hash: "test-coupon-3", TeamID: activeTeam.ID}, 42, errcode.ErrCouponExpired},
{"test-coupon-4", &CouponValidate_Input{Hash: "test-coupon-4", TeamID: activeTeam.ID}, 84, nil},
}

for _, test := range tests {
ret, err := svc.CouponValidate(ctx, test.input)
testSameErrcodes(t, test.name, test.expectedErr, err)

// check cash, even if the previous function returned an error
if test.input != nil && test.input.TeamID != 0 && test.input.TeamID != 42 {
var team pwdb.Team
err2 := db.First(&team, test.input.TeamID).Error
require.NoErrorf(t, err2, test.name)
assert.Equalf(t, test.expectedCash, team.Cash, test.name)
}

if err != nil {
continue
continue // skip other tests of previous function returned an error
}

validation := ret.CouponValidation
assert.Equalf(t, test.input.Hash, validation.Coupon.Hash, test.name)
assert.Equalf(t, test.input.TeamID, validation.Team.ID, test.name)
assert.Equalf(t, validation.Team.SeasonID, validation.Coupon.SeasonID, test.name)
//fmt.Println(godev.PrettyJSON(ret))
}
}

0 comments on commit 0b68c71

Please sign in to comment.