Files
ADconsle/tests/Feature/DashboardTest.php
T
kzbkowskiandClaude Sonnet 5 1126d96271
CI / PHP 8.2 (push) Failing after 2m46s
CI / PHP 8.3 (push) Failing after 59s
CI / PHP 8.4 (push) Failing after 1m7s
ADconsole v1.0.0
First public release.

- docs: document the audit log, user offboarding, CSV export and the
  "require a reason for destructive actions" setting across README, docs/
  and CLAUDE/AGENTS.md; note retention for panel_audit
- docs: fill the publishing placeholders (repo URL, security contact,
  issue-template links) and add a semantic-versioning policy
- move agent/contributor context into CLAUDE/ (AGENTS.md + a README);
  root CLAUDE.md is now a thin @-import pointer
- add a favicon (shield-lock, matching the login screen) to the login
  page and the app layout
- CHANGELOG: promote [Unreleased] to [1.0.0]; keep the earlier internal
  build as [0.1.0]

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfuvfwexztdS7LDpGuKsuK
2026-09-08 23:14:39 +02:00

180 lines
7.0 KiB
PHP

<?php
namespace Tests\Feature;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;
/**
* Renderowanie pulpitu na podstawie gotowego cache (bez realnego LDAP).
* Kontroler czyta wynik z Cache::store('file') pod kluczem 'dashboard:summary',
* więc podstawiamy go ręcznie i sprawdzamy sam widok.
*/
class DashboardTest extends TestCase
{
protected function tearDown(): void
{
Cache::store('file')->forget('dashboard:summary');
Cache::store('file')->forget('dashboard:summary_ts');
parent::tearDown();
}
private function seedSummary(array $overrides = []): void
{
$payload = array_merge([
'stats' => [
'users_total' => 128,
'users_disabled' => 4,
'users_locked' => 2,
'pwd_never_expires' => 6,
'pwd_expiring_7d' => 3,
'pwd_expired' => 5,
'inactive_90d' => 9,
'account_expired' => 1,
'users_no_manager' => 7,
'computers_inactive_90d' => 3,
],
'pwd_age' => ['lt30' => 40, 'd30_60' => 30, 'd60_90' => 20, 'gt90' => 30, 'unknown' => 8],
'computers_total' => 44,
'groups_total' => 17,
'expiring_soon' => [['cn' => 'Jan Kowalski', 'sam' => 'jkowalski', 'date' => '2026-09-10']],
'inactive' => [],
'recent' => [],
'pwd_never_list' => [['cn' => 'Anna Nowak', 'sam' => 'anowak']],
'locked_list' => [['cn' => 'Piotr Lis', 'sam' => 'plis', 'since' => '2026-09-08 07:30']],
'no_manager_list' => [['cn' => 'Ewa Mak', 'sam' => 'emak']],
'computers_inactive' => [['cn' => 'WS-OLD-01', 'last' => '2026-01-02']],
'max_password_age_days' => 42,
], $overrides);
Cache::store('file')->put('dashboard:summary', $payload, 300);
Cache::store('file')->put('dashboard:summary_ts', now()->subMinutes(2)->format('Y-m-d H:i:s'), 300);
}
public function test_it_renders_clickable_and_alerting_tiles(): void
{
$this->seedSummary();
$res = $this->actingAsPanelAdmin()->get('/dashboard')->assertOk();
// Klikalne kafelki prowadzą do przefiltrowanej listy (href w HTML ma &amp;,
// więc porównujemy z domyślnym escapowaniem oczekiwanego łańcucha).
$res->assertSee(route('users.index', ['all_ou' => 1, 'filter' => 'locked']));
$res->assertSee(route('users.index', ['all_ou' => 1, 'filter' => 'pwd_expired']));
$res->assertSee(route('users.index', ['all_ou' => 1, 'status' => 'disabled']));
$res->assertSee(route('computers.index', ['all_ou' => 1]));
// Kafelek "z problemem" (locked > 0) dostaje wyróżniającą klasę.
$res->assertSee('dashboard-tile-alert', escape: false);
// Histogram wieku hasła i wiek danych.
$res->assertSee(__('dashboard.pwd_age.heading'));
$res->assertSee(__('dashboard.reports.expiring_date'));
}
public function test_it_shows_a_global_empty_state_when_ldap_returned_nothing(): void
{
$this->seedSummary([
'stats' => [
'users_total' => 0, 'users_disabled' => 0, 'users_locked' => 0,
'pwd_never_expires' => 0, 'pwd_expiring_7d' => 0, 'pwd_expired' => 0,
'inactive_90d' => 0, 'account_expired' => 0,
],
'pwd_age' => ['lt30' => 0, 'd30_60' => 0, 'd60_90' => 0, 'gt90' => 0, 'unknown' => 0],
'computers_total' => 0,
'groups_total' => 0,
'expiring_soon' => [],
]);
$this->actingAsPanelAdmin()->get('/dashboard')
->assertOk()
->assertSee(__('dashboard.empty'))
->assertDontSee('dashboard-tile', escape: false);
}
public function test_it_renders_the_new_signal_cards_and_actions(): void
{
$this->seedSummary();
$res = $this->actingAsPanelAdmin()->get('/dashboard')->assertOk();
// Nowe kafelki + ich linki do przefiltrowanych list.
$res->assertSee(route('users.index', ['all_ou' => 1, 'filter' => 'no_manager']));
$res->assertSee(route('computers.index', ['all_ou' => 1, 'inactive' => 1]));
// Nowe karty raportów.
$res->assertSee(__('dashboard.reports.pwd_never'));
$res->assertSee(__('dashboard.reports.locked'));
$res->assertSee(__('dashboard.reports.no_manager'));
$res->assertSee(__('dashboard.reports.computers_inactive'));
// Wiersze z list.
$res->assertSee('anowak')->assertSee('plis')->assertSee('WS-OLD-01');
// Akcja "odblokuj" w karcie zablokowanych + "resetuj" w karcie wygasających.
$res->assertSee(route('users.unlock', 'plis'));
$res->assertSee(route('users.index', ['all_ou' => 1, 'q' => 'jkowalski', 'open' => 'reset']));
}
public function test_preferences_endpoint_stores_layout_cookie(): void
{
$this->actingAsPanelAdmin()->post('/dashboard/preferences', [
'section_tiles' => '0',
'section_pwd_age' => '1',
'report_order' => ['r_recent', 'r_expiring', 'r_locked'],
'visible' => ['r_recent', 'r_expiring'],
])
->assertRedirect(route('dashboard.index'))
->assertCookie('dashboard_layout');
}
public function test_layout_cookie_hides_and_reorders_report_cards(): void
{
$this->seedSummary([
'recent' => [['cn' => 'Nowe Konto', 'sam' => 'nowe', 'created' => '2026-09-07']],
]);
$layout = json_encode([
'tiles' => false,
'pwd_age' => true,
'reports' => [
['key' => 'r_recent', 'visible' => true],
['key' => 'r_expiring', 'visible' => true],
['key' => 'r_locked', 'visible' => false],
],
]);
$res = $this->actingAsPanelAdmin()
->withoutMiddleware(EncryptCookies::class)
->withUnencryptedCookie('dashboard_layout', $layout)
->get('/dashboard')
->assertOk();
// Ukryta sekcja kafelków.
$res->assertDontSee('dashboard-tile', escape: false);
// Ukryta karta "zablokowane" - jej wiersz i nagłówek kolumny nie występują
// (sama nazwa raportu jest jeszcze na liście w oknie preferencji).
$res->assertDontSee('Piotr Lis');
$res->assertDontSee(__('dashboard.reports.locked_since'));
// Karta "Nowe konta" przed "Hasła wygasające" - nagłówki kolumn dat
// występują tylko w kartach (nie w oknie preferencji).
$res->assertSeeInOrder([
__('dashboard.reports.recent_date'),
__('dashboard.reports.expiring_date'),
]);
}
public function test_stale_cache_is_flagged(): void
{
$this->seedSummary();
Cache::store('file')->put('dashboard:summary_ts', now()->subHour()->format('Y-m-d H:i:s'), 300);
$this->actingAsPanelAdmin()->get('/dashboard')
->assertOk()
->assertSee('text-bg-warning', escape: false);
}
}