feat(v4): consolidate audited data recovery economic replay and optional archival

This commit is contained in:
ENELIX Agent
2026-10-03 10:40:18 +00:00
parent 6c91d6bcc6
commit 8eb9688942
26 changed files with 1747 additions and 38 deletions
+13
View File
@@ -63,16 +63,29 @@ try {
check($sent[0]['records'][0]['raw']['s101']['sourceUpdatedAt']===$clock,'original timestamp retained');
check(!str_contains(json_encode($sent),'synthetic-not-a-real-token'),'token never included in data payload');
check($sent[0]['records'][0]['controlEligible']===false,'data delivery is not actuator permission');
$expected=hash('sha256',json_encode(NetzfahrplanV4Messaufnahme::configuration($c),JSON_THROW_ON_ERROR|JSON_PRESERVE_ZERO_FRACTION));
check($sent[0]['records'][0]['mappingSha256']===$expected,'property roundtrip preserves approved mapping identity');
check(json_decode($m->a['NetzfahrplanV4LetzteDatenquittung'],true)['acceptedThrough']===end($sent[0]['records'])['capturedAt'],'successful acknowledgement persisted independently');
$clock+=30;$m->ErfasseNetzfahrplanV4Daten();check(count($sent)===1,'30-second collection does not double request rate');
check($m->a['NetzfahrplanV4DatenCursor']===$cursor,'unsent rows remain behind cursor');
check(isset(json_decode($m->vars['NetzfahrplanV4Datenstatus'],true)['lastAcknowledgement']['acceptedThrough']),'scheduled status retains last successful receipt');
$clock+=30;$mode='429';$m->ErfasseNetzfahrplanV4Daten();check($m->a['NetzfahrplanV4DatenCursor']===$cursor,'429 never advances acknowledgement cursor');
check(json_decode($m->vars['NetzfahrplanV4Datenstatus'],true)['delivery']['dataRetained'],'offline data retained');
check(json_decode($m->vars['NetzfahrplanV4Datenstatus'],true)['delivery']['httpStatus']===429,'safe HTTP error code visible without credentials');
$mode='wrong_ack';$clock+=120;$m->ErfasseNetzfahrplanV4Daten();check($m->a['NetzfahrplanV4DatenCursor']===$cursor,'wrong ack timestamp rejected');
$mode='ok';$clock+=240;$m->ErfasseNetzfahrplanV4Daten();check($m->a['NetzfahrplanV4DatenCursor']!==$cursor,'later success sends the preserved backlog');
check(count(end($sent)['records'])===4,'all previously unacknowledged rows included once');
$file=glob($m->folder().'/raw-*.jsonl')[0];$count=count(file($file));check($count===5,'raw journal never cleared by upload');
$lastCursor=$m->a['NetzfahrplanV4DatenCursor'];file_put_contents($file,'incomplete',FILE_APPEND);
check($m->send()['status']==='up_to_date','in-progress tail not transmitted');check($m->a['NetzfahrplanV4DatenCursor']===$lastCursor,'partial tail not acknowledged');
// Only temporary test journal: turn the prior incomplete tail into an additional complete queued record.
$buf=file_get_contents($file);$buf=substr($buf,0,-strlen('incomplete'));
$extra=$sent[0]['records'][0];$clock+=60;$extra['capturedAt']=gmdate('c',$clock);$extra['captureStartedAt']=$extra['capturedAt'];
file_put_contents($file,$buf.json_encode($extra)."\n".'crash-tail');
$clock+=60;$beforeCount=count($sent);$m->ErfasseNetzfahrplanV4Daten();
$err=json_decode($m->vars['NetzfahrplanV4Datenstatus'],true);
check($err['status']==='capture_error'&&$err['delivery']['status']==='acknowledged','capture failure does not deadlock pending delivery');
check(count($sent)===$beforeCount+1&&str_ends_with(file_get_contents($file),'crash-tail'),'complete prefix sent while original incomplete tail retained');
$m->p['NetzfahrplanV4MessdatenAktiv']=false;$m->configure();check($m->timers['NetzfahrplanV4DatenErfassen']===0,'disable affects acquisition only');
check(count($m->vars)===1,'only own manager data-status variable written');
$text=file_get_contents(__DIR__.'/../../libs/ManagerNetzfahrplanV4DatenTrait.php');