1, 'role' => 'admin', 'csrf' => 'csrf-token'];
}
protected function tearDown(): void
{
unset($_SESSION);
}
public function testAdminDashboardShowsSystemTotalsAndGlobalBucketOwners(): void
{
$response = Views::dashboard(
['id' => 1, 'username' => 'root-admin'],
[['name' => 'team-files', 'username' => 'alice', 'objects' => 2, 'bytes' => 2048]],
[
'users' => '3',
'buckets' => '1',
'objects' => '2',
'actual_storage_used' => '2.0 KiB',
'configured_hosting_capacity' => '200 GiB',
'allocated_quotas' => '10 GiB',
'security_events_last_24h' => '4',
'php' => '8.3.35',
'capacity_warning' => 'No low-capacity warning.',
],
);
self::assertSame(200, $response->status);
self::assertStringContainsString('Admin dashboard', $response->body);
self::assertStringContainsString('Registered accounts', $response->body);
self::assertStringContainsString('team-files', $response->body);
self::assertStringContainsString('alice', $response->body);
self::assertStringContainsString('200 GiB', $response->body);
self::assertStringContainsString('href="/_admin/buckets#create-bucket">Create bucket', $response->body);
self::assertStringContainsString('href="/_admin/files">Upload files', $response->body);
self::assertStringContainsString('href="/_admin/keys">Create access key', $response->body);
}
public function testAdminDashboardSurfacesConfiguredCapacityWarning(): void
{
$response = Views::dashboard(
['id' => 1, 'username' => 'root-admin'],
[],
['capacity_warning' => 'Storage is at or above 90% of configured capacity.'],
);
self::assertStringContainsString('Storage capacity warning', $response->body);
self::assertStringContainsString('at or above 90%', $response->body);
}
public function testAdminBucketsPageProvidesCreationAndOwnBucketFileAccess(): void
{
$response = Views::adminBuckets(
['id' => 1, 'username' => 'root-admin'],
[
['id' => 10, 'owner_id' => 1, 'name' => 'admin-files', 'username' => 'root-admin', 'objects' => 1, 'bytes' => 12, 'created_at' => '2026-10-13'],
['id' => 11, 'owner_id' => 2, 'name' => 'private-user-files', 'username' => 'alice', 'objects' => 1, 'bytes' => 24, 'created_at' => '2026-10-13'],
],
'csrf-token',
);
self::assertStringContainsString('name="action" value="create"', $response->body);
self::assertStringContainsString('3–63 lowercase letters, numbers, dots, or hyphens', $response->body);
self::assertStringContainsString('autocapitalize="none"', $response->body);
self::assertStringContainsString('href="/_admin/files?bucket=admin-files">Open files', $response->body);
self::assertStringContainsString('href="/_admin/files?bucket=admin-files">admin-files', $response->body);
self::assertStringNotContainsString('href="/_admin/files?bucket=private-user-files"', $response->body);
self::assertStringContainsString('href="/_admin/files">Open file manager', $response->body);
}
public function testAdminFileManagerShowsUploadAndShareActions(): void
{
$_SESSION['flash'] = 'Share link (expires in 1 hour): https://storage.example.test/photos/cat.png?X-Amz-Signature=abc';
$response = Views::files(
['id' => 1, 'username' => 'root-admin'],
[['name' => 'photos']],
'photos',
[['key' => 'cat.png', 'size' => 3]],
'csrf-token',
);
self::assertStringContainsString('name="action" value="upload"', $response->body);
self::assertStringContainsString('Share link', $response->body);
self::assertStringContainsString('Anyone with this link can download the file for one hour.', $response->body);
self::assertStringContainsString('data-copy-url=', $response->body);
}
public function testAdminCanReachAndCreateAnAccessKey(): void
{
$response = Views::keys(['id' => 1, 'username' => 'root-admin', 'role' => 'admin'], [], 'csrf-token');
self::assertStringContainsString('href="/_admin/keys">Access keys', $response->body);
self::assertStringContainsString('
Create API key
', $response->body);
self::assertStringContainsString('', $response->body);
}
public function testNewKeySecretRendersWithCopyButtons(): void
{
$_SESSION['flash'] = 'Created — copy the secret now, it is never shown again: AKIATESTKEY0001 / super-secret-value';
$response = Views::keys(
['id' => 1, 'username' => 'root-admin', 'role' => 'admin'],
[['id' => 7, 'access_key_id' => 'AKIATESTKEY0001', 'description' => 'app', 'allowed_buckets' => null, 'permissions' => ['read', 'write'], 'enabled' => 1, 'created_at' => '2026-10-13', 'last_used_at' => 'never']],
'csrf-token',
);
self::assertStringContainsString('id="f-new-akid"', $response->body);
self::assertStringContainsString('id="f-new-secret"', $response->body);
self::assertStringContainsString('data-copy="f-new-akid"', $response->body);
self::assertStringContainsString('data-copy="f-new-secret"', $response->body);
self::assertStringContainsString('value="AKIATESTKEY0001"', $response->body);
self::assertStringContainsString('value="super-secret-value"', $response->body);
self::assertStringContainsString('data-copy-text="AKIATESTKEY0001"', $response->body);
}
public function testAdminNavAndDashboardExposeConnectPage(): void
{
$keys = Views::keys(['id' => 1, 'username' => 'root-admin', 'role' => 'admin'], [], 'csrf-token');
self::assertStringContainsString('href="/_admin/connect">Connect', $keys->body);
$dashboard = Views::dashboard(['id' => 1, 'username' => 'root-admin'], [], []);
self::assertStringContainsString('href="/_admin/connect">Connect an application', $dashboard->body);
}
public function testAdminConnectIncludesSdkExamples(): void
{
$response = Views::connect(
['id' => 1, 'username' => 'root-admin'],
[],
[],
'https://s3.example.com',
'us-east-1',
);
self::assertStringContainsString('SDK examples', $response->body);
self::assertStringContainsString('rclone', $response->body);
self::assertStringContainsString('boto3', $response->body);
}
public function testConsoleConnectKeepsExamplesAndConsoleLinks(): void
{
$response = Views::consoleConnect(
['id' => 9, 'username' => 'owner', 'role' => 'user'],
[],
[],
'https://s3.example.com',
'us-east-1',
);
self::assertStringContainsString('SDK examples', $response->body);
self::assertStringContainsString('href="/_console/keys"', $response->body);
self::assertStringNotContainsString('href="/_admin/keys"', $response->body);
}
public function testAdminKeyFormSuggestsOwnedBucketNames(): void
{
$response = Views::keys(
['id' => 1, 'username' => 'root-admin', 'role' => 'admin'],
[],
'csrf-token',
false,
['photos', 'backups'],
);
self::assertStringContainsString('id="bucket-suggest"', $response->body);
self::assertStringContainsString('