Skip to main content
Glama
transaction-grouper.test.ts6.56 kB
import { describe, it, expect, vi } from 'vitest'; import { TransactionGrouper } from './transaction-grouper.js'; import type { Transaction, CategoryGroupInfo } from '../types/domain.js'; describe('TransactionGrouper', () => { const grouper = new TransactionGrouper(); const mockGetCategoryName = vi.fn((categoryId: string) => { const names: Record<string, string> = { cat1: 'Food', cat2: 'Rent', cat3: 'Salary', cat4: 'Utilities', }; return names[categoryId] || 'Unknown Category'; }); const mockGetGroupInfo = vi.fn((categoryId: string): CategoryGroupInfo | undefined => { const groups: Record<string, CategoryGroupInfo> = { cat1: { id: 'g1', name: 'Living', isIncome: false, isSavingsOrInvestment: false, }, cat2: { id: 'g1', name: 'Living', isIncome: false, isSavingsOrInvestment: false, }, cat3: { id: 'g2', name: 'Income', isIncome: true, isSavingsOrInvestment: false, }, cat4: { id: 'g1', name: 'Living', isIncome: false, isSavingsOrInvestment: false, }, }; return groups[categoryId]; }); const mockTransactions: Transaction[] = [ { id: '1', account: 'acc1', date: '2023-01-01', amount: -100, category: 'cat1', }, { id: '2', account: 'acc1', date: '2023-01-02', amount: -50, category: 'cat1', }, { id: '3', account: 'acc1', date: '2023-01-03', amount: -800, category: 'cat2', }, { id: '4', account: 'acc1', date: '2023-01-04', amount: 2000, category: 'cat3', }, { id: '5', account: 'acc1', date: '2023-01-05', amount: -200, category: 'cat4', }, { id: '6', account: 'acc1', date: '2023-01-06', amount: 100 }, // No category ]; beforeEach(() => { vi.clearAllMocks(); }); it('should group transactions by category excluding income when includeIncome is false', () => { const result = grouper.groupByCategory(mockTransactions, mockGetCategoryName, mockGetGroupInfo, false); expect(result).toHaveProperty('cat1'); expect(result).toHaveProperty('cat2'); expect(result).toHaveProperty('cat4'); expect(result).not.toHaveProperty('cat3'); // Income excluded expect(result['cat1']).toEqual({ id: 'cat1', name: 'Food', group: 'Living', isIncome: false, total: -150, // -100 + -50 transactions: 2, }); expect(result['cat2']).toEqual({ id: 'cat2', name: 'Rent', group: 'Living', isIncome: false, total: -800, transactions: 1, }); expect(result['cat4']).toEqual({ id: 'cat4', name: 'Utilities', group: 'Living', isIncome: false, total: -200, transactions: 1, }); }); it('should group transactions by category including income when includeIncome is true', () => { const result = grouper.groupByCategory(mockTransactions, mockGetCategoryName, mockGetGroupInfo, true); expect(result).toHaveProperty('cat1'); expect(result).toHaveProperty('cat2'); expect(result).toHaveProperty('cat3'); // Income included expect(result).toHaveProperty('cat4'); expect(result['cat3']).toEqual({ id: 'cat3', name: 'Salary', group: 'Income', isIncome: true, total: 2000, transactions: 1, }); }); it('should skip transactions without category', () => { const transactionsWithoutCategory: Transaction[] = [ { id: '1', account: 'acc1', date: '2023-01-01', amount: -100 }, // No category { id: '2', account: 'acc1', date: '2023-01-02', amount: -50, category: 'cat1', }, ]; const result = grouper.groupByCategory(transactionsWithoutCategory, mockGetCategoryName, mockGetGroupInfo, false); expect(Object.keys(result)).toHaveLength(1); expect(result).toHaveProperty('cat1'); expect(result).not.toHaveProperty('undefined'); }); it('should handle unknown categories with default group', () => { const transactionsWithUnknownCategory: Transaction[] = [ { id: '1', account: 'acc1', date: '2023-01-01', amount: -100, category: 'unknown', }, ]; const result = grouper.groupByCategory( transactionsWithUnknownCategory, mockGetCategoryName, mockGetGroupInfo, false ); expect(result).toHaveProperty('unknown'); expect(result['unknown']).toEqual({ id: 'unknown', name: 'Unknown Category', group: 'Unknown Group', isIncome: false, total: -100, transactions: 1, }); }); it('should handle empty transactions array', () => { const result = grouper.groupByCategory([], mockGetCategoryName, mockGetGroupInfo, false); expect(result).toEqual({}); }); it('should accumulate multiple transactions for the same category', () => { const sameCategory: Transaction[] = [ { id: '1', account: 'acc1', date: '2023-01-01', amount: -100, category: 'cat1', }, { id: '2', account: 'acc1', date: '2023-01-02', amount: -50, category: 'cat1', }, { id: '3', account: 'acc1', date: '2023-01-03', amount: -25, category: 'cat1', }, ]; const result = grouper.groupByCategory(sameCategory, mockGetCategoryName, mockGetGroupInfo, false); expect(result['cat1']).toEqual({ id: 'cat1', name: 'Food', group: 'Living', isIncome: false, total: -175, // -100 + -50 + -25 transactions: 3, }); }); it('should handle positive transaction amounts correctly', () => { const positiveTransactions: Transaction[] = [ { id: '1', account: 'acc1', date: '2023-01-01', amount: 100, category: 'cat1', }, { id: '2', account: 'acc1', date: '2023-01-02', amount: 50, category: 'cat1', }, ]; const result = grouper.groupByCategory(positiveTransactions, mockGetCategoryName, mockGetGroupInfo, false); expect(result['cat1']).toEqual({ id: 'cat1', name: 'Food', group: 'Living', isIncome: false, total: 150, // 100 + 50 transactions: 2, }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/s-stefanov/actual-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server