feat(admin): независимые именованные UTM-наборы
This commit is contained in:
1 parent
941a4f01aa
commit
94619432a3
16 files changed
+220
-114
No files matched your search
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require dirname(__DIR__).'/bootstrap.php';
|
||||
use PrelandAdmin\{UtmPresets,Store,Copy,Config,Jobs,Transport,Problem};
|
||||
function checkPreset(bool $ok,string $label):void{if(!$ok)throw new RuntimeException($label);echo "PASS $label\n";}
|
||||
function rejectsPreset(callable $f,string $label):void{try{$f();}catch(Problem){checkPreset(true,$label);return;}throw new RuntimeException($label);}
|
||||
$root=sys_get_temp_dir().'/presets-'.bin2hex(random_bytes(6));mkdir($root,0700);$store=new Store($root.'/state');$presets=new UtmPresets($store);
|
||||
$utm=Config::decode('{"theme":{"default":"3","weights":{"1":33.33,"3":66.67}},"cons":{"default":2,"weights":{"1":0,"2":0}}}');
|
||||
$saved=$presets->create(['name'=>'Осень','utm'=>$utm]);
|
||||
checkPreset(array_keys($saved)===['id','name','utm'],'preset has no source domain or target binding');
|
||||
$utm->theme->weights->{'1'}=100;checkPreset($presets->get($saved['id'])['utm']->theme->weights->{'1'}===33.33,'draft is an immutable independent snapshot');
|
||||
checkPreset((new UtmPresets(new Store($store->dir)))->get($saved['id'])['name']==='Осень','preset survives store reload');
|
||||
foreach([['name'=>'','utm'=>$saved['utm']],['name'=>' ','utm'=>$saved['utm']],['name'=>'осень','utm'=>$saved['utm']],['name'=>'x','utm'=>$saved['utm'],'domain'=>'a.invalid'],['name'=>'x','utm'=>(object)[]],['name'=>'x','utm'=>(object)['theme'=>(object)['default'=>1,'weights'=>(object)['1'=>80]]]],['name'=>'x','utm'=>(object)['theme'=>(object)['default'=>1,'weights'=>(object)['1'=>100],'note'=>'secret']]],['name'=>'x','utm'=>(object)['unknown'=>(object)[]]]] as $bad)rejectsPreset(fn()=>$presets->create($bad),'strict independent preset validation');
|
||||
$paths=[];foreach(['a','b'] as $id){$paths[$id]=$root.'/'.$id;mkdir($paths[$id]);}putenv('ADMIN_LOCAL_ROOTS='.implode(',',$paths));
|
||||
$raw='{"host":"keep.invalid","utm":{"theme":{"default":"dark","weights":{"dark":100},"note":"keep"},"reg":{"default":2,"weights":{"2":100}},"custom":{"a":1}},"requisites":["keep"],"unknown":{}}';
|
||||
foreach($paths as $id=>$path){file_put_contents($path.'/config.json',$raw);$store->saveTarget(['id'=>$id,'domain'=>$id.'.invalid','type'=>'local','root'=>$path,'enabled'=>true]);$store->verifyTarget($id);}
|
||||
$store->write('utm-notes.json',['theme'=>['text'=>'keep note']]);$notes=file_get_contents($store->path('utm-notes.json'));
|
||||
$read=fn($id)=>(new Transport($store,$store->target($id)))->readConfig();$selection=fn($id)=>(object)['id'=>$id,'version'=>$read($id)['version']];
|
||||
$copy=new Copy($store);$job=$copy->create(['preset'=>$saved['id'],'targets'=>[$selection('a'),$selection('b')]]);
|
||||
$presets->delete($saved['id']);rejectsPreset(fn()=>$presets->get($saved['id']),'deleted preset unavailable');
|
||||
file_put_contents($paths['b'].'/config.json',str_replace('keep.invalid','foreign.invalid',$raw));$jobs=new Jobs($store);$jobs->run($job['id']);$done=$jobs->get($job['id']);
|
||||
checkPreset($done['results'][0]['status']==='success'&&$done['results'][1]['status']==='conflict','queued snapshot survives deletion and isolates CAS conflict');
|
||||
$a=$read('a')['config'];checkPreset($a->host==='keep.invalid'&&$a->utm->theme->note==='keep'&&$a->utm->reg->default===2&&$a->utm->custom->a===1&&$a->unknown instanceof stdClass,'host, absent groups, metadata and unknown types unchanged');
|
||||
checkPreset($a->utm->theme->default==='3'&&$a->utm->theme->weights->dark===33.33&&$a->utm->theme->weights->{'3'}===66.67&&$a->utm->cons->weights->{'2'}===0,'manual variants, semantic targets and all-zero percentages copied');
|
||||
checkPreset($store->backupContent('a',$store->backups('a')[0]['id'])===$raw&&$store->backups('b')===[],'exact backup only for successful recipient');
|
||||
checkPreset(file_get_contents($store->path('utm-notes.json'))===$notes,'notes stay byte identical');
|
||||
$retry=$store->read('jobs/'.$job['id'].'.json');$retry['status']='running';$retry['results'][0]['status']='running';$store->write('jobs/'.$job['id'].'.json',$retry);$jobs->run($job['id']);checkPreset(count($store->backups('a'))===1,'desired hash recovery does not repeat write');
|
||||
$next=$presets->create(['name'=>'После удаления','utm'=>$saved['utm']]);$store->setTargetVisibility('b',true);$store->deleteTarget('b');checkPreset($presets->get($next['id'])['name']==='После удаления','target deletion cannot delete or detach preset');
|
||||
foreach([['source'=>$selection('a'),'targets'=>[$selection('a')]],['preset'=>$next['id'],'targets'=>[]],['preset'=>$next['id'],'targets'=>[$selection('a'),$selection('a')]],['preset'=>$next['id'],'targets'=>[(object)['id'=>'a','version'=>'bad']]],['preset'=>$next['id'],'targets'=>[$selection('a')],'utm'=>(object)[]]] as $bad)rejectsPreset(fn()=>$copy->create($bad),'strict copy request');
|
||||
$same=$copy->create(['preset'=>$next['id'],'targets'=>[$selection('a')]]);$jobs->run($same['id']);checkPreset($jobs->get($same['id'])['results'][0]['status']==='success','any domain can receive independent preset');
|
||||
echo "ALL PRESET CHECKS PASS\n";
|
||||
Reference in new issue
Block a user