Skip to content

Commit

Permalink
feat: add logic that validates items prices
Browse files Browse the repository at this point in the history
  • Loading branch information
kweeuhree committed Jan 8, 2025
1 parent 656b9bb commit 8007335
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 66 deletions.
82 changes: 71 additions & 11 deletions cmd/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,29 @@ import (
"kweeuhree.receipt-processor-challenge/internal/models"
)

var handlers *Handlers
var receiptStore *models.ReceiptStore

// Writes an OK status to the response
func mockHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.WriteHeader(http.StatusOK)
type TestDependencies struct {
receiptStore *models.ReceiptStore
utils *utils.Utils
helpers *helpers.Helpers
handlers *Handlers
}

func TestMain(m *testing.M) {
receiptStore = models.NewStore()
func setupTestDependencies() *TestDependencies {
receiptStore := models.NewStore()
utils := &utils.Utils{}
helpers := &helpers.Helpers{}
handlers = NewHandlers(log.Default(), log.Default(), receiptStore, utils, helpers)
m.Run()
handlers := NewHandlers(log.Default(), log.Default(), receiptStore, utils, helpers)

return &TestDependencies{
receiptStore: receiptStore,
utils: utils,
helpers: helpers,
handlers: handlers,
}
}

func TestProcessReceipt(t *testing.T) {
deps := setupTestDependencies()
tests := []struct {
name string
input ReceiptInput
Expand Down Expand Up @@ -60,7 +66,7 @@ func TestProcessReceipt(t *testing.T) {
resp := httptest.NewRecorder()

// Test ProcessReceipt function
handlers.ProcessReceipt(resp, req)
deps.handlers.ProcessReceipt(resp, req)

// Check the response status
if status := resp.Code; status != entry.expectedStatus {
Expand Down Expand Up @@ -95,6 +101,60 @@ func TestProcessReceipt(t *testing.T) {
}
}

})
}
}

func TestGetReceiptPoints(t *testing.T) {
router := httprouter.New()
// Register the route
router.GET("/receipts/:id/points", mockHandler)

tests := []struct {
name string
ID string
url string
status int
}{
{"Valid receipt id", SimpleReceipt.ID, "/receipts/123-qwe-456-rty-7890/points", http.StatusOK},
{"Invalid receipt id", "hello-world", "/receipts/hello-world/points", http.StatusNotFound},
{"Invalid request url", SimpleReceipt.ID, "/hello/123-qwe-456-rty-7890/world", http.StatusNotFound},
}

for _, entry := range tests {
t.Run(entry.name, func(t *testing.T) {
receiptStore.Insert(*SimpleReceipt)

// Create request and response
req := httptest.NewRequest(http.MethodGet, entry.url, nil)
// Create request context that will enable id extraction
params := httprouter.Params{
httprouter.Param{Key: "id", Value: entry.ID},
}
ctx := context.WithValue(req.Context(), httprouter.ParamsKey, params)
req = req.WithContext(ctx)
// Create response
resp := httptest.NewRecorder()

// Serve the request
router.ServeHTTP(resp, req)

// Test GetReceiptPoints function
handlers.GetReceiptPoints(resp, req)

// Check response status
if resp.Code == http.StatusOK && entry.status == http.StatusOK {
var response PointsResponse
// Decode the request body
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
t.Fatalf("Failed to decode response: %v", err)
}
// Check if the points match
if response.Points != SimpleReceipt.Points {
t.Fatalf("Expected %d points, received %d", SimpleReceipt.Points, response.Points)
}
}

t.Cleanup(func() {
receiptStore = models.NewStore()
})
Expand Down
3 changes: 1 addition & 2 deletions cmd/handlers/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import (
"kweeuhree.receipt-processor-challenge/internal/validator"
)

var v *validator.Validator

func (input *ReceiptInput) Validate() {
var v *validator.Validator
input.CheckField(v.NotBlank(input.Retailer), "retailerName", "This field cannot be blank")
input.CheckField(v.NotBlank(input.PurchaseDate), "purchaseDate", "This field cannot be blank")
input.CheckField(v.ValidDate(input.PurchaseDate), "purchaseDate", "This field must be a valid date")
Expand Down
25 changes: 17 additions & 8 deletions cmd/helpers/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,10 @@ import (
"github.com/julienschmidt/httprouter"
)

var h *Helpers

func TestMain(m *testing.M) {
h = &Helpers{
func TestServerError(t *testing.T) {
h := &Helpers{
ErrorLog: log.Default(),
}
m.Run()
}

func TestServerError(t *testing.T) {
tests := []struct {
name string
err error
Expand Down Expand Up @@ -54,6 +48,9 @@ func TestServerError(t *testing.T) {
}

func TestClientError(t *testing.T) {
h := &Helpers{
ErrorLog: log.Default(),
}
tests := []struct {
name string
status int
Expand All @@ -76,6 +73,9 @@ func TestClientError(t *testing.T) {
}

func TestNotFound(t *testing.T) {
h := &Helpers{
ErrorLog: log.Default(),
}
resp := httptest.NewRecorder()
h.NotFound(resp)

Expand All @@ -85,6 +85,9 @@ func TestNotFound(t *testing.T) {
}

func TestDecodeJSON(t *testing.T) {
h := &Helpers{
ErrorLog: log.Default(),
}
tests := []struct {
name string
reqBody string
Expand Down Expand Up @@ -114,6 +117,9 @@ func TestDecodeJSON(t *testing.T) {
}

func TestEncodeJSON(t *testing.T) {
h := &Helpers{
ErrorLog: log.Default(),
}
tests := []struct {
name string
data interface{}
Expand Down Expand Up @@ -151,6 +157,9 @@ func TestEncodeJSON(t *testing.T) {
}

func TestGetIdFromParams(t *testing.T) {
h := &Helpers{
ErrorLog: log.Default(),
}
tests := []struct {
name string
paramsId string
Expand Down
16 changes: 8 additions & 8 deletions cmd/utils/calculate-utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,8 @@ import (
"kweeuhree.receipt-processor-challenge/testdata"
)

// Declare and initialize application instance for all tests
var utils *Utils

func TestMain(m *testing.M) {
utils = &Utils{}
m.Run()
}

func Test_getRetailerNamePoints(t *testing.T) {
var utils *Utils
tests := []struct {
name string
expected int
Expand All @@ -37,6 +30,7 @@ func Test_getRetailerNamePoints(t *testing.T) {
}

func Test_isAlphanumeric(t *testing.T) {
var utils *Utils
tests := []struct {
name string
char string
Expand All @@ -61,6 +55,7 @@ func Test_isAlphanumeric(t *testing.T) {
}

func Test_getRoundTotalPoints(t *testing.T) {
var utils *Utils
tests := []struct {
name string
num float64
Expand All @@ -85,6 +80,7 @@ func Test_getRoundTotalPoints(t *testing.T) {
}

func Test_getQuartersPoints(t *testing.T) {
var utils *Utils
tests := []struct {
name string
num float64
Expand All @@ -109,6 +105,7 @@ func Test_getQuartersPoints(t *testing.T) {
}

func Test_getEveryTwoItemsPoints(t *testing.T) {
var utils *Utils
tests := []struct {
name string
items []models.Item
Expand All @@ -130,6 +127,7 @@ func Test_getEveryTwoItemsPoints(t *testing.T) {
}

func Test_getItemDescriptionPoints(t *testing.T) {
var utils *Utils
tests := []struct {
name string
items []models.Item
Expand All @@ -152,6 +150,7 @@ func Test_getItemDescriptionPoints(t *testing.T) {
}

func Test_getOddDayPoints(t *testing.T) {
var utils *Utils
tests := []struct {
name string
date string
Expand All @@ -176,6 +175,7 @@ func Test_getOddDayPoints(t *testing.T) {
}

func Test_getPurchaseTimePoints(t *testing.T) {
var utils *Utils
tests := []struct {
date string
expected int
Expand Down
40 changes: 24 additions & 16 deletions internal/models/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@ var SimpleReceipt = &Receipt{
{ShortDescription: "Pepsi - 12-oz", Price: "1.25"},
},
}
var store *ReceiptStore

func TestMain(m *testing.M) {
store = NewStore()
m.Run()
type TestStore struct {
receiptStore *ReceiptStore
}

func setupTestDependencies() *TestStore {
receiptStore := NewStore()
return &TestStore{
receiptStore: receiptStore,
}
}

func TestNewStore(t *testing.T) {
if store.receipts == nil {
d := setupTestDependencies()
if d.receiptStore.receipts == nil {
t.Errorf("Expected receipts field to be a map, but got nil")
}
}

func TestInsert(t *testing.T) {
d := setupTestDependencies()
tests := []struct {
name string
receipt *Receipt
Expand All @@ -38,29 +44,30 @@ func TestInsert(t *testing.T) {
}
for _, entry := range tests {
t.Run(entry.name, func(t *testing.T) {
_ = store.Insert(*entry.receipt)
_ = d.receiptStore.Insert(*entry.receipt)

if entry.receipt == nil && len(store.receipts) == 1 {
t.Errorf("Expected receipts map to be empty, but got length %d", len(store.receipts))
if entry.receipt == nil && len(d.receiptStore.receipts) == 1 {
t.Errorf("Expected receipts map to be empty, but got length %d", len(d.receiptStore.receipts))
}

if entry.receipt != nil && len(store.receipts) != 1 {
t.Errorf("Expected a receipt, but got length %d", len(store.receipts))
if entry.receipt != nil && len(d.receiptStore.receipts) != 1 {
t.Errorf("Expected a receipt, but got length %d", len(d.receiptStore.receipts))
}

_, exists := store.receipts[entry.receipt.ID]
_, exists := d.receiptStore.receipts[entry.receipt.ID]
if !exists {
t.Errorf("receipt with ID %v was not inserted", entry.receipt.ID)
}

t.Cleanup(func() {
store = NewStore()
d.receiptStore = NewStore()
})
})
}
}

func TestGet(t *testing.T) {
d := setupTestDependencies()
tests := []struct {
name string
receipt *Receipt
Expand All @@ -70,8 +77,8 @@ func TestGet(t *testing.T) {
}
for _, entry := range tests {
t.Run(entry.name, func(t *testing.T) {
_ = store.Insert(*entry.receipt)
inserted, err := store.Get(entry.receipt.ID)
_ = d.receiptStore.Insert(*entry.receipt)
inserted, err := d.receiptStore.Get(entry.receipt.ID)
if err != nil {
t.Errorf("Could not get receipt with ID %s", entry.receipt.ID)
}
Expand All @@ -81,14 +88,15 @@ func TestGet(t *testing.T) {
}

t.Cleanup(func() {
store = NewStore()
d.receiptStore = NewStore()
})
})
}
}

func TestGetInvalidID(t *testing.T) {
_, err := store.Get("invalid-id")
d := setupTestDependencies()
_, err := d.receiptStore.Get("invalid-id")
if err == nil {
t.Errorf("Expected an error, but got none")
}
Expand Down
Loading

0 comments on commit 8007335

Please sign in to comment.