FSCash/tests/FscashTest.php
fds2610-bot 803c1e5867 Feature: Zahlart-Filter im Personenreport (Issue #11)
Ergänzt rep-person.php um eine zweite Filtertabelle für Zahlweisen
neben der Kategorien-Auswahl; die Summen werden entsprechend beider
Filter berechnet. Der Link zum Dashboard gibt die gewählten Zahlweisen
als Vorauswahl weiter, analog zur bestehenden Kategorien-Vorauswahl.
2026-07-26 14:13:10 +02:00

333 lines
11 KiB
PHP

<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* Tests für die reinen Hilfsfunktionen in html/lib/fscash.php.
*
* Diese Funktionen bilden die wichtigste Geschäftslogik von FSCash ab:
* Monatsnavigation, Zugriffsrechte, Aktien-Gewinn/Verlust-Berechnung,
* Auswertung externer API-Antworten, Eingabevalidierung, CSV-Export und
* das Migrationssystem.
*/
final class FscashTest extends TestCase
{
// --- fscash_normalize_month_year ---------------------------------
public function testNormalizeMonthYearLaesstGueltigenMonatUnveraendert(): void
{
$this->assertSame(
['month' => 6, 'year' => 2026],
fscash_normalize_month_year(6, 2026)
);
}
public function testNormalizeMonthYearSpringtBeiUnterschreitungInsVorjahr(): void
{
$this->assertSame(
['month' => 12, 'year' => 2025],
fscash_normalize_month_year(0, 2026)
);
}
public function testNormalizeMonthYearSpringtBeiUeberschreitungInsFolgejahr(): void
{
$this->assertSame(
['month' => 1, 'year' => 2027],
fscash_normalize_month_year(13, 2026)
);
}
public function testNormalizeMonthYearGrenzwerteBleibenUnveraendert(): void
{
$this->assertSame(['month' => 1, 'year' => 2026], fscash_normalize_month_year(1, 2026));
$this->assertSame(['month' => 12, 'year' => 2026], fscash_normalize_month_year(12, 2026));
}
// --- fscash_user_roles --------------------------------------------
public function testUserRolesErkenntAdminUndAktienUser(): void
{
$this->assertSame(
['is_admin' => true, 'is_aktien' => true],
fscash_user_roles(1, [1, 2], [1, 2])
);
}
public function testUserRolesErkenntNormalenUserOhneRechte(): void
{
$this->assertSame(
['is_admin' => false, 'is_aktien' => false],
fscash_user_roles(99, [1, 2], [1, 2])
);
}
public function testUserRolesVergleichtStriktTypisiert(): void
{
// "1" (string) darf ein int 1 in der Liste nicht faelschlich matchen,
// da fscash_user_roles ausschliesslich mit int-typisierten IDs arbeitet
// und in_array(..., true) strikt vergleicht.
$this->assertSame(
['is_admin' => true, 'is_aktien' => false],
fscash_user_roles(2, [1, 2], [1])
);
}
// --- fscash_aktie_guv ------------------------------------------------
public function testAktieGuvBerechnetGewinn(): void
{
$result = fscash_aktie_guv(100.0, 10.0, 120.0);
$this->assertSame(1000.0, $result['kaufSumme']);
$this->assertSame(1200.0, $result['aktuellerWert']);
$this->assertSame(200.0, $result['guv']);
$this->assertSame('guv-positive', $result['guvClass']);
}
public function testAktieGuvBerechnetVerlust(): void
{
$result = fscash_aktie_guv(100.0, 10.0, 80.0);
$this->assertSame(-200.0, $result['guv']);
$this->assertSame('guv-negative', $result['guvClass']);
}
public function testAktieGuvBeiGleichbleibendemKursOhneKlasse(): void
{
$result = fscash_aktie_guv(50.0, 4.0, 50.0);
$this->assertSame(0.0, $result['guv']);
$this->assertSame('', $result['guvClass']);
}
public function testAktieGuvOhneKursErgibtNegativenGuvGleichKaufsumme(): void
{
// kurs = 0 (noch keine Kursaktualisierung erfolgt)
$result = fscash_aktie_guv(100.0, 3.0, 0.0);
$this->assertSame(300.0, $result['kaufSumme']);
$this->assertSame(0.0, $result['aktuellerWert']);
$this->assertSame(-300.0, $result['guv']);
$this->assertSame('guv-negative', $result['guvClass']);
}
// --- fscash_select_figi_symbol --------------------------------------
public function testSelectFigiSymbolBevorzugtXetraEintrag(): void
{
$entries = [
['ticker' => 'FOO', 'exchCode' => 'US'],
['ticker' => 'BAR', 'exchCode' => 'XETR'],
];
$this->assertSame('BAR.XETR', fscash_select_figi_symbol($entries));
}
public function testSelectFigiSymbolNimmtErstenEintragOhneXetra(): void
{
$entries = [
['ticker' => 'FOO', 'exchCode' => 'US'],
['ticker' => 'BAZ', 'exchCode' => 'LSE'],
];
$this->assertSame('FOO.US', fscash_select_figi_symbol($entries));
}
public function testSelectFigiSymbolOhneExchCode(): void
{
$entries = [
['ticker' => 'FOO'],
];
$this->assertSame('FOO', fscash_select_figi_symbol($entries));
}
public function testSelectFigiSymbolBeiLeeremTickerLiefertNull(): void
{
$entries = [
['ticker' => '', 'exchCode' => 'US'],
];
$this->assertNull(fscash_select_figi_symbol($entries));
}
public function testSelectFigiSymbolBeiLeererListeLiefertNull(): void
{
$this->assertNull(fscash_select_figi_symbol([]));
}
// --- fscash_resolve_selected_categories ------------------------------
public function testResolveSelectedCategoriesMitAuswahl(): void
{
$this->assertSame(
[2, 3],
fscash_resolve_selected_categories([1, 2, 3, 4], [2, 3])
);
}
public function testResolveSelectedCategoriesOhneAuswahlLiefertAlle(): void
{
$this->assertSame(
[1, 2, 3, 4],
fscash_resolve_selected_categories([1, 2, 3, 4], null)
);
}
public function testResolveSelectedCategoriesBeiLeeremArrayLiefertAlle(): void
{
// Ein leeres categories[]-Array (z.B. alle Haken entfernt) fällt
// bewusst auf "alle Kategorien" zurück, siehe rep-person.php.
$this->assertSame(
[1, 2, 3, 4],
fscash_resolve_selected_categories([1, 2, 3, 4], [])
);
}
// --- fscash_resolve_selected_payment_methods --------------------------
public function testResolveSelectedPaymentMethodsMitAuswahl(): void
{
$this->assertSame(
[2, 3],
fscash_resolve_selected_payment_methods([1, 2, 3, 4], [2, 3])
);
}
public function testResolveSelectedPaymentMethodsOhneAuswahlLiefertAlle(): void
{
$this->assertSame(
[1, 2, 3, 4],
fscash_resolve_selected_payment_methods([1, 2, 3, 4], null)
);
}
public function testResolveSelectedPaymentMethodsBeiLeeremArrayLiefertAlle(): void
{
$this->assertSame(
[1, 2, 3, 4],
fscash_resolve_selected_payment_methods([1, 2, 3, 4], [])
);
}
// --- fscash_extract_quote_price --------------------------------------
public function testExtractQuotePriceLiestPreisAus(): void
{
$response = ['Global Quote' => ['05. price' => '123.4500']];
$this->assertSame(123.45, fscash_extract_quote_price($response));
}
public function testExtractQuotePriceBeiFehlendemFeldLiefertNull(): void
{
$this->assertNull(fscash_extract_quote_price(['Global Quote' => []]));
$this->assertNull(fscash_extract_quote_price([]));
}
public function testExtractQuotePriceBeiLeeremStringLiefertNull(): void
{
$this->assertNull(fscash_extract_quote_price(['Global Quote' => ['05. price' => '']]));
}
// --- fscash_map_symbol_search_results ---------------------------------
public function testMapSymbolSearchResultsWandeltAlphaVantageFormatUm(): void
{
$response = [
'bestMatches' => [
['1. symbol' => 'SAP.DE', '2. name' => 'SAP SE', '4. region' => 'Frankfurt'],
],
];
$this->assertSame(
[['symbol' => 'SAP.DE', 'name' => 'SAP SE', 'region' => 'Frankfurt']],
fscash_map_symbol_search_results($response)
);
}
public function testMapSymbolSearchResultsOhneTrefferLiefertLeeresArray(): void
{
$this->assertSame([], fscash_map_symbol_search_results([]));
$this->assertSame([], fscash_map_symbol_search_results(['bestMatches' => []]));
}
// --- fscash_is_valid_symbol_payload -----------------------------------
public function testIsValidSymbolPayloadMitGueltigenWerten(): void
{
$this->assertTrue(fscash_is_valid_symbol_payload(42, 'SAP.DE'));
$this->assertTrue(fscash_is_valid_symbol_payload('42', 'SAP.DE'));
}
public function testIsValidSymbolPayloadOhneIdOderSymbolIstUngueltig(): void
{
$this->assertFalse(fscash_is_valid_symbol_payload(null, 'SAP.DE'));
$this->assertFalse(fscash_is_valid_symbol_payload(42, null));
$this->assertFalse(fscash_is_valid_symbol_payload(0, 'SAP.DE'));
$this->assertFalse(fscash_is_valid_symbol_payload(42, ''));
}
// --- fscash_format_report_month_csv_row --------------------------------
public function testFormatReportMonthCsvRow(): void
{
$entry = [
'id' => 5,
'datum' => '2026-07-01',
'benutzer_name' => 'Alice',
'kategorie_id' => 3,
'kategorie_name' => 'Einkaufen',
'betreff' => 'Wocheneinkauf',
'betrag' => 1234.5,
'zahlungsmethode' => 'Karte',
];
$this->assertSame(
['5', '2026-07-01', 'Alice', '3', 'Einkaufen', 'Wocheneinkauf', '1.234,50', 'Karte'],
fscash_format_report_month_csv_row($entry)
);
}
public function testFormatReportMonthCsvRowMitFehlendenFeldern(): void
{
$this->assertSame(
['', '', '', '', '', '', '0,00', ''],
fscash_format_report_month_csv_row([])
);
}
// --- fscash_pending_migrations ------------------------------------------
public function testPendingMigrationsFiltertBereitsAngewendete(): void
{
$available = ['/m/0002_add_col.sql', '/m/0001_init.sql', '/m/0003_index.sql'];
$applied = ['0001_init.sql'];
$this->assertSame(
['/m/0002_add_col.sql', '/m/0003_index.sql'],
fscash_pending_migrations($available, $applied)
);
}
public function testPendingMigrationsSortiertNumerischAlsString(): void
{
$available = ['/m/0010_b.sql', '/m/0002_a.sql'];
$this->assertSame(
['/m/0002_a.sql', '/m/0010_b.sql'],
fscash_pending_migrations($available, [])
);
}
public function testPendingMigrationsBeiAllenAngewendetLiefertLeeresArray(): void
{
$available = ['/m/0001_init.sql'];
$applied = ['0001_init.sql'];
$this->assertSame([], fscash_pending_migrations($available, $applied));
}
}