
    iH1                       d Z ddlmZ ddlZddlmc mZ ddl	Z	e	j                  j                  dd       ddlZddlZddlmZmZ ddlmZmZmZmZ ddlZddlmZmZmZ ddlmZ d	 Z	 	 	 	 d4	 	 	 	 	 	 	 	 	 d5d
Zd6dZd7dZd8dZ d9dZ!d Z"d Z#d Z$d Z%d Z&d Z'd Z(d Z)d Z*d Z+d Z,d Z-d Z.d Z/e0dk(  rddl1Z1de"fde#fd e$fd!e%fd"e&fd#e'fd$e(fd%e)fd&e*fd'e+fd(e,fd)e-fd*e.fd+e/fgZ2dZ3 e4e2      Z5e2D ]  \  Z6Z7	  e7         e8d,e6        e3d-z  Z3  e8d0e3 d1e5 d2       e3e5k(  r	 e8d3       y e	jx                  d-       yy# e9$ r)Z: e8d.e6 d/e:         e1jv                          Y dZ:[:zdZ:[:ww xY w):u_  
Tests for Story 6.05 (Track B): StagingArea — ProposedDelta Collection Hub

Black Box tests (BB): verify the public contract from the outside —
    submit_delta stores a delta, collect_all returns all deltas,
    clear removes the hash, wait_for_all polls until complete or timeout.

White Box tests (WB): verify internal mechanics — correct Redis key pattern,
    EXPIRE called with 600s TTL, bytes are decoded, partial results on timeout,
    STAGING_KEY_PREFIX constant value.

Story: 6.05
File under test: core/coherence/staging_area.py

ALL tests use mocks — NO real Redis connection is made.
    )annotationsNz/mnt/e/genesis-system)datetimetimezone)	AsyncMock	MagicMockcallpatch)StagingAreaSTAGING_KEY_PREFIXSTAGING_TTL_SECONDS)
StateDeltac                H    t        j                         j                  |       S )z<Run a coroutine synchronously (pytest-asyncio not required).)asyncioget_event_looprun_until_complete)coros    6/mnt/e/genesis-system/tests/track_b/test_story_6_05.pyrunr   /   s    !!#66t<<    c                    |ddddg}t        | ||t        |      t        ddddd	d	t        j                  
            S )zBuild a StateDelta fixture.addz/statusactive)oppathvaluei           r   )tzinfoagent_id
session_idversion_at_readr	   submitted_at)r   tupler   r   utc)r!   r"   r#   	patch_opss       r   _make_deltar(   4   sO     !9xHI	'IdAr2q!HLLI r   c                    t        j                  | j                  | j                  | j                  t        | j                        | j                  j                         d      S )z=Produce the JSON string that StagingArea.submit_delta stores.r    )	jsondumpsr!   r"   r#   listr	   r$   	isoformat)deltas    r   _serializedr/   F   sO    ::**$44%++&!..88:	
 r   c                     t               } t        d      | _        t        d      | _        t        i       | _        t        d      | _        t        d      | _        | S )z6Return a mock Redis client with an empty staging hash.   return_valueTr   )r   r   hsetexpirehgetallhlendelete)rs    r   _make_redis_emptyr:   S   sJ    AA&AFd+AHr*AIA&AFa(AHHr   c                    t               }t        | j                  t        |       i      |_        t        d      |_        |S )zFReturn a mock Redis client whose hgetall returns one serialized delta.r2   r1   r:   r   r!   r/   r6   r7   )r.   r9   s     r   _make_redis_with_one_deltar=   ^   s:    Annk%&89AI A&AFHr   c                    t               }t        | j                  t        |       |j                  t        |      i      |_        t        d      |_        |S )z2Return a mock Redis client with two staged deltas.r2   r   r<   )d1d2r9   s      r   _make_redis_with_two_deltasrA   h   sH    AKKRKKR
AI A&AFHr   c                 
   t               } t        |       }t        |      }t        |j	                  |              t        |j                  | j                              }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}|d
   }	|	d   }
| j                   }|
|k(  }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}
x}}|	d   }
| j                  }|
|k(  }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}
x}}|	d   }
| j"                  }|
|k(  }|st        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}
x}}|	d   }
| j$                  }t'        |      }|
|k(  }|s
t        j                  d|fd|
|f      t        j                  |
      dt        j                         v st        j                  t&              rt        j                  t&              nddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}
x}x}}y	)uG   BB1: submit_delta then collect_all → returns dict matching the delta.r1   ==z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenresultpy0py1py3py6assert %(py8)spy8Nr   r!   z0%(py1)s == %(py5)s
{%(py5)s = %(py3)s.agent_id
}r.   rJ   rK   py5assert %(py7)spy7r"   )z2%(py1)s == %(py5)s
{%(py5)s = %(py3)s.session_id
}r#   )z7%(py1)s == %(py5)s
{%(py5)s = %(py3)s.version_at_read
}r	   )zK%(py1)s == %(py8)s
{%(py8)s = %(py3)s(%(py6)s
{%(py6)s = %(py4)s.patch
})
}r,   )rJ   rK   py4rL   rN   zassert %(py10)spy10)r(   r=   r
   r   submit_deltacollect_allr"   rF   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr!   r#   r	   r,   )r.   r9   sarG   @py_assert2@py_assert5@py_assert4@py_format7@py_format9d@py_assert0@py_format6@py_format8@py_assert7@py_format11s                  r   3test_bb1_submit_then_collect_returns_matching_deltarl   z   s   ME"5)A	QB 0 012Fv;!;!;!33vv;!q	AZ=*ENN*=N****=N***=******E***E***N*******\?.e...?.....?....?......e...e...........85#8#88#88888#888888888858885888#88888888W:*ekk*k**:*****:****:***************e***e***k***********r   c                    t        d      } t        d      }t        | |      }t        |      }t        |j	                  | j
                              }t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }	t        t        j                  |	            dx}x}}|D 
ch c]  }
|
d   	 }}
d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            dx}}yc c}
w )u:   BB2: Multiple agents submit → all appear in collect_all.agent-alphar!   
agent-betar   rC   rE   rF   rG   rH   rM   rN   Nr!   )in)z%(py1)s in %(py3)s	agent_ids)rJ   rK   assert %(py5)srQ   )r(   rA   r
   r   rW   r"   rF   rX   rY   rZ   r[   r\   r]   r^   r_   )r?   r@   r9   r`   rG   ra   rb   rc   rd   re   rf   rr   rg   @py_format4rh   s                  r   2test_bb2_multiple_agents_all_appear_in_collect_allru      s   	m	,B	l	+B#B+A	QB./Fv;!;!;!33vv;!(./1:/I/%=I%%%%=I%%%=%%%%%%I%%%I%%%%%%%$<9$$$$<9$$$<$$$$$$9$$$9$$$$$$$ 0s   (Kc                 x   t               } t               }t        |      }t        |j	                  | j
                               t        |j                  | j
                              }g }||k(  }|st        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  }t        t        j                  |            dx}}|j                  j!                  t"         | j
                          y)u9   BB3: clear → subsequent collect_all returns empty list.rC   z%(py0)s == %(py3)srG   rI   rK   rs   rQ   N)r(   r:   r
   r   clearr"   rW   rX   rY   rZ   r[   r\   r]   r^   r_   r8   assert_awaited_once_withr   )r.   r9   r`   rG   ra   @py_assert1rt   rh   s           r   .test_bb3_clear_then_collect_returns_empty_listr|      s    MEA	QB!!"# 0 012F6R<6R66RHH%%);(<U=M=M<N&OPr   c                 P   t               } t               }t        d      |_        t        | j                  t        |       i      |_        t        |      }t        dt                     5  t        |j                  | j                  dd            }ddd       t        t              }|sd	d
t        j                         v st!        j"                  t              rt!        j$                  t              nd
dt        j                         v st!        j"                  |      rt!        j$                  |      nddt        j                         v st!        j"                  t              rt!        j$                  t              ndt!        j$                  |      dz  }t'        t!        j(                  |            d}t+        |      }d}||k(  }|st!        j,                  d|fd||f      dt        j                         v st!        j"                  t*              rt!        j$                  t*              nddt        j                         v st!        j"                  |      rt!        j$                  |      ndt!        j$                  |      t!        j$                  |      dz  }	dd|	iz  }
t'        t!        j(                  |
            dx}x}}|d   d   }| j                  }||k(  }|st!        j,                  d|fd||f      t!        j$                  |      dt        j                         v st!        j"                  |       rt!        j$                  |       ndt!        j$                  |      dz  }dd|iz  }t'        t!        j(                  |            dx}x}}y# 1 sw Y   	xY w)u   
    BB4: wait_for_all with expected=2, only 1 submitted, timeout 200ms
         → returns partial (1 delta), does NOT raise.
    r1   r2   )core.coherence.staging_area.asyncio.sleepnewr      expected_count
timeout_msNz5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancerG   r,   )rI   rJ   py2rT   rC   rE   rF   rH   rM   rN   r   r!   rO   r.   rP   rR   rS   )r(   r:   r   r7   r!   r/   r6   r
   r	   r   wait_for_allr"   r   r,   rZ   r[   rX   r\   r]   r^   r_   rF   rY   )r.   r9   r`   rG   @py_assert3@py_format5ra   rb   rc   rd   re   rg   rh   ri   s                 r   7test_bb4_wait_for_all_timeout_returns_partial_not_errorr      s   
 MEAA&AFE8J'KLAI	QB	:		L ZR__U%5%5aTW_XYZ fd########:###:######f###f######d###d##########v;!;!;!33vv;!!9Z 2ENN2 N2222 N222 222222E222E222N2222222Z Zs   ,(NN%c                 J   t        d      } t        d      }t        | |      }t        |      }t               }t	        d|      5  t        |j                  | j                  dd            }d	d	d	       |j                          t              }d}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t!        t        j"                  |
            d	x}x}}y	# 1 sw Y   +xY w)u   
    BB5: wait_for_all with all deltas already present → returns without
         waiting the full timeout (sleep called fewer times than timeout allows).
    rn   ro   rp   r~   r   r   i`  r   NrC   rE   rF   rG   rH   rM   rN   )r(   rA   r
   r   r	   r   r   r"   assert_not_awaitedrF   rX   rY   rZ   r[   r\   r]   r^   r_   )r?   r@   r9   r`   
sleep_mockrG   ra   rb   rc   rd   re   s              r   8test_bb5_wait_for_all_returns_immediately_when_count_metr      s   
 
m	,B	l	+B#B+A	QBJ	:
	K ZR__R]]1QW_XYZ !!#v;!;!;!33vv;!Z Zs   (FF"c                     t        d      } t               }t        |      }t        |j	                  |              d}|j
                  j                  || j                  t        |              y)zGWB1: HSET called with correct key pattern genesis:staging:{session_id}.zsess-wb1r"   zgenesis:staging:sess-wb1N)	r(   r:   r
   r   rV   r4   rz   r!   r/   )r.   r9   r`   expected_keys       r   -test_wb1_hset_called_with_correct_key_patternr      sW    :.EA	QB-LFF##Er   c                     t        d      } t               }t        |      }t        |j	                  |              |j
                  j                  dd       y)z9WB2: EXPIRE called with 600s TTL after each submit_delta.zsess-wb2r   zgenesis:staging:sess-wb2X  N)r(   r:   r
   r   rV   r5   rz   )r.   r9   r`   s      r   1test_wb2_expire_called_with_600s_ttl_after_submitr      sF    :.EA	QBHH%%
"r   c                 L   t               } t        d      | _        t        i       | _        t	        |       }t        dt                     5  t        |j                  ddd            }d	d	d	       g }|k(  }|st        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  }t        t        j                  |            d	x}}y	# 1 sw Y   xY w)uH   WB3: wait_for_all returns partial results on timeout — does not raise.r   r2   r~   r   zsess-timeout   d   r   NrC   rw   rG   rx   rs   rQ   )r:   r   r7   r6   r
   r	   r   r   rX   rY   rZ   r[   r\   r]   r^   r_   )r9   r`   rG   ra   r{   rt   rh   s          r   ;test_wb3_wait_for_all_returns_partial_on_timeout_not_raisesr      s    AA&AFr*AI	QB	:		L XR__^ARU_VWX 6R<6R66R	X Xs   DD#c                 L   t        dd      } t        |       j                  d      }| j                  j                  d      }t	               }t        ||i      |_        t        |      }t        |j                  | j                              }t        |      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                   t              rt        j"                  t              nd	d
t        j                         v st        j                   |      rt        j"                  |      nd
t        j"                  |      t        j"                  |      dz  }	dd|	iz  }
t%        t        j&                  |
            dx}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j"                  |      t        j"                  |      dz  }dd|iz  }	t%        t        j&                  |	            dx}x}}|d   d   }d}||k(  }|slt        j                  d|fd||f      t        j"                  |      t        j"                  |      dz  }dd|iz  }	t%        t        j&                  |	            dx}x}}y)z@WB4: collect_all decodes bytes keys/values from Redis correctly.zagent-bytesz
sess-bytes)r!   r"   zutf-8r2   r1   rC   rE   rF   rG   rH   rM   rN   Nr   r!   )z%(py1)s == %(py4)s)rJ   rT   zassert %(py6)srL   r"   )r(   r/   encoder!   r:   r   r6   r
   r   rW   r"   rF   rX   rY   rZ   r[   r\   r]   r^   r_   )r.   serialized_bytesagent_id_bytesr9   r`   rG   ra   rb   rc   rd   re   rg   r   r   s                 r   !test_wb4_handles_bytes_from_redisr     s   <HE"5)009^^**73NA8H'IJAI	QB 0 012Fv;!;!;!33vv;!!9Z 1M1 M1111 M111 111M1111111!9\"2l2"l2222"l222"222l2222222r   c                 p   d} t         | k(  }|st        j                  d|fdt         | f      dt        j                         v st        j
                  t               rt        j                  t               ndt        j                  |       dz  }dd|iz  }t        t        j                  |            dx}} y)	z.WB5: STAGING_KEY_PREFIX is 'genesis:staging:'.genesis:staging:rC   rw   r   rx   rs   rQ   N)	r   rX   rY   rZ   r[   r\   r]   r^   r_   ra   r{   rt   rh   s       r   $test_wb5_staging_key_prefix_constantr     sb    !33!33333!3333333333333!33333333r   c                    ddl m}  | t        u }|st        j                  d|fd| t        f      dt	        j
                         v st        j                  |       rt        j                  |       nddt	        j
                         v st        j                  t              rt        j                  t              nddz  }dd	|iz  }t        t        j                  |            d
}y
)z:Package level: StagingArea importable from core.coherence.r   )r
   )is)z%(py0)s is %(py2)sSAr
   )rI   r   zassert %(py4)srT   N)
core.coherencer
   rX   rY   rZ   r[   r\   r]   r^   r_   )r   r{   @py_format3r   s       r   !test_package_exports_staging_arear     sm    0222r   c                 \   ddl m}  d}| |k(  }|st        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  }t        t        j                  |            d
x}}y
)zAPackage level: STAGING_KEY_PREFIX importable from core.coherence.r   )r   r   rC   rw   SKPrx   rs   rQ   N)
r   r   rX   rY   rZ   r[   r\   r]   r^   r_   )r   ra   r{   rt   rh   s        r   'test_package_exports_staging_key_prefixr   $  sa    8$$3$$$$$3$$$$$$$3$$$3$$$$$$$$$$$r   c                 \   ddl m}  d}| |k(  }|st        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  }t        t        j                  |            d
x}}y
)zBPackage level: STAGING_TTL_SECONDS importable from core.coherence.r   )r   r   rC   rw   TTLrx   rs   rQ   N)
r   r   rX   rY   rZ   r[   r\   r]   r^   r_   )r   ra   r{   rt   rh   s        r   (test_package_exports_staging_ttl_secondsr   *  s]    93#:3#33#r   c                 p   d} t         | k(  }|st        j                  d|fdt         | f      dt        j                         v st        j
                  t               rt        j                  t               ndt        j                  |       dz  }dd|iz  }t        t        j                  |            dx}} y)	z/STAGING_TTL_SECONDS module constant equals 600.r   rC   rw   r   rx   rs   rQ   N)	r   rX   rY   rZ   r[   r\   r]   r^   r_   r   s       r   test_staging_ttl_is_600r   0  s_    "%%#%%%%#%%%%%%%%%%%%#%%%%%%%r   __main__u:   BB1: submit_delta then collect_all → matching delta dictu+   BB2: Multiple agents → all in collect_allu-   BB3: clear → collect_all returns empty listu8   BB4: wait_for_all timeout → partial result (not error)u3   BB5: wait_for_all count met → returns immediatelyz)WB1: HSET called with correct key patternz WB2: EXPIRE called with 600s TTLz.WB3: wait_for_all partial on timeout, no raisez'WB4: Bytes from Redis decoded correctlyz-WB5: STAGING_KEY_PREFIX == 'genesis:staging:'z/PKG: StagingArea importable from core.coherencez6PKG: STAGING_KEY_PREFIX importable from core.coherencez7PKG: STAGING_TTL_SECONDS importable from core.coherencezSTAGING_TTL_SECONDS == 600z	  [PASS] r1   z	  [FAIL] z: 
/z tests passedz(ALL TESTS PASSED -- Story 6.05 (Track B))rn   zsess-001r   N)
r!   strr"   r   r#   intr'   zlist | Nonereturnr   )r.   r   r   r   )r   r   )r.   r   r   r   )r?   r   r@   r   r   r   )=__doc__
__future__r   builtinsrZ   _pytest.assertion.rewrite	assertionrewriterX   sysr   insertr   r*   r   r   unittest.mockr   r   r   r	   pytestcore.coherence.staging_arear
   r   r   core.coherence.state_deltar   r   r(   r/   r:   r=   rA   rl   ru   r|   r   r   r   r   r   r   r   r   r   r   r   __name__	tracebacktestspassedrF   totalnamefnprint	Exceptionexc	print_excexit r   r   <module>r      s9  " #   
 * +   ' ; ;  
 2= " !	  	
 $

$+$%
Q3,0 3$4%& z 
FGz{	68jk	8:hi	CE|}	>@xy	46cd	+-^_	9;vw	24UV	8:^_	:<]^	ACjk	BDlm	%'>?E" FJE "b	"DIdV$%aKF	" 
Bvhawm
,-89G 6  	"IdV2cU+,I!!	"s   =EE5E00E5