
    +j8                    J   d Z ddlmZ ddlZddlmc mZ ddl	Z
ddlZddlZddlZddlZddlZddlZddlmZmZ ddlmZ  ee      j-                         j.                  j.                  j.                  Z ee      ej4                  vr"ej4                  j7                  d ee             ddZ edd        ed	d
      Z edd      Z edd      Zej@                  Z dZ! ejD                  d      Z#d Z$d Z% G d dejL                        Z'e(dk(  r ejR                          yy)u  task-2729+25 LAUNCH_WAKE_AT_FORMAT_COMPAT — 필수 regression.

발번: real callback spawn canary 에서 발견 — `anu_runner_pickup_and_fire` 의
pickup 경로(at=None)가 callback request 를 `--at "30s"`(초 suffix) 로 생성 →
cokacdir live runtime 이 `invalid --at value: 30s` 로 거부 → wake-build/authority
는 통과하나 실 cron 등록 실패(launcher compatibility bug, production activation
blocker 아님).

수정(회장 verbatim, 1줄+import): `anu_owned_callback_enforcement.py:1005` 의
`at=at or "30s"` → `at=at or build_absolute_at_for_normal_delay(60)` (now+60s 절대
timestamp). `build_absolute_at_for_normal_delay` 는 normal_fallback_callback_helper
의 task-2661 Phase 2b 검증 helper 재사용(신규 로직 0). 명시 at 주입 시 그 값 우선.

regression
  1. pickup default at(at=None) → argv `--at` 값이 is_absolute_at True
     (cokacdir-compatible 절대 `YYYY-MM-DD HH:MM:SS`).
  2. 출력/argv 에 "30s" / Ns(초 suffix) 부재.
  3. 명시 at 주입 시 그 값 우선(기존 동작 보존).
  4. normal callback path(build_absolute_at_for_normal_delay 재사용) 무영향.
  5. owner proof/envelope/raw key(owner_key 제거 유지) 회귀 무손상.
  6. raw key 0 — envelope prompt / argv 어디에도 ANU key literal 부재.
  7. ACTIVE=false / systemd enable 0 / activation_epoch absent / real spawn 0.
  + dry-run: launch_wake(dry_run=True) 로 빌드된 argv 의 `--at` 값이 is_absolute_at
    True, decision=DRY_RUN, subprocess(real spawn) 0.
    )annotationsN)datetimetimezone)Pathc                r   | t         j                  v rt         j                  |    S t        j                  j	                  | t
        |z        }g }d }||u}|}|r|j                  }d }||u}	|	}|slt        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }
dd|
iz  }|j                  |       |rt        j                  d	fdf      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }d	d
|iz  }|j                  |       t        j                  |d      i z  }dd|iz  }t        t        j                   |            d x}x}x}x}x}x}	}t        j                  j#                  |      }|t         j                  | <   |j                  j%                  |       |S )N)is not)z%(py2)s is not %(py5)sspec)py2py5z%(py7)spy7)z5%(py11)s
{%(py11)s = %(py9)s.loader
} is not %(py14)s)py9py11py14z%(py16)spy16r   zassert %(py19)spy19)sysmodules	importlibutilspec_from_file_location_ROOTloader
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprappend_format_boolopAssertionError_format_explanationmodule_from_specexec_module)modnamerelpathr	   @py_assert1@py_assert4@py_assert3@py_assert0@py_assert10@py_assert13@py_assert12@py_format6@py_format8@py_format15@py_format17@py_format18@py_format20mods                    J/home/jay/workspace/tests/regression/test_launch_wake_at_format_2729p25.py_loadr6   ,   sI   #++{{7##>>11'57?KD7t74t77474 777774t77777747774777t77777774777777777777777477777777777777
..
)
)$
/CCKKKKC J    z dispatch.callback_owner_enforcerz#dispatch/callback_owner_enforcer.pyz(dispatch.normal_fallback_callback_helperz+dispatch/normal_fallback_callback_helper.pyz'dispatch.anu_owned_callback_enforcement*dispatch/anu_owned_callback_enforcement.pyz!dispatch.anu_pickup_wake_launcherz$dispatch/anu_pickup_wake_launcher.pydeadbeefdev6selfz\b\d+s\bc            	     B    t        ddddddt        j                        S )N     	      r   tzinfo)r   r   utc r7   r5   _nowrC   H   s    D!QAq>>r7   c                0    | | j                  d      dz      S )u   argv 의 `--at` 값 추출.z--at   )index)argvs    r5   _argv_atrH   L   s    

6"Q&''r7   c                  J    e Zd Zd ZddZd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zy)Regression2729p25c                    t        j                  d      | _        | j                  t        j
                  | j                  d       y )Nztask2729p25-r-)prefixT)ignore_errors)tempfilemkdtemp_rdir
addCleanupshutilrmtree)selfs    r5   setUpzRegression2729p25.setUpS   s/    %%-=>
tzzFr7   c                   t         j                  |d| dddd| j                  t                     }| j	                  |j
                         | j                  |j                         | j                  |j                         |S )u7   executor result JSON 작성 (schedule 생성/발사 0).zmemory/reports/z.mdfeedfacedone)report_pathsha256summary)task_idpayload
result_dirnow)	Mexecutor_write_result_jsonrP   rC   
assertTrueokassertFalseschedule_createdcallback_fired)rT   r\   ews      r5   _write_resultzRegression2729p25._write_resultW   sz    ))&5gYc$B)f>zztv	 * 
 	,,-**+	r7   c                   | j                         }t        j                  |j                  t        t
        d      }| j                  |j                         | j                  |j                         t        |j                        }| j                  t        j                  |      d|       t        j                  |d       y)u   at=None(pickup 경로) → argv `--at` 값이 cokacdir-compatible 절대
        timestamp(`YYYY-MM-DD HH:MM:SS`), is_absolute_at True.
6937032012result_json_pathexecutor_keyanu_keychat_idu1   pickup default --at 가 절대 timestamp 아님: z%Y-%m-%d %H:%M:%SN)rh   r`   anu_runner_pickup_and_firerl   DEV6_SELF_KEYANU_KEYrb   rc   assertIsNotNonerG   rH   His_absolute_atr   strptime)rT   rg   rrat_values       r5   $test_1_pickup_default_at_is_absolutez6Regression2729p25.test_1_pickup_default_at_is_absolutee   s     !))00& 	 * 
 	RWW%BGG$X&?|L	

 	($78r7   c                6   | j                         }t        j                  |j                  t        t
        d      }| j                  |j                         t        |j                        }| j                  |d       | j                  d|       dj                  |j                        }| j                  t        j                  |      d|       | j                  d|       | j                  dt!        j"                  |j%                         d             y)	u@   argv 전체 + at 값 어디에도 "30s" / Ns(초 suffix) 부재.rj   rk   30s u#   --at 값에 초 suffix(Ns) 잔존: F)ensure_asciiN)rh   r`   rp   rl   rq   rr   rb   rc   rH   rG   assertNotEqualassertNotInjoinassertIsNone_SECOND_SUFFIX_REsearchjsondumpsto_json)rT   rg   rw   rx   joineds        r5   test_2_no_second_suffix_in_argvz1Regression2729p25.test_2_no_second_suffix_in_argv{   s    !))00& * 
 	BGG$He,) "''"$$X.1(>	
 	'

2::<e LMr7   c                   | j                         }d}t        j                  |j                  t        t
        d|      }| j                  |j                         | j                  t        |j                        |       t        j                  |j                  t        t
        dd      }| j                  t        |j                        d       y)uU   명시 at 인자 제공 시 default 로 덮어쓰지 않고 그 값 그대로 사용.z2026-06-09 18:30:00rj   )rl   rm   rn   ro   at10mN)rh   r`   rp   rl   rq   rr   rb   rc   assertEqualrH   rG   )rT   rg   explicitrw   rr2s        r5   #test_3_explicit_at_takes_precedencez5Regression2729p25.test_3_explicit_at_takes_precedence   s    !())00& * 

 	"''*H5 **00& + 

 	#((+U3r7   c                   t        dddddd      }| j                  t        j                  d|      d       | j                  t        j                  d	|      d
       | j                  t        j                  d       | j                  t        j
                  d       t        j                  ddt        t        ddt        j                  d|      ddt        t        j                        d      }| j                  |j                         | j                  t        j                  t        |j                                      y)u   재사용 helper build_absolute_at_for_normal_delay 의 기존 동작/normal
        경로 default(30s) 무변경. 본 task 는 pickup default(60s)만 추가 결선.r;   r<   r=   r>   r      )r_   z2026-06-09 12:00:30<   z2026-06-09 12:01:00r   normaltask-2729+25rj   ztask_id=task-2729+25ztask-2729+25::dispatchztask-2729+25::normalT)kindr\   rm   	owner_keyro   promptr   dispatch_cron_idnormal_collector_cron_idanu_keysno_fallbackN)r   r   rt   "build_absolute_at_for_normal_delayDEFAULT_AT_NORMAL_DELAY_SECONDSDEFAULT_AT_FALLBACK build_anu_owned_callback_requestrq   rr   tupler`   DEFAULT_ANU_KEYSrb   rc   ru   rH   rG   )rT   basereqs      r5   &test_4_normal_callback_path_unaffectedz8Regression2729p25.test_4_normal_callback_path_unaffected   s    aB1-000>!	
 	000>!	

 	::B?..600>&' )?33BD3A5%;1--. 1 	
 	((#(();<=r7   c                Z   t         j                  dddddt        d      }| j                  d|       d	D ]  }| j	                  ||        d
 }t         j                  d|      }| j                  |j                         | j                  |j                  t         j                         y)u   envelope prompt 가 task-2729+24 hygiene(owner_key 줄 제거) 유지 +
        owner proof 는 cron-history 경유(envelope 텍스트 미사용) 회귀 무손상.r   z/tmp/x.result.jsonzmemory/reports/task-2729+25.mdrW   r\   rl   rY   rZ   rj   rn   ro   z
owner_key=)ztask_id=zresult_path=zreport_path=zsha256=zcollector_role=ANUzchat_id=zcanonical_root=c                    dd|  dS )Nerrorz%schedule not found or access denied: )statusmessagerB   )_sids    r5   _probezHRegression2729p25.test_5_owner_proof_and_envelope_intact.<locals>._probe   s    %!FtfMO Or7   	SELFOWNED)probeN)r`   _build_envelope_promptrr   r   assertInresolve_authoritative_ownerrd   owner_is_anur   outcomeOWNER_NOT_ANU)rT   r   fieldr   ress        r5   &test_5_owner_proof_and_envelope_intactz8Regression2729p25.test_5_owner_proof_and_envelope_intact   s     ))&!5<!# \ * 
 	v.K 	)EMM%(	)	O ++Kv+F))*aoo6r7   c                   t         j                  dddddt        d      }| j                  t        |       | j	                         }t         j                  |j                  t        t        d      }|j                  |j                  j                  d	      d
z      }| j                  t        |       | j                  t        dj                  |j                               y)ut  envelope prompt 와 callback argv 어디에도 ANU key literal 미노출.

        ★ argv 의 --key 값은 ANU key 그 자체(데이터)지만, 그것은 cokacdir 등록용
        '명시 인자 슬롯'이며 prompt body(content-driven log 적재 표면)에는 절대
        들어가지 않아야 한다. 본 테스트는 prompt body 에 key literal 부재를 고정.r   z/tmp/xzr.mdrW   r   rj   r   rk   z--cronrE   r|   N)r`   r   rr   r   rh   rp   rl   rq   rG   rF   r   )rT   r   rg   rw   	cron_bodys        r5   test_6_no_raw_key_literalz+Regression2729p25.test_6_no_raw_key_literal   s     ))&H"j:\ * 

 	&)!))00& * 
 GGBGGMM(3a78	),(9:r7   c                   | j                         }t        j                  |j                  t        t
        d      }| j                  |j                         | j                  |j                  t        j                         t        j                  |      }| j                  |d          | j                  |d          t        dz  j                  d      }| j                  d|       | j                  d|       y	)
u   runner 는 자가발사 0(dry_run=True, fire_path NOT_ACTIVATED). activation
        epoch/systemd enable 부재. 소스에 systemd enable / activation_epoch 생성
        코드 없음.rj   rk   fireddry_runr8   zutf-8zsystemctl enableactivation_epochN)rh   r`   rp   rl   rq   rr   rb   r   r   	fire_pathFIRE_NOT_ACTIVATEDfire_callback_requestrd   r   	read_textr   )rT   rg   rw   gatesrcs        r5   0test_7_active_false_no_systemd_no_epoch_no_spawnzBRegression2729p25.test_7_active_false_no_systemd_no_epoch_no_spawn   s     !))00& * 
 	

#q';';<&&r*g'Y(CCNN+S1+S1r7   c           	     .   | j                         }t        j                  |j                  t        t
        d      }| j                  |j                         t        |j                        }| j                  t        j                  |             g fd}t        t        | j                        dz        }t        j!                  |j                  ddd||d 	      }| j#                  |j$                  t        j&                         | j                  |j(                         | j#                  g        | j+                  t        |      j-                                | j                  t        j                  t        |j                                     y
)u   launch_wake(dry_run=True) 로 빌드된 argv 의 --at 가 is_absolute_at True,
        decision=DRY_RUN, subprocess(real spawn) 0. 실 cron 등록 0.rj   rk   c                <    j                  |        t        d      )Nu1   dry_run=True 인데 real subprocess 가 호출됨)r   r!   )rG   spawn_callss    r5   	_no_spawnzTRegression2729p25.test_8_launch_wake_dry_run_absolute_at_no_spawn.<locals>._no_spawn  s    t$ !TUUr7   zlaunch_ledger.jsonlr   rW   Tc                 6    t               j                  d       S )Nr?   )rC   replacerB   r7   r5   <lambda>zSRegression2729p25.test_8_launch_wake_dry_run_absolute_at_no_spawn.<locals>.<lambda>&  s    $&...5 r7   )r\   rZ   r   launch_ledger_pathsubprocess_runnerclockN)rh   r`   rp   rl   rq   rr   rb   rc   rH   rG   rt   ru   strr   rP   Llaunch_waker   decisionDECISION_DRY_RUNr   rd   exists)rT   rg   rw   rx   r   ledgerrecr   s          @r5   /test_8_launch_wake_dry_run_absolute_at_no_spawnzARegression2729p25.test_8_launch_wake_dry_run_absolute_at_no_spawn  s8    !))00& * 
 	BGG$((23 	V T$**%(==>mmGG^J%'5  
 	q'9'9:$b)f,,./(("''):;<r7   N)r   )__name__
__module____qualname__rU   rh   ry   r   r   r   r   r   r   r   rB   r7   r5   rJ   rJ   Q   s6    G9,N.4*>>72;02,!=r7   rJ   __main__)r%   r   r&   r   )*__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   importlib.utilr   r   rerR   r   rN   unittestr   r   pathlibr   __file__resolveparentr   r   pathinsertr6   rt   r`   r   rr   rq   compiler   rC   rH   TestCaserJ   r   mainrB   r7   r5   <module>r      s  2 #      	  
   ' X ''..55u:SXXHHOOAs5z" (+-	
4
79	
3
68	
-
02 ))"BJJ{+ ?(
]=)) ]=@ zHMMO r7   