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.
333 lines
11 KiB
PHP
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));
|
|
}
|
|
}
|