Files
preland/admin/tests/backend-http.php
T
2026-10-25 11:33:56 +03:00

30 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
require __DIR__.'/backend.php';
$port=random_int(19000,29000);$env=getenv();$env['ADMIN_COOKIE_SECURE']='0';$env['ADMIN_USERNAME']='test-admin';$env['ADMIN_PASSWORD_HASH']=password_hash('fixture-admin-password',PASSWORD_DEFAULT);$env['ADMIN_STATE_DIR']=$root.'/state';$env['ADMIN_LOCAL_ROOTS']=$root.'/site';
$log=fopen($root.'/http.log','a');$server=proc_open([PHP_BINARY,'-S','127.0.0.1:'.$port,'-t',dirname(__DIR__).'/public',dirname(__DIR__).'/public/index.php'],[0=>['pipe','r'],1=>$log,2=>$log],$pipes,null,$env);if(!is_resource($server))throw new RuntimeException('server failed');
$cookie='';
function request(string $path,string $method='GET',?array $body=null,?string $csrf=null): array {
global $port,$cookie;
$headers=['Content-Type: application/json'];if($cookie)$headers[]='Cookie: '.$cookie;if($csrf!==null)$headers[]='X-CSRF-Token: '.$csrf;
$context=stream_context_create(['http'=>['method'=>$method,'header'=>implode("\r\n",$headers),'content'=>$body===null?'':json_encode($body,JSON_THROW_ON_ERROR),'ignore_errors'=>true,'timeout'=>30]]);
$result=file_get_contents('http://127.0.0.1:'.$port.$path,false,$context);$responseHeaders=$http_response_header??[];preg_match('~HTTP/\S+ (\d+)~',$responseHeaders[0]??'',$match);
foreach($responseHeaders as $header)if(str_starts_with(strtolower($header),'set-cookie: '))$cookie=explode(';',substr($header,12))[0];
return [(int)($match[1]??0),json_decode((string)$result,true,64,JSON_THROW_ON_ERROR),$responseHeaders];
}
try{
$deadline=microtime(true)+5;do{$socket=@fsockopen('127.0.0.1',$port);if($socket){fclose($socket);break;}usleep(50000);}while(microtime(true)<$deadline);
[$status,$body]=request('/api/state');check($status===401&&!$body['ok'],'HTTP unauthenticated state blocked');
[$status,$body,$headers]=request('/api/login','POST',['username'=>'test-admin','password'=>'fixture-admin-password']);check($status===200&&isset($body['csrf']),'HTTP login creates authenticated CSRF session');$csrf=$body['csrf'];
check((bool)array_filter($headers,static fn($h)=>str_contains(strtolower($h),'httponly')&&str_contains(strtolower($h),'samesite=strict')),'session cookie HttpOnly and SameSite Strict');
[$status,$body]=request('/api/state');check($status===200&&isset($body['targets'][0])&&!array_key_exists('secret',$body['targets'][0]),'HTTP state redacts transport credentials');
[$status,$body]=request('/api/apply','POST',['targets'=>[],'changes'=>[]]);check($status===403,'HTTP mutation rejects missing CSRF');
[$status,$body]=request('/api/config?target=test');check($status===200&&isset($body['version'],$body['config']),'HTTP config reads actual target version');$version=$body['version'];
[$status,$body]=request('/api/apply','POST',['targets'=>[['id'=>'test','version'=>$version]],'changes'=>[['path'=>['host'],'value'=>'http.example']]],$csrf);check($status===202&&isset($body['job']['id']),'HTTP apply queues persistent job');$id=$body['job']['id'];
(new PrelandAdmin\Jobs($store))->run($id);
[$status,$body]=request('/api/jobs?id='.$id);check($body['job']['results'][0]['status']==='success'&&$body['job']['changes'][0]['path']===['host'],'HTTP persisted result and changed paths visible');
[$status,$body]=request('/api/logout','POST',null,$csrf);check($status===200,'HTTP logout succeeds');
[$status,$body]=request('/api/state');check($status===401,'HTTP logout invalidates session');
echo "HTTP integration complete\n";
}finally{proc_terminate($server);proc_close($server);fclose($log);}