public void MonthlySummaryStaticAmounts(int[] years, int[] months)
        {
            const int totalMonths = 6;

            Assert.Equal(totalMonths, years.Length);
            Assert.Equal(totalMonths, months.Length);

            // Arrange: Fixed amounts.
            long[][] allAmounts = new long[][]
            {
                new long[] { 1000, 100, -200, -100 },
                new long[] { 2000, 200, -300, -150 },
                new long[] { 3000, 300, -400, -200 },
                new long[] { 0, -100, -220 },
                new long[] { 1000, 200 },
                new long[] { 0, 0, 0, 0 }
            };

            long[] ExpectedIncomePerMonth = new long[]
            {
                1100, 2200, 3300, 0, 1200, 0
            };

            long[] ExpectedExpensesPerMonth = new long[]
            {
                -300, -450, -600, -320, 0, 0
            };

            // Create the transaction with the fixed amounts.
            IEnumerable <Transaction> allTransactions = new List <Transaction>();

            for (int i = 0; i < totalMonths; i++)
            {
                long[] amounts = allAmounts[i];
                IEnumerable <Transaction> transactions = CreateTransactionsInMonth(amounts, years[i], months[i]);
                allTransactions = allTransactions.Concat(transactions);
            }

            // Randomize the order of the transactions in the list.
            Random random = new Random();

            allTransactions = allTransactions.OrderBy(x => random.Next()).ToList();

            // Act
            SummaryByTimeCategorizer categorizer = new SummaryByTimeCategorizer();
            var summaryBuckets = categorizer.CategorizeByYearAndMonth(allTransactions as IList <Transaction>);

            IList <YearMonthKey> monthKeys = new List <YearMonthKey>();

            for (int i = 0; i < totalMonths; i++)
            {
                YearMonthKey monthKey = new YearMonthKey(years[i], months[i]);
                monthKeys.Add(monthKey);

                // Verify the summary contains the month.
                Assert.True(summaryBuckets.ContainsKey(monthKey), monthKey.ToString());
                var monthSummary = summaryBuckets[monthKey];

                // Verify the summary contains the expected amount for the month.
                Assert.Equal(ExpectedIncomePerMonth[i], monthSummary.Income);
                Assert.Equal(ExpectedExpensesPerMonth[i], monthSummary.Expenses);
            }

            // Verify the summary doesn't contain another month that was not in the list.
            foreach (var monthKey in summaryBuckets.Keys)
            {
                Assert.True(monthKeys.Contains(monthKey), monthKey.ToString());
            }
        }
        public void MonthlySummaryWithRandomAmounts(int[] monthsToExclude)
        {
            const int year = 2016;
            int       minNumberOfTransaction = 1;
            int       maxNumberOfTransaction = 100;
            int       minAmount = 0;
            int       maxamount = 1000000;

            // Arrange: Create income and expenses transactions.
            IDictionary <YearMonthKey, long> ExpectedIncomePerMonth   = new Dictionary <YearMonthKey, long>();
            IDictionary <YearMonthKey, long> ExpectedExpensesPerMonth = new Dictionary <YearMonthKey, long>();

            IEnumerable <Transaction> allIncome = CreateRandomTransactions(
                year,
                minNumberOfTransaction,
                maxNumberOfTransaction,
                minAmount,
                maxamount,
                positiveAmounts: true,
                monthsToExclude: monthsToExclude,
                TotalAmounts: ExpectedIncomePerMonth);

            IEnumerable <Transaction> allExpenses = CreateRandomTransactions(
                year,
                minNumberOfTransaction,
                maxNumberOfTransaction,
                minAmount,
                maxamount,
                positiveAmounts: false,
                monthsToExclude: monthsToExclude,
                TotalAmounts: ExpectedExpensesPerMonth);

            IEnumerable <Transaction> allTransactions = allIncome.Concat(allExpenses);

            // Randomize the order of the transactions in the list.
            Random random = new Random();

            allTransactions = allTransactions.OrderBy(x => random.Next()).ToList();

            // Act
            SummaryByTimeCategorizer categorizer = new SummaryByTimeCategorizer();
            var summaryBuckets = categorizer.CategorizeByYearAndMonth(allTransactions as IList <Transaction>);

            // Assert
            Assert.NotNull(summaryBuckets);
            foreach (var kvp in ExpectedIncomePerMonth)
            {
                var  monthKey         = kvp.Key;
                long expectedIncome   = kvp.Value;
                long expectedExpenses = ExpectedExpensesPerMonth[monthKey];

                // Verify the month is included in the summary.
                Assert.True(summaryBuckets.ContainsKey(monthKey));
                var monthSummary = summaryBuckets[monthKey];
                Assert.NotNull(monthSummary);

                // Verify it has the expected amounts.
                Assert.Equal(expectedIncome, monthSummary.Income);
                Assert.Equal(expectedExpenses, monthSummary.Expenses);
            }

            // Verify that the summary doesn't include months that were excluded.
            foreach (int month in monthsToExclude)
            {
                YearMonthKey monthKey = new YearMonthKey(year, month);
                Assert.False(summaryBuckets.ContainsKey(monthKey));
            }
        }