Skip to content

Commit

Permalink
feat: coupons
Browse files Browse the repository at this point in the history
  • Loading branch information
moul committed Dec 21, 2019
1 parent 1243c75 commit 6e18b00
Show file tree
Hide file tree
Showing 12 changed files with 1,011 additions and 159 deletions.
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ jobs:
- moul/mod-download
- gotools/mod-tidy-check
- run: make install
- run: SKIP_SLOW_TESTS=1 make unittest GO_TEST_OPTS="-test.timeout=30s -count=10"
- run: SKIP_SLOW_TESTS=0 make unittest GO_TEST_OPTS="-test.timeout=30s -count=2"
- run: SKIP_SLOW_TESTS=1 make unittest GO_TEST_OPTS="-test.timeout=60s -count=10"
- run: SKIP_SLOW_TESTS=0 make unittest GO_TEST_OPTS="-test.timeout=60s -count=2"
- moul/install_golangci-lint
- run: PATH=$PATH:$(pwd)/bin make lint
- codecov/upload:
Expand Down
16 changes: 16 additions & 0 deletions api/pwapi.proto
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ service Service {
rpc TeamSendInvite(TeamSendInvite.Input) returns (TeamSendInvite.Output) { option (google.api.http) = {post: "/team/invite"; body: "*"}; };
rpc TeamAcceptInvite(TeamAcceptInvite.Input) returns (TeamAcceptInvite.Output) { option (google.api.http) = {post: "/team/invite/accept"; body: "*"}; };

//
// Coupon
//

rpc CouponValidate(CouponValidate.Input) returns (CouponValidate.Output) { option (google.api.http) = {post: "/coupon-validation"; body: "*"}; };

//
// Tool
//
Expand Down Expand Up @@ -288,6 +294,16 @@ message GetInfo {
}
}

message CouponValidate {
message Input {
string hash = 1;
int64 team_id = 2 [(gogoproto.customname) = "TeamID"];
}
message Output {
pathwar.db.CouponValidation coupon_validation = 1;
}
}


//
// Swagger tuning
Expand Down
2 changes: 1 addition & 1 deletion docs/gen.sum

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

2 changes: 1 addition & 1 deletion go/gen.sum

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

90 changes: 90 additions & 0 deletions go/pkg/pwapi/api_coupon-validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package pwapi

import (
"context"
"fmt"

"pathwar.land/go/pkg/errcode"
"pathwar.land/go/pkg/pwdb"
)

func (svc *service) CouponValidate(ctx context.Context, in *CouponValidate_Input) (*CouponValidate_Output, error) {
// validation
if in == nil || in.Hash == "" || in.TeamID == 0 {
return nil, errcode.ErrMissingInput
}

userID, err := userIDFromContext(ctx, svc.db)
if err != nil {
return nil, fmt.Errorf("get userid from context: %w", err)
}

// check if user belongs to team
// FIXME: or is admin
var team pwdb.Team
err = svc.db.
Joins("JOIN team_member ON team_member.team_id = team.id AND team_member.user_id = ?", userID).
Preload("Members").
First(&team, in.TeamID).
Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
}

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

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

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

// create validation
validation := pwdb.CouponValidation{
Comment: "xxx",
AuthorID: userID,
TeamID: team.ID,
CouponID: coupon.ID,
}
err = svc.db.Create(&validation).Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
}

// FIXME: increment team.cash

// load it again with preload
err = svc.db.
Preload("Team").
Preload("Author").
Preload("Coupon").
First(&validation, validation.ID).
Error
if err != nil {
return nil, errcode.TODO.Wrap(err)
}

ret := CouponValidate_Output{
CouponValidation: &validation,
}
return &ret, nil
}
57 changes: 57 additions & 0 deletions go/pkg/pwapi/api_coupon-validate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package pwapi

import (
"context"
"testing"

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

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

// 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
}{
{"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},
}

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

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))
}
}
9 changes: 5 additions & 4 deletions go/pkg/pwapi/api_tool-info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"pathwar.land/go/internal/testutil"
)

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

status, err := svc.ToolInfo(ctx, nil)
checkErr(t, "", err)
assert.NoError(t, err)
expected := &GetInfo_Output{
Version: "dev",
Commit: "n/a",
BuiltAt: "n/a",
BuiltBy: "n/a",
}
expected.Uptime = status.Uptime // may vary
testSameDeep(t, "", expected, status)
assert.Equal(t, expected, status)
}
Loading

0 comments on commit 6e18b00

Please sign in to comment.