Add Schaad.Accounting.Tests (xUnit + NSubstitute + Shouldly, net9.0) wired into Accounting.sln with a direct Reference to Schaad.Finance.Api.dll so it flows into the test binary. Naming convention: file/class is <Subject>TestShould, each test method is DoThisWhenThat. Reads as a sentence: "ViewService test should sum activa and passiva totals separately when getting balance view". All assertions use Shouldly (.ShouldBe, .ShouldBeNull, .ShouldContain, ...) rather than xUnit Assert.*. 38 tests across seven files: - ViewServiceTestShould: balance math (activa/passiva totals, per- account balance from start balance + credits - debits, FX conversion to CHF) and bank-transaction auto-matching (booking-rule text, value-matching preference, same-accounts-last-month fallback, open-transaction filter). - TransactionRepositoryTestShould: FX round-trip against a temp XML directory, unknown-id -> null, and the mutation-on-read regression from PR A (a second Get on the same FX transaction used to divide by FxRate again). - FormattingTestShould: Swiss thousands separator, two-decimal rounding, culture independence. - RepositoryCacheTestShould: loader called once, per-key isolation, invalidation forces reload, case-insensitive keys. - AccountRepositoryTestShould: id assignment, in-place update, currency defaulting, delete, bank-account lookup, bank-balance update, and constructor re-run against an existing file. - ChartServiceTestShould: honours settingsService.GetYear() without mutating it (locks in PR E), account vs sub-class grouping heuristic, skips empty accounts. - FileServiceTestShould: CSV header + running balance for debit and credit lines, ordering by BookingDate/ValueDate/Value, no in-place value mutation (locks in PR A). CLAUDE.md gains a `dotnet test` line. IMPROVEMENT_PLAN.md notes that this landed as three PRs (G, H, I) and was squashed on request. Also add `*.DotSettings.user` to .gitignore so Rider's per-user solution settings don't get accidentally staged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
58 lines
1.6 KiB
C#
58 lines
1.6 KiB
C#
using Schaad.Accounting.Repositories;
|
|
using Shouldly;
|
|
|
|
namespace Schaad.Accounting.Tests;
|
|
|
|
public class RepositoryCacheTestShould
|
|
{
|
|
[Fact]
|
|
public void CallLoaderOnceWhenKeyIsRequestedRepeatedly()
|
|
{
|
|
var cache = new RepositoryCache();
|
|
var loaderCalls = 0;
|
|
|
|
var first = cache.GetOrLoad("key", () => { loaderCalls++; return new List<int> { 1, 2 }; });
|
|
var second = cache.GetOrLoad("key", () => { loaderCalls++; return new List<int> { 99 }; });
|
|
|
|
loaderCalls.ShouldBe(1);
|
|
second.ShouldBeSameAs(first);
|
|
second.ShouldBe(new[] { 1, 2 });
|
|
}
|
|
|
|
[Fact]
|
|
public void KeepEntriesSeparateWhenKeysDiffer()
|
|
{
|
|
var cache = new RepositoryCache();
|
|
|
|
var a = cache.GetOrLoad("a", () => new List<int> { 1 });
|
|
var b = cache.GetOrLoad("b", () => new List<int> { 2 });
|
|
|
|
b.ShouldNotBeSameAs(a);
|
|
a.ShouldBe(new[] { 1 });
|
|
b.ShouldBe(new[] { 2 });
|
|
}
|
|
|
|
[Fact]
|
|
public void ReloadFromLoaderWhenKeyIsInvalidated()
|
|
{
|
|
var cache = new RepositoryCache();
|
|
var loaderCalls = 0;
|
|
|
|
cache.GetOrLoad("k", () => { loaderCalls++; return new List<int> { 1 }; });
|
|
cache.Invalidate("k");
|
|
cache.GetOrLoad("k", () => { loaderCalls++; return new List<int> { 2 }; });
|
|
|
|
loaderCalls.ShouldBe(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void MatchKeysCaseInsensitivelyWhenLookingUp()
|
|
{
|
|
var cache = new RepositoryCache();
|
|
|
|
cache.GetOrLoad("Path/To/File.xml", () => new List<int> { 1 });
|
|
var upper = cache.GetOrLoad("PATH/TO/FILE.XML", () => new List<int> { 99 });
|
|
|
|
upper.ShouldBe(new[] { 1 });
|
|
}
|
|
}
|