Files
preland/admin/tests/copy.php
T

26 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
require dirname(__DIR__).'/bootstrap.php';
use PrelandAdmin\{Store,Config,Jobs,Copy,Problem};
function httpCheck(bool $ok,string $label):void{if(!$ok)throw new RuntimeException($label);echo "PASS $label\n";}
$root=sys_get_temp_dir().'/preset-http-'.bin2hex(random_bytes(6));mkdir($root,0700);$store=new Store($root.'/state');$port=random_int(19000,29000);$env=getenv();$env['ADMIN_STATE_DIR']=$store->dir;$env['ADMIN_COOKIE_SECURE']='0';$env['ADMIN_USERNAME']='test';$env['ADMIN_PASSWORD_HASH']=password_hash('fixture',PASSWORD_DEFAULT);
$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);$cookie='';
function presetRequest(string $method,string $path,?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;$ctx=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'=>10]]);$raw=file_get_contents('http://127.0.0.1:'.$port.$path,false,$ctx);preg_match('~HTTP/\S+ (\d+)~',$http_response_header[0]??'',$m);foreach($http_response_header as $h)if(str_starts_with(strtolower($h),'set-cookie: '))$cookie=explode(';',substr($h,12))[0];return [(int)($m[1]??0),json_decode((string)$raw,true,64,JSON_THROW_ON_ERROR)];}
try{
$deadline=microtime(true)+5;do{$socket=@fsockopen('127.0.0.1',$port);if($socket){fclose($socket);break;}usleep(20000);}while(microtime(true)<$deadline);
foreach(['GET','POST','DELETE'] as $method){[$status]=presetRequest($method,'/api/utm-presets');httpCheck($status===401,'presets auth '.$method);}
[$status,$login]=presetRequest('POST','/api/login',['username'=>'test','password'=>'fixture']);$csrf=$login['csrf'];
$payload=['name'=>'HTTP набор','utm'=>Config::decode('{"theme":{"default":3,"weights":{"3":100}}}')];
foreach(['POST','DELETE'] as $method){[$status]=presetRequest($method,'/api/utm-presets',$payload);httpCheck($status===403,'presets CSRF '.$method);}
[$status,$saved]=presetRequest('POST','/api/utm-presets',$payload,$csrf);httpCheck($status===201&&array_keys($saved['preset'])===['id','name','utm'],'HTTP creates strictly domain-free snapshot');$id=$saved['preset']['id'];
[$status,$list]=presetRequest('GET','/api/utm-presets');httpCheck($status===200&&$list['presets']===[['id'=>$id,'name'=>'HTTP набор']],'global list without domain');
[$status]=presetRequest('GET','/api/utm-presets?target=anything');httpCheck($status===422,'domain list filter rejected');
[$status]=presetRequest('POST','/api/utm-presets',$payload,$csrf);httpCheck($status===409,'duplicate name conflict');
foreach(['source','domain','host','notes','target'] as $key){[$status]=presetRequest('POST','/api/utm-presets',$payload+[$key=>'forbidden'],$csrf);httpCheck($status===422,'reject preset binding or unrelated field '.$key);}
[$status]=presetRequest('POST','/api/copy',['source'=>(object)['id'=>'old','version'=>str_repeat('a',64)],'targets'=>[]],$csrf);httpCheck($status===422,'legacy domain-copy API rejected');
[$status]=presetRequest('DELETE','/api/utm-presets',['id'=>$id],$csrf);httpCheck($status===200,'delete independent preset');
[$status]=presetRequest('DELETE','/api/utm-presets',['id'=>$id],$csrf);httpCheck($status===404,'repeat deletion returns not found');
[$status,$list]=presetRequest('GET','/api/utm-presets');httpCheck($list['presets']===[],'deleted preset absent from global list');
echo "Preset HTTP contract complete\n";
}finally{proc_terminate($server);proc_close($server);fclose($log);}