Skip to content

Commit

Permalink
chore(test): Writing some more tests. (#1488)
Browse files Browse the repository at this point in the history
* chore(test): Writing some more tests.

Just writing some tests on the weekend.

* chore: More tests
  • Loading branch information
elliotcourant authored Aug 20, 2023
1 parent e792ad0 commit a2a574d
Show file tree
Hide file tree
Showing 6 changed files with 864 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/incoming.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ jobs:
run: make test-go
- name: Annotate tests
if: always()
uses: guyarb/golang-test-annotations@v0.6.0
uses: guyarb/golang-test-annotations@v0.7.0
with:
test-results: '${{ github.workspace }}/rest-api-tests.json'
- uses: actions/upload-artifact@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ jobs:
run: make test-go
- name: Annotate tests
if: always()
uses: guyarb/golang-test-annotations@v0.6.0
uses: guyarb/golang-test-annotations@v0.7.0
with:
test-results: '${{ github.workspace }}/rest-api-tests.json'
- uses: actions/upload-artifact@v3
Expand Down
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -276,17 +276,25 @@ release-asset:
$(GH) release upload $(RELEASE_VERSION) $(BINARY_TAR) --clobber
endif


TEST_FLAGS=-race -v -parallel 8
test-go: $(GO) $(GOMODULES) $(ALL_GO_FILES) $(GOTESTSUM)
$(COVERAGE_TXT): $(GO) $(GOMODULES) $(ALL_GO_FILES) $(GOTESTSUM)
$(call infoMsg,Running go tests for monetr REST API)
$(GO) run $(MONETR_CLI_PACKAGE) database migrate -d $(POSTGRES_DB) -U $(POSTGRES_USER) -H $(POSTGRES_HOST)
$(GOTESTSUM) --junitfile $(PWD)/rest-api-junit.xml \
--jsonfile $(PWD)/rest-api-tests.json \
--format testname -- $(TEST_FLAGS) \
-coverprofile=$(COVERAGE_TXT) \
-coverpkg=./... \
-coverprofile=$(COVERAGE_TXT).tmp \
-covermode=atomic $(GO_SRC_DIR)/...
cat $(COVERAGE_TXT).tmp | grep -v "mockgen" > $(COVERAGE_TXT) && rm $(COVERAGE_TXT).tmp
$(GO) tool cover -func=$(COVERAGE_TXT)

test-go: $(COVERAGE_TXT)

coverage-go: $(COVERAGE_TXT)
$(GO) tool cover -html=$(COVERAGE_TXT)

ifdef CI
EXTRA_JEST_TEST=--reporters='github-actions'
endif
Expand Down
101 changes: 84 additions & 17 deletions pkg/controller/funding_schedules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ func TestPostFundingSchedules(t *testing.T) {

response.Status(http.StatusOK)
response.JSON().Path("$.fundingScheduleId").Number().Gt(0)
response.JSON().Path("$.bankAccountId").Number().Equal(bank.BankAccountId)
response.JSON().Path("$.nextOccurrence").String().DateTime(time.RFC3339).Gt(time.Now())
response.JSON().Path("$.excludeWeekends").Boolean().True()
response.JSON().Path("$.bankAccountId").Number().IsEqual(bank.BankAccountId)
response.JSON().Path("$.nextOccurrence").String().AsDateTime(time.RFC3339).Gt(time.Now())
response.JSON().Path("$.excludeWeekends").Boolean().IsTrue()
})

t.Run("create a funding schedule that respects the provided next occurrence", func(t *testing.T) {
Expand Down Expand Up @@ -89,10 +89,10 @@ func TestPostFundingSchedules(t *testing.T) {

response.Status(http.StatusOK)
response.JSON().Path("$.fundingScheduleId").Number().Gt(0)
response.JSON().Path("$.bankAccountId").Number().Equal(bank.BankAccountId)
response.JSON().Path("$.nextOccurrence").String().DateTime(time.RFC3339).Gt(time.Now())
response.JSON().Path("$.nextOccurrence").String().DateTime(time.RFC3339).Equal(nextFriday)
response.JSON().Path("$.excludeWeekends").Boolean().False()
response.JSON().Path("$.bankAccountId").Number().IsEqual(bank.BankAccountId)
response.JSON().Path("$.nextOccurrence").String().AsDateTime(time.RFC3339).Gt(time.Now())
response.JSON().Path("$.nextOccurrence").String().AsDateTime(time.RFC3339).IsEqual(nextFriday)
response.JSON().Path("$.excludeWeekends").Boolean().IsFalse()
})

t.Run("cannot create a duplicate name", func(t *testing.T) {
Expand All @@ -115,9 +115,9 @@ func TestPostFundingSchedules(t *testing.T) {

response.Status(http.StatusOK)
response.JSON().Path("$.fundingScheduleId").Number().Gt(0)
response.JSON().Path("$.bankAccountId").Number().Equal(bank.BankAccountId)
response.JSON().Path("$.nextOccurrence").String().DateTime(time.RFC3339).Gt(time.Now())
response.JSON().Path("$.excludeWeekends").Boolean().False()
response.JSON().Path("$.bankAccountId").Number().IsEqual(bank.BankAccountId)
response.JSON().Path("$.nextOccurrence").String().AsDateTime(time.RFC3339).Gt(time.Now())
response.JSON().Path("$.excludeWeekends").Boolean().IsFalse()
}

{ // Then try to create another one with the same name.
Expand All @@ -132,7 +132,7 @@ func TestPostFundingSchedules(t *testing.T) {
Expect()

response.Status(http.StatusBadRequest)
response.JSON().Path("$.error").Equal("failed to create funding schedule: a similar object already exists")
response.JSON().Path("$.error").IsEqual("failed to create funding schedule: a similar object already exists")
}
})

Expand All @@ -152,7 +152,7 @@ func TestPostFundingSchedules(t *testing.T) {
Expect()

response.Status(http.StatusBadRequest)
response.JSON().Path("$.error").Equal("funding schedule must have a name")
response.JSON().Path("$.error").IsEqual("funding schedule must have a name")
})

t.Run("requires a valid bank account Id", func(t *testing.T) {
Expand All @@ -170,7 +170,7 @@ func TestPostFundingSchedules(t *testing.T) {
Expect()

response.Status(http.StatusBadRequest)
response.JSON().Path("$.error").Equal("must specify a valid bank account Id")
response.JSON().Path("$.error").IsEqual("must specify a valid bank account Id")
})

t.Run("invalid json", func(t *testing.T) {
Expand All @@ -187,7 +187,7 @@ func TestPostFundingSchedules(t *testing.T) {
Expect()

response.Status(http.StatusBadRequest)
response.JSON().Path("$.error").Equal("invalid JSON body")
response.JSON().Path("$.error").IsEqual("invalid JSON body")
})
}

Expand All @@ -210,7 +210,7 @@ func TestPutFundingSchedules(t *testing.T) {
Expect()

response.Status(http.StatusOK)
response.JSON().Path("$.name").Equal(fundingSchedule.Name)
response.JSON().Path("$.name").IsEqual(fundingSchedule.Name)
})
}

Expand All @@ -230,7 +230,74 @@ func TestDeleteFundingSchedules(t *testing.T) {
Expect()

response.Status(http.StatusOK)
response.Body().Empty()
response.Body().IsEmpty()
})

t.Run("funding schedule is in use", func(t *testing.T) {
e := NewTestApplication(t)
user, password := fixtures.GivenIHaveABasicAccount(t)
link := fixtures.GivenIHaveAManualLink(t, user)
bank := fixtures.GivenIHaveABankAccount(t, &link, models.DepositoryBankAccountType, models.CheckingBankAccountSubType)
token := GivenILogin(t, e, user.Login.Email, password)

var fundingScheduleId uint64
{ // Create the funding schedule
response := e.POST("/api/bank_accounts/{bankAccountId}/funding_schedules").
WithPath("bankAccountId", bank.BankAccountId).
WithCookie(TestCookieName, token).
WithJSON(map[string]interface{}{
"name": "Payday",
"description": "15th and the Last day of every month",
"rule": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=15,-1",
"excludeWeekends": true,
}).
Expect()

response.Status(http.StatusOK)
response.JSON().Path("$.bankAccountId").Number().IsEqual(bank.BankAccountId)
response.JSON().Path("$.fundingScheduleId").Number().Gt(0)
fundingScheduleId = uint64(response.JSON().Path("$.fundingScheduleId").Number().Raw())
assert.NotZero(t, fundingScheduleId, "must be able to extract the funding schedule ID")
}

{ // Create an expense
now := time.Now()
timezone := testutils.MustEz(t, user.Account.GetTimezone)
rule := testutils.Must(t, models.NewRule, "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=1")
rule.DTStart(util.Midnight(now, timezone)) // Force the Rule to be in the correct TZ.
nextRecurrence := rule.After(now, false)
assert.Greater(t, nextRecurrence, now, "first of the next month should be relative to now")
nextRecurrence = util.Midnight(nextRecurrence, timezone)

response := e.POST("/api/bank_accounts/{bankAccountId}/spending").
WithPath("bankAccountId", bank.BankAccountId).
WithCookie(TestCookieName, token).
WithJSON(map[string]interface{}{
"name": "Some Monthly Expense",
"recurrenceRule": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=1",
"fundingScheduleId": fundingScheduleId,
"targetAmount": 1000,
"spendingType": models.SpendingTypeExpense,
"nextRecurrence": nextRecurrence,
}).
Expect()

response.Status(http.StatusOK)
response.JSON().Path("$.bankAccountId").Number().IsEqual(bank.BankAccountId)
response.JSON().Path("$.fundingScheduleId").Number().IsEqual(fundingScheduleId)
response.JSON().Path("$.nextRecurrence").String().AsDateTime(time.RFC3339).IsEqual(nextRecurrence)
}

{ // Then try to delete the funding schedule
response := e.DELETE("/api/bank_accounts/{bankAccountId}/funding_schedules/{fundingScheduleId}").
WithPath("bankAccountId", bank.BankAccountId).
WithPath("fundingScheduleId", fundingScheduleId).
WithCookie(TestCookieName, token).
Expect()

response.Status(http.StatusBadRequest)
response.JSON().Path("$.error").String().IsEqual("Cannot delete a funding schedule with goals or expenses associated with it")
}
})

t.Run("funding schedule does not exist", func(t *testing.T) {
Expand All @@ -247,6 +314,6 @@ func TestDeleteFundingSchedules(t *testing.T) {
Expect()

response.Status(http.StatusNotFound)
response.JSON().Path("$.error").String().Equal("cannot remove funding schedule, it does not exist")
response.JSON().Path("$.error").String().IsEqual("cannot remove funding schedule, it does not exist")
})
}
24 changes: 11 additions & 13 deletions pkg/controller/spending.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,27 +131,25 @@ func (c *Controller) postSpending(ctx echo.Context) error {

spending.LastRecurrence = nil

var next time.Time
// Once we know that the next recurrence is not in the past we can just store it here;
// itll be sanitized and converted to midnight below.
next := spending.NextRecurrence
if next.Before(time.Now()) {
requestSpan.Status = sentry.SpanStatusInvalidArgument
return c.badRequest(ctx, "next due date cannot be in the past")
}

switch spending.SpendingType {
case models.SpendingTypeExpense:
next = spending.NextRecurrence
// Once we know that the next recurrence is not in the past we can just store it here;
// itll be sanitized and converted to midnight below.
if next.Before(time.Now()) {
if spending.RecurrenceRule == nil {
requestSpan.Status = sentry.SpanStatusInvalidArgument
return c.badRequest(ctx, "next due date cannot be inthe past")
return c.badRequest(ctx, "recurrence rule must be specified for expenses")
}
case models.SpendingTypeGoal:
// If the spending is a goal, then we don't need the rule at all.
next = spending.NextRecurrence
if next.Before(time.Now()) {
if spending.RecurrenceRule != nil {
requestSpan.Status = sentry.SpanStatusInvalidArgument
return c.badRequest(ctx, "due date cannot be in the past")
return c.badRequest(ctx, "recurrence rule cannot be specified for goals")
}

// Goals do not recur.
spending.RecurrenceRule = nil
}

// Make sure that the next recurrence date is properly in the user's timezone.
Expand Down
Loading

0 comments on commit a2a574d

Please sign in to comment.