1
<?php
2
declare(strict_types=1);
3
4
use Machweis\Backend\Auth\TokenService;
5
use Machweis\Backend\CompanyProfileService;
6
use Machweis\Backend\Config;
7
use Machweis\Backend\Kernel;
8
use Machweis\Backend\Logger;
9
use Machweis\Backend\Request;
10
11
require dirname(__DIR__) . '/src/autoload.php';
12
13
function companyCheck(bool $condition, string $message): void { if (!$condition) throw new RuntimeException($message); }
14
function companyUuid(): string { $hex = bin2hex(random_bytes(16)); return substr($hex,0,8).'-'.substr($hex,8,4).'-4'.substr($hex,13,3).'-8'.substr($hex,17,3).'-'.substr($hex,20); }
15
16
$config = new Config('test', false, (string)getenv('DB_HOST'), (int)getenv('DB_PORT'), (string)getenv('DB_NAME'), (string)getenv('DB_USER'), (string)getenv('DB_PASSWORD'), [], true, str_repeat('company-profile-secret-', 2));
17
$pdo = new PDO(sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', $config->dbHost, $config->dbPort, $config->dbName), $config->dbUser, $config->dbPassword, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false]);
18
$tenantA = companyUuid(); $tenantB = companyUuid();
19
$pdo->prepare('INSERT INTO tenants (id,name,status,created_at) VALUES (?, ?, ?, UTC_TIMESTAMP()), (?, ?, ?, UTC_TIMESTAMP())')->execute([$tenantA, 'Firma A', 'active', $tenantB, 'Firma B', 'active']);
20
$service = new CompanyProfileService($pdo);
21
companyCheck($service->one($tenantA)['name'] === 'Firma A', 'tenant name must be the initial profile name');
22
$updated = $service->update($tenantA, ['name'=>'Firma A Neu','owner'=>'Ada Admin','email'=>'office@example.test','phone'=>'+49 30 1','address'=>'Hauptstraße 1','taxNumber'=>'12/345','reportFooter'=>'Danke']);
23
companyCheck($updated['name'] === 'Firma A Neu' && $updated['owner'] === 'Ada Admin', 'profile must persist all normalized fields');
24
companyCheck($service->one($tenantB)['name'] === 'Firma B' && $service->one($tenantB)['owner'] === '', 'profile access must remain tenant isolated');
25
try { $service->update($tenantA, ['name'=>'', 'email'=>'invalid']); throw new RuntimeException('invalid profile accepted'); } catch (InvalidArgumentException) {}
26
27
$tokens = new TokenService($config); $kernel = new Kernel($config, new Logger('test'));
28
foreach (['owner','office'] as $role) {
29
$token = $tokens->issueAccess(['sub'=>companyUuid(),'tid'=>$tenantA,'role'=>$role]);
30
$response = $kernel->handle(new Request('PATCH','/api/v1/company-profile',['authorization'=>'Bearer '.$token],json_encode(['name'=>'Firma '.$role], JSON_THROW_ON_ERROR)));
31
companyCheck($response->status === 200, "$role must update the company profile");
32
}
33
foreach (['supervisor','technician'] as $role) {
34
$token = $tokens->issueAccess(['sub'=>companyUuid(),'tid'=>$tenantA,'role'=>$role]);
35
$read = $kernel->handle(new Request('GET','/api/v1/company-profile',['authorization'=>'Bearer '.$token]));
36
companyCheck($read->status === 200, "$role must read the company profile");
37
$write = $kernel->handle(new Request('PATCH','/api/v1/company-profile',['authorization'=>'Bearer '.$token],'{"name":"Nicht erlaubt"}'));
38
companyCheck($write->status === 403, "$role must not update the company profile");
39
}
40
companyCheck($service->one($tenantB)['name'] === 'Firma B', 'HTTP updates must not cross tenant boundaries');
41
$pdo->prepare('DELETE FROM tenant_company_profiles WHERE tenant_id IN (?, ?)')->execute([$tenantA,$tenantB]);
42
$pdo->prepare('DELETE FROM tenants WHERE id IN (?, ?)')->execute([$tenantA,$tenantB]);
43
fwrite(STDOUT, "company_profile_contract: OK\n");
44