Skip to content
Snippets Groups Projects
Unverified Commit 81f59ff7 authored by Alex Walker's avatar Alex Walker Committed by GitHub
Browse files

Add unit tests for goal template types (#3183)

* Add unit tests for each goal type

* Add test for goals schedule template

* Update release notes
parent 63d9547e
No related branches found
No related tags found
No related merge requests found
import * as actions from '../actions';
import { goalsAverage } from './goalsAverage';
jest.mock('../actions');
describe('goalsAverage', () => {
const mockGetSheetValue = actions.getSheetValue as jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
});
it('should calculate the amount to budget based on the average of only one previous month of spending', async () => {
// Given
const template = { amount: 1 };
const month = '2024-07';
const category = { id: 1 };
const errors: string[] = [];
const to_budget = 0;
mockGetSheetValue.mockResolvedValueOnce(200);
// When
const result = await goalsAverage(
template,
month,
category,
errors,
to_budget,
);
// Then
expect(result.to_budget).toBe(-200);
expect(result.errors).toHaveLength(0);
});
it('should calculate the amount to budget based on the average of multiple previous months of spending', async () => {
// Given
const template = { amount: 4 };
const month = '2024-08';
const category = { id: 1 };
const errors: string[] = [];
const to_budget = 0;
mockGetSheetValue
.mockResolvedValueOnce(200)
.mockResolvedValueOnce(300)
.mockResolvedValueOnce(100)
.mockResolvedValueOnce(400);
// When
const result = await goalsAverage(
template,
month,
category,
errors,
to_budget,
);
// Then
expect(result.to_budget).toBe(-250);
expect(result.errors).toHaveLength(0);
});
it('should return error when template amount passed in is <= 0', async () => {
// Given
const template = { amount: 0 };
const month = '2024-08';
const category = { id: 1 };
const errors: string[] = [];
const to_budget = 1000;
// When
const result = await goalsAverage(
template,
month,
category,
errors,
to_budget,
);
// Then
expect(result.to_budget).toBe(1000);
expect(result.errors).toStrictEqual([
'Number of months to average is not valid',
]);
});
});
import * as actions from '../actions';
import { goalsBy } from './goalsBy';
jest.mock('../actions');
describe('goalsBy', () => {
const mockIsReflectBudget = actions.isReflectBudget as jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
});
it('should return correct budget amount with target in the future and no current balance', async () => {
// Given
const template = { amount: 100, month: '2024-12' };
const current_month = '2024-08';
const last_month_balance = 0;
const to_budget = 0;
const errors: string[] = [];
const template_lines = [template];
const l = 0;
const remainder = 0;
mockIsReflectBudget.mockReturnValue(false);
// When
const result = await goalsBy(
template_lines,
current_month,
template,
l,
remainder,
last_month_balance,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(2000);
expect(result.errors).toHaveLength(0);
expect(result.remainder).toBe(0);
});
it('should return correct budget amount with target in the future and existing balance towards goal', async () => {
// Given
const template = { amount: 100, month: '2024-12' };
const current_month = '2024-08';
const last_month_balance = 5000;
const to_budget = 0;
const errors: string[] = [];
const template_lines = [template];
const l = 0;
const remainder = 0;
mockIsReflectBudget.mockReturnValue(false);
// When
const result = await goalsBy(
template_lines,
current_month,
template,
l,
remainder,
last_month_balance,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(1000);
expect(result.errors).toHaveLength(0);
expect(result.remainder).toBe(0);
});
it('should return correct budget amount when target balance met early', async () => {
// Given
const template = { amount: 100, month: '2024-12' };
const current_month = '2024-08';
const last_month_balance = 10000;
const to_budget = 0;
const errors: string[] = [];
const template_lines = [template];
const l = 0;
const remainder = 0;
mockIsReflectBudget.mockReturnValue(false);
// When
const result = await goalsBy(
template_lines,
current_month,
template,
l,
remainder,
last_month_balance,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(0);
expect(result.errors).toHaveLength(0);
expect(result.remainder).toBe(0);
});
it('should return error when budget is a reflect budget', async () => {
// Given
const template = { amount: -100, month: '2024-08', repeat: 1 };
const current_month = '2024-08';
const last_month_balance = 0;
const to_budget = 0;
const errors: string[] = [];
const template_lines = [template];
const l = 0;
const remainder = 0;
mockIsReflectBudget.mockReturnValue(true);
// When
const result = await goalsBy(
template_lines,
current_month,
template,
l,
remainder,
last_month_balance,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(0);
expect(result.errors).toStrictEqual([
'by templates are not supported in Report budgets',
]);
});
});
import * as db from '../../db';
import * as actions from '../actions';
import { goalsPercentage } from './goalsPercentage';
jest.mock('../actions');
jest.mock('../../db');
describe('goalsPercentage', () => {
const mockGetSheetValue = actions.getSheetValue as jest.Mock;
const mockGetCategories = db.getCategories as jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
});
it('should calculate the budget based on a percentage of all income for the current month', async () => {
// Given
const template = { percent: 10, category: 'all income' };
const month = '2024-08';
const available_start = 0;
const sheetName = '2024-08';
const to_budget = 0;
const errors: string[] = [];
mockGetSheetValue.mockResolvedValueOnce(1000);
// When
const result = await goalsPercentage(
template,
month,
available_start,
sheetName,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(100);
expect(result.errors).toHaveLength(0);
});
it('should calculate the budget based on a percentage of all income for the previous month', async () => {
// Given
const template = { percent: 10, category: 'all income', previous: true };
const month = '2024-08';
const available_start = 0;
const sheetName = '2024-08';
const to_budget = 0;
const errors: string[] = [];
mockGetSheetValue.mockResolvedValueOnce(1000);
// When
const result = await goalsPercentage(
template,
month,
available_start,
sheetName,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(100);
expect(result.errors).toHaveLength(0);
});
it('should calculate the budget based on a percentage of available funds', async () => {
// Given
const template = { percent: 10, category: 'available funds' };
const month = '2024-08';
const available_start = 1000;
const sheetName = '2024-08';
const to_budget = 0;
const errors: string[] = [];
// When
const result = await goalsPercentage(
template,
month,
available_start,
sheetName,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(100);
expect(result.errors).toHaveLength(0);
});
it('should calculate the budget based on a percentage of a specific income category for the current month', async () => {
// Given
const template = { percent: 10, category: 'Salary' };
const month = '2024-08';
const available_start = 0;
const sheetName = '2024-08';
const to_budget = 0;
const errors: string[] = [];
mockGetCategories.mockResolvedValueOnce([
{ id: 1, name: 'Salary', is_income: true },
]);
mockGetSheetValue.mockResolvedValueOnce(1000);
// When
const result = await goalsPercentage(
template,
month,
available_start,
sheetName,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(100);
expect(result.errors).toHaveLength(0);
});
it('should calculate the budget based on a percentage of a specific income category for the previous month', async () => {
// Given
const template = { percent: 10, category: 'Salary', previous: true };
const month = '2024-08';
const available_start = 0;
const sheetName = '2024-08';
const to_budget = 0;
const errors: string[] = [];
mockGetCategories.mockResolvedValueOnce([
{ id: 1, name: 'Salary', is_income: true },
]);
mockGetSheetValue.mockResolvedValueOnce(1000);
// When
const result = await goalsPercentage(
template,
month,
available_start,
sheetName,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(100);
expect(result.errors).toHaveLength(0);
});
it('should return an error if the specified income category does not exist', async () => {
// Given
const template = { percent: 10, category: 'NonExistentCategory' };
const month = '2024-08';
const available_start = 0;
const sheetName = '2024-08';
const to_budget = 0;
const errors: string[] = [];
mockGetCategories.mockResolvedValueOnce([
{ id: 1, name: 'Salary', is_income: true },
]);
// When
const result = await goalsPercentage(
template,
month,
available_start,
sheetName,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(0);
expect(result.errors).toStrictEqual([
'Could not find category “NonExistentCategory”',
]);
});
});
import { goalsRemainder } from './goalsRemainder';
describe('goalsRemainder', () => {
it('should calculate the budget correctly when remainder_scale is greater than 0', async () => {
// Given
const template = { weight: 100 };
const budgetAvailable = 1000;
const remainder_scale = 0.5;
const to_budget = 0;
// When
const result = await goalsRemainder(
template,
budgetAvailable,
remainder_scale,
to_budget,
);
// Then
expect(result.to_budget).toBe(50);
});
it('should calculate the budget correctly when remainder_scale is 0', async () => {
// Given
const template = { weight: 100 };
const budgetAvailable = 1000;
const remainder_scale = 0;
const to_budget = 0;
// When
const result = await goalsRemainder(
template,
budgetAvailable,
remainder_scale,
to_budget,
);
// Then
expect(result.to_budget).toBe(100);
});
it('should calculate the budget correctly when when the calculated budget exceeds the budget available', async () => {
// Given
const template = { weight: 1000 };
const budgetAvailable = 500;
const remainder_scale = 1;
const to_budget = 0;
// When
const result = await goalsRemainder(
template,
budgetAvailable,
remainder_scale,
to_budget,
);
// Then
expect(result.to_budget).toBe(500);
});
it('should calculate the budget correctly when there is 1 minor unit leftover from rounding', async () => {
// Given
const template = { weight: 499 };
const budgetAvailable = 500;
const remainder_scale = 1;
const to_budget = 0;
// When
const result = await goalsRemainder(
template,
budgetAvailable,
remainder_scale,
to_budget,
);
// Then
expect(result.to_budget).toBe(500);
});
});
import * as db from '../../db';
import { getRuleForSchedule } from '../../schedules/app';
import { isReflectBudget } from '../actions';
import { goalsSchedule } from './goalsSchedule';
jest.mock('../../db');
jest.mock('../actions');
jest.mock('../../schedules/app', () => {
const actualModule = jest.requireActual('../../schedules/app');
return {
...actualModule,
getRuleForSchedule: jest.fn(),
};
});
describe('goalsSchedule', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should return correct budget when recurring schedule set', async () => {
// Given
const scheduleFlag = false;
const template_lines = [{ type: 'schedule', name: 'Test Schedule' }];
const current_month = '2024-08-01';
const balance = 0;
const remainder = 0;
const last_month_balance = 0;
const to_budget = 0;
const errors: string[] = [];
const category = { id: 1, name: 'Test Category' };
(db.first as jest.Mock).mockResolvedValue({ id: 1, completed: 0 });
(getRuleForSchedule as jest.Mock).mockResolvedValue({
serialize: () => ({
conditions: [
{
op: 'is',
field: 'date',
value: {
start: '2024-08-01',
interval: 1,
frequency: 'monthly',
patterns: [],
skipWeekend: false,
weekendSolveMode: 'before',
endMode: 'never',
endOccurrences: 1,
endDate: '2024-08-04',
},
type: 'date',
},
{
op: 'is',
field: 'amount',
value: -10000,
type: 'number',
},
],
}),
execActions: () => ({ amount: -10000, subtransactions: [] }),
});
(isReflectBudget as jest.Mock).mockReturnValue(false);
// When
const result = await goalsSchedule(
scheduleFlag,
template_lines,
current_month,
balance,
remainder,
last_month_balance,
to_budget,
errors,
category,
);
// Then
expect(result.to_budget).toBe(10000);
expect(result.errors).toHaveLength(0);
expect(result.remainder).toBe(0);
expect(result.scheduleFlag).toBe(true);
});
});
import { goalsSimple } from './goalsSimple';
describe('goalsSimple', () => {
it('should return correct budget amount when limit set and no balance left from previous months', async () => {
// Given
const template = { limit: { amount: 100, hold: false } };
const limitCheck = false;
const errors: string[] = [];
const limit = 0;
const hold = false;
const to_budget = 0;
const last_month_balance = 0;
// When
const result = await goalsSimple(
template,
limitCheck,
errors,
limit,
hold,
to_budget,
last_month_balance,
);
// Then
expect(result.to_budget).toBe(10000);
expect(result.errors).toHaveLength(0);
expect(result.limitCheck).toBe(true);
expect(result.limit).toBe(10000);
expect(result.hold).toBe(false);
});
it('should return correct budget amount when limit set and balance from previous month left over', async () => {
// Given
const template = { limit: { amount: 100, hold: false } };
const limitCheck = false;
const errors: string[] = [];
const limit = 0;
const hold = false;
const to_budget = 0;
const last_month_balance = 2000;
// When
const result = await goalsSimple(
template,
limitCheck,
errors,
limit,
hold,
to_budget,
last_month_balance,
);
// Then
expect(result.to_budget).toBe(8000);
expect(result.errors).toHaveLength(0);
expect(result.limitCheck).toBe(true);
expect(result.limit).toBe(10000);
});
it('should return correct budget amount when assigned from previous month is greater than the limit set', async () => {
// Given
const template = { limit: { amount: 100, hold: false } };
const limitCheck = false;
const errors: string[] = [];
const limit = 0;
const hold = false;
const to_budget = 0;
const last_month_balance = 20000;
// When
const result = await goalsSimple(
template,
limitCheck,
errors,
limit,
hold,
to_budget,
last_month_balance,
);
// Then
expect(result.to_budget).toBe(-10000);
expect(result.errors).toHaveLength(0);
expect(result.limitCheck).toBe(true);
expect(result.limit).toBe(10000);
expect(result.hold).toBe(false);
});
it('should return correct budget amount when both limit and monthly limit set', async () => {
// Given
const template = { monthly: 50, limit: { amount: 100, hold: false } };
const limitCheck = false;
const errors: string[] = [];
const limit = 0;
const hold = false;
const to_budget = 0;
const last_month_balance = 0;
// When
const result = await goalsSimple(
template,
limitCheck,
errors,
limit,
hold,
to_budget,
last_month_balance,
);
// Then
expect(result.to_budget).toBe(5000);
expect(result.errors).toHaveLength(0);
expect(result.limitCheck).toBe(true);
expect(result.limit).toBe(10000);
expect(result.hold).toBe(false);
});
it('should fail when multiple limit checks exist', async () => {
// Given
const template = { limit: { amount: 100, hold: true } };
const limitCheck = true;
const errors: string[] = [];
const limit = 0;
const hold = true;
const to_budget = 0;
const last_month_balance = 200;
// When
const result = await goalsSimple(
template,
limitCheck,
errors,
limit,
hold,
to_budget,
last_month_balance,
);
// Then
expect(result.to_budget).toBe(0);
expect(result.errors).toStrictEqual(['More than one “up to” limit found.']);
expect(result.limitCheck).toBe(true);
expect(result.limit).toBe(0);
expect(result.hold).toBe(true);
});
});
import { getSheetValue } from '../actions';
import { goalsSpend } from './goalsSpend';
jest.mock('../actions');
describe('goalsSpend', () => {
const mockGetSheetValue = getSheetValue as jest.Mock;
beforeEach(() => {
jest.clearAllMocks();
});
it('should return correct budget amount for range when no spending has happened', async () => {
// Given
const template = { amount: 60, from: '2024-01', month: '2024-12' };
const last_month_balance = 0;
const current_month = '2024-08-01';
const to_budget = 0;
const errors: string[] = [];
const category = { id: 'uuid' };
mockGetSheetValue
.mockResolvedValueOnce(0)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500);
// When
const result = await goalsSpend(
template,
last_month_balance,
current_month,
to_budget,
errors,
category,
);
// Then
expect(result.to_budget).toBe(500);
expect(result.errors).toHaveLength(0);
});
it('should return correct budget amount for range when spending has happened', async () => {
// Given
const template = { amount: 60, from: '2024-01', month: '2024-12' };
const last_month_balance = 0;
const current_month = '2024-08-01';
const to_budget = 0;
const errors: string[] = [];
const category = { id: 'uuid' };
mockGetSheetValue
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500)
.mockResolvedValueOnce(500);
// When
const result = await goalsSpend(
template,
last_month_balance,
current_month,
to_budget,
errors,
category,
);
// Then
expect(result.to_budget).toBe(600);
expect(result.errors).toHaveLength(0);
});
it('should return error when range is in the past', async () => {
// Given
const template = { amount: 60, from: '2024-01', month: '2024-05' };
const last_month_balance = 0;
const current_month = '2024-08-01';
const to_budget = 0;
const errors: string[] = [];
const category = { id: 'uuid' };
// When
const result = await goalsSpend(
template,
last_month_balance,
current_month,
to_budget,
errors,
category,
);
// Then
expect(result.to_budget).toBe(0);
expect(result.errors).toStrictEqual(['2024-05 is in the past.']);
});
});
import { goalsWeek } from './goalsWeek';
describe('goalsWeek', () => {
it('should return the correct budget amount for a weekly repeat', async () => {
// Given
const template = { amount: 100, starting: '2024-08-01', weeks: 1 };
const limit = 0;
const limitCheck = false;
const hold = false;
const current_month = '2024-08-01';
const to_budget = 0;
const errors: string[] = [];
// When
const result = await goalsWeek(
template,
limit,
limitCheck,
hold,
current_month,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(50000);
expect(result.errors).toHaveLength(0);
expect(result.limit).toBe(0);
expect(result.limitCheck).toBe(false);
expect(result.hold).toBe(false);
});
it('should return the correct budget amount for a bi-weekly repeat', async () => {
// Given
const template = { amount: '100', starting: '2024-08-01', weeks: 2 };
const limit = 0;
const limitCheck = false;
const hold = false;
const current_month = '2024-08-01';
const to_budget = 0;
const errors: string[] = [];
// When
const result = await goalsWeek(
template,
limit,
limitCheck,
hold,
current_month,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(30000);
expect(result.errors).toHaveLength(0);
expect(result.limit).toBe(0);
expect(result.limitCheck).toBe(false);
expect(result.hold).toBe(false);
});
it('should return the correct budget when limit set', async () => {
// Given
const template = { amount: 100, starting: '2024-08-01', weeks: 1 };
const limit = 20000;
const limitCheck = false;
const hold = false;
const current_month = '2024-08-01';
const to_budget = 0;
const errors: string[] = [];
// When
const result = await goalsWeek(
template,
limit,
limitCheck,
hold,
current_month,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(50000);
expect(result.errors).toHaveLength(0);
expect(result.limit).toBe(20000);
expect(result.limitCheck).toBe(false);
expect(result.hold).toBe(false);
});
it('should return error when multiple limit checks exist', async () => {
// Given
const template = {
amount: '100',
starting: '2024-08-01',
weeks: 1,
limit: { amount: 100, hold: true },
};
const limit = 1000;
const limitCheck = true;
const hold = false;
const current_month = '2024-08-01';
const to_budget = 0;
const errors: string[] = [];
// When
const result = await goalsWeek(
template,
limit,
limitCheck,
hold,
current_month,
to_budget,
errors,
);
// Then
expect(result.to_budget).toBe(0);
expect(result.errors).toStrictEqual(['More than one “up to” limit found.']);
expect(result.limit).toBe(1000);
expect(result.limitCheck).toBe(true);
expect(result.hold).toBe(false);
});
});
---
category: Maintenance
authors: [ ACWalker ]
---
Add unit tests for the existing goal template types.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment