Skip to content

Commit

Permalink
API available offers tested
Browse files Browse the repository at this point in the history
  • Loading branch information
henrod committed Mar 9, 2017
1 parent 5a4a4a7 commit bcc3fef
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 175 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ start-deps:
stop-deps:
@env MY_IP=${MY_IP} docker-compose --project-name offers down

test: deps unit integration acceptance test-coverage-func
test: deps unit integration test-coverage-func

clear-coverage-profiles:
@find . -name '*.coverprofile' -delete
Expand Down
2 changes: 1 addition & 1 deletion api/offer_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (h *OfferRequestHandler) getOffers(w http.ResponseWriter, r *http.Request)
}
currentTime := h.App.Clock.GetTime()

ots, err := models.GetAvailableOffers(h.App.DB, nil, gameID, playerID, currentTime, mr)
ots, err := models.GetAvailableOffers(h.App.DB, h.App.RedisClient, gameID, playerID, currentTime, mr)

if err != nil {
logger.WithError(err).Error("Failed to retrieve offer for player.")
Expand Down
296 changes: 151 additions & 145 deletions api/offer_request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/topfreegames/offers/models"
. "github.com/topfreegames/offers/testing"
)

Expand All @@ -41,150 +42,155 @@ var _ = Describe("Offer Handler", func() {
})
})

// Describe("GET /available-offers", func() {
// It("should return available offers", func() {
// playerID := "player-1"
// gameID := "offers-game"
// url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
// request, _ := http.NewRequest("GET", url, nil)
// var jsonBody map[string][]map[string]interface{}
//
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
// err := json.Unmarshal(recorder.Body.Bytes(), &jsonBody)
// Expect(err).NotTo(HaveOccurred())
// Expect(recorder.Code).To(Equal(http.StatusOK))
// Expect(jsonBody).To(HaveKey("popup"))
// Expect(jsonBody).To(HaveKey("store"))
// popup := jsonBody["popup"]
// Expect(popup).To(HaveLen(1))
// Expect(popup[0]).To(HaveKey("id"))
// Expect(popup[0]).To(HaveKey("productId"))
// Expect(popup[0]).To(HaveKey("contents"))
// Expect(popup[0]).To(HaveKey("metadata"))
// store := jsonBody["store"]
// Expect(store).To(HaveLen(2))
// Expect(store[0]).To(HaveKey("id"))
// Expect(store[0]).To(HaveKey("productId"))
// Expect(store[0]).To(HaveKey("contents"))
// Expect(store[0]).To(HaveKey("metadata"))
// Expect(store[0]).To(HaveKey("expireAt"))
// Expect(store[1]).To(HaveKey("id"))
// Expect(store[1]).To(HaveKey("productId"))
// Expect(store[1]).To(HaveKey("contents"))
// Expect(store[1]).To(HaveKey("metadata"))
// Expect(store[1]).To(HaveKey("expireAt"))
// })
//
// It("should return empty list of available offers", func() {
// playerID := "player-1"
// gameID := "non-existing-offers-game"
// url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
// request, _ := http.NewRequest("GET", url, nil)
// var jsonBody map[string]map[string]interface{}
//
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
// err := json.Unmarshal(recorder.Body.Bytes(), &jsonBody)
// Expect(err).NotTo(HaveOccurred())
// Expect(jsonBody).To(BeEmpty())
// Expect(recorder.Code).To(Equal(http.StatusOK))
// })
//
// It("should return status code 400 if player-id is not informed available offers", func() {
// gameID := "offers-game"
// url := fmt.Sprintf("/available-offers?game-id=%s", gameID)
// request, _ := http.NewRequest("GET", url, nil)
//
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
// Expect(recorder.Code).To(Equal(http.StatusBadRequest))
// var obj map[string]interface{}
// err := json.Unmarshal([]byte(recorder.Body.String()), &obj)
// Expect(err).NotTo(HaveOccurred())
// Expect(obj["code"]).To(Equal("OFF-004"))
// Expect(obj["error"]).To(Equal("The player-id parameter cannot be empty."))
// Expect(obj["description"]).To(Equal("The player-id parameter cannot be empty"))
//
// })
//
// It("should return status code 400 if game-id is not informed available offers", func() {
// playerID := "player-1"
// url := fmt.Sprintf("/available-offers?player-id=%s", playerID)
// request, _ := http.NewRequest("GET", url, nil)
//
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
// Expect(recorder.Code).To(Equal(http.StatusBadRequest))
// var obj map[string]interface{}
// err := json.Unmarshal([]byte(recorder.Body.String()), &obj)
// Expect(err).NotTo(HaveOccurred())
// Expect(obj["code"]).To(Equal("OFF-004"))
// Expect(obj["error"]).To(Equal("The game-id parameter cannot be empty."))
// Expect(obj["description"]).To(Equal("The game-id parameter cannot be empty"))
// })
//
// It("should return status code of 500 if some error occurred", func() {
// playerID := "player-1"
// gameID := "offers-game"
// url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
// request, _ := http.NewRequest("GET", url, nil)
//
// oldDB := app.DB
// db, err := GetTestDB()
// Expect(err).NotTo(HaveOccurred())
// app.DB = db
// app.DB.(*runner.DB).DB.Close() // make DB connection unavailable
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
//
// Expect(recorder.Code).To(Equal(http.StatusInternalServerError))
// var obj map[string]interface{}
// err = json.Unmarshal([]byte(recorder.Body.String()), &obj)
// Expect(err).NotTo(HaveOccurred())
// Expect(obj["code"]).To(Equal("OFF-004"))
// Expect(obj["error"]).To(Equal("Failed to retrieve offer for player"))
// Expect(obj["description"]).To(Equal("sql: database is closed"))
// app.DB = oldDB // avoid errors in after each
// })
//
// It("should not return offer after claim if offer template period has max 1", func() {
// // Create Offer by requesting it
// gameID := "limited-offers-game"
// playerID := "player-1"
// place := "store"
// url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
// request, _ := http.NewRequest("GET", url, nil)
// var body map[string][]*models.OfferToReturn
//
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Code).To(Equal(http.StatusOK))
// err := json.Unmarshal(recorder.Body.Bytes(), &body)
// Expect(err).ToNot(HaveOccurred())
//
// // Claim the Offer
// id := body[place][0].ID
// offerReader := JSONFor(JSON{
// "playerId": playerID,
// "gameId": gameID,
// })
// request, _ = http.NewRequest("PUT", fmt.Sprintf("/available-offers/%s/claim", id), offerReader)
// recorder = httptest.NewRecorder()
//
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Code).To(Equal(http.StatusOK))
//
// // Offer must not be returned again in next Get
// request, _ = http.NewRequest("GET", url, nil)
// recorder = httptest.NewRecorder()
// app.Router.ServeHTTP(recorder, request)
// Expect(recorder.Code).To(Equal(http.StatusOK))
// var newBody map[string][]*models.OfferToReturn
// err = json.Unmarshal(recorder.Body.Bytes(), &newBody)
// Expect(err).ToNot(HaveOccurred())
// Expect(newBody).NotTo(HaveKey(place))
// })
// })
Describe("GET /available-offers", func() {
It("should return available offers", func() {
playerID := "player-1"
gameID := "offers-game"
url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
request, _ := http.NewRequest("GET", url, nil)
var jsonBody map[string][]map[string]interface{}

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
err := json.Unmarshal(recorder.Body.Bytes(), &jsonBody)
Expect(err).NotTo(HaveOccurred())
Expect(recorder.Code).To(Equal(http.StatusOK))
Expect(jsonBody).To(HaveKey("popup"))
Expect(jsonBody).To(HaveKey("store"))
popup := jsonBody["popup"]
Expect(popup).To(HaveLen(1))
Expect(popup[0]).To(HaveKey("id"))
Expect(popup[0]).To(HaveKey("productId"))
Expect(popup[0]).To(HaveKey("contents"))
Expect(popup[0]).To(HaveKey("metadata"))
store := jsonBody["store"]
Expect(store).To(HaveLen(2))
Expect(store[0]).To(HaveKey("id"))
Expect(store[0]).To(HaveKey("productId"))
Expect(store[0]).To(HaveKey("contents"))
Expect(store[0]).To(HaveKey("metadata"))
Expect(store[0]).To(HaveKey("expireAt"))
Expect(store[1]).To(HaveKey("id"))
Expect(store[1]).To(HaveKey("productId"))
Expect(store[1]).To(HaveKey("contents"))
Expect(store[1]).To(HaveKey("metadata"))
Expect(store[1]).To(HaveKey("expireAt"))
})

It("should return empty list of available offers", func() {
playerID := "player-1"
gameID := "non-existing-offers-game"
url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
request, _ := http.NewRequest("GET", url, nil)
var jsonBody map[string]map[string]interface{}

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
err := json.Unmarshal(recorder.Body.Bytes(), &jsonBody)
Expect(err).NotTo(HaveOccurred())
Expect(jsonBody).To(BeEmpty())
Expect(recorder.Code).To(Equal(http.StatusOK))
})

It("should return status code 400 if player-id is not informed available offers", func() {
gameID := "offers-game"
url := fmt.Sprintf("/available-offers?game-id=%s", gameID)
request, _ := http.NewRequest("GET", url, nil)

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
Expect(recorder.Code).To(Equal(http.StatusBadRequest))
var obj map[string]interface{}
err := json.Unmarshal([]byte(recorder.Body.String()), &obj)
Expect(err).NotTo(HaveOccurred())
Expect(obj["code"]).To(Equal("OFF-004"))
Expect(obj["error"]).To(Equal("The player-id parameter cannot be empty."))
Expect(obj["description"]).To(Equal("The player-id parameter cannot be empty"))

})

It("should return status code 400 if game-id is not informed available offers", func() {
playerID := "player-1"
url := fmt.Sprintf("/available-offers?player-id=%s", playerID)
request, _ := http.NewRequest("GET", url, nil)

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))
Expect(recorder.Code).To(Equal(http.StatusBadRequest))
var obj map[string]interface{}
err := json.Unmarshal([]byte(recorder.Body.String()), &obj)
Expect(err).NotTo(HaveOccurred())
Expect(obj["code"]).To(Equal("OFF-004"))
Expect(obj["error"]).To(Equal("The game-id parameter cannot be empty."))
Expect(obj["description"]).To(Equal("The game-id parameter cannot be empty"))
})

It("should return status code of 500 if some error occurred", func() {
playerID := "player-1"
gameID := "offers-game"
url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
request, _ := http.NewRequest("GET", url, nil)

oldDB := app.DB
db, err := GetTestDB()
Expect(err).NotTo(HaveOccurred())
app.DB = db
app.DB.(*runner.DB).DB.Close() // make DB connection unavailable
app.Router.ServeHTTP(recorder, request)
Expect(recorder.Header().Get("Content-Type")).To(Equal("application/json"))

Expect(recorder.Code).To(Equal(http.StatusInternalServerError))
var obj map[string]interface{}
err = json.Unmarshal([]byte(recorder.Body.String()), &obj)
Expect(err).NotTo(HaveOccurred())
Expect(obj["code"]).To(Equal("OFF-004"))
Expect(obj["error"]).To(Equal("Failed to retrieve offer for player"))
Expect(obj["description"]).To(Equal("sql: database is closed"))
app.DB = oldDB // avoid errors in after each
})

It("should not return offer after claim if offer template period has max 1", func() {
// Create Offer by requesting it
gameID := "limited-offers-game"
playerID := "player-1"
place := "store"
url := fmt.Sprintf("/available-offers?player-id=%s&game-id=%s", playerID, gameID)
request, _ := http.NewRequest("GET", url, nil)
var body map[string][]*models.OfferToReturn

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Code).To(Equal(http.StatusOK))
err := json.Unmarshal(recorder.Body.Bytes(), &body)
Expect(err).ToNot(HaveOccurred())

// Claim the Offer
id := body[place][0].ID
offerReader := JSONFor(JSON{
"gameId": gameID,
"playerId": playerID,
"productId": "com.tfg.sample",
"timestamp": time.Now().Unix(),
"transactionId": uuid.NewV4().String(),
"offerInstanceId": id,
})
request, _ = http.NewRequest("PUT", "/offers/claim", offerReader)
recorder = httptest.NewRecorder()

app.Router.ServeHTTP(recorder, request)
Expect(recorder.Body.String()).To(Equal(`{"contents":{"gems":5,"gold":100}}`))
Expect(recorder.Code).To(Equal(http.StatusOK))

// Offer must not be returned again in next Get
request, _ = http.NewRequest("GET", url, nil)
recorder = httptest.NewRecorder()
app.Router.ServeHTTP(recorder, request)
Expect(recorder.Code).To(Equal(http.StatusOK))
var newBody map[string][]*models.OfferToReturn
err = json.Unmarshal(recorder.Body.Bytes(), &newBody)
Expect(err).ToNot(HaveOccurred())
Expect(newBody).NotTo(HaveKey(place))
})
})

Describe("PUT /offers/claim", func() {
It("should claim valid offer", func() {
Expand Down Expand Up @@ -284,7 +290,7 @@ var _ = Describe("Offer Handler", func() {
Expect(err).NotTo(HaveOccurred())
Expect(obj["code"]).To(Equal("OFF-002"))
Expect(obj["error"]).To(Equal("ValidationFailedError"))
Expect(obj["description"]).To(Equal("GameID: non zero value required;PlayerID: non zero value required;ProductID: non zero value required;Timestamp: 0 does not validate as requiredInt;;TransactionID: non zero value required;"))
Expect(obj["description"]).To(Equal("GameID: non zero value required;PlayerID: non zero value required;ProductID: non zero value required;Timestamp: non zero value required;TransactionID: non zero value required;"))
})

It("should return 404 if non existing OfferID", func() {
Expand Down
22 changes: 0 additions & 22 deletions api/validation_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"context"
"encoding/json"
"net/http"
"strconv"

"gopkg.in/mgutz/dat.v2/dat"

Expand Down Expand Up @@ -111,27 +110,6 @@ func (m *ValidationMiddleware) configureCustomValidators() {
},
),
)
govalidator.CustomTypeTagMap.Set(
"requiredInt",
govalidator.CustomTypeValidator(
func(i interface{}, context interface{}) bool {
switch v := i.(type) {
case string:
_, err := strconv.ParseInt(v, 10, 64)
return err == nil && v != ""
case int:
return v != 0
case int16:
return v != int16(0)
case int32:
return v != int32(0)
case int64:
return v != int64(0)
}
return false
},
),
)
}

//ServeHTTP method
Expand Down
6 changes: 3 additions & 3 deletions migrations/migrations.go

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

0 comments on commit bcc3fef

Please sign in to comment.