
    it?                       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ZddlZddlmZmZmZ ddlmZmZmZ d Zd8d9dZd:d	Zd: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(d Z)d Z*d Z+d Z,d Z-e.dk(  rddl/Z/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d+e)fd,e*fd-e+fd.e,fd/e-fgZ0dZ1 e2e0      Z3e0D ]  \  Z4Z5	  e5         e6d0e4        e1d1z  Z1  e6d4e1 d5e3 d6       e1e3k(  r	 e6d7       y e	jt                  d1       yy# e7$ r)Z8 e6d2e4 d3e8         e/jr                          Y dZ8[8zdZ8[8ww xY w);u1  
Tests for Story 6.03 (Track B): TaskDAGPusher — Redis Streams Task Queue

Black Box tests (BB): verify the public contract from the outside —
    push_dag return type/count, create_consumer_group idempotency,
    get_stream_length delegation.
White Box tests (WB): verify internal mechanics — XADD called once per task,
    entry IDs vs task_id UUIDs, payload JSON encoding, STREAM_KEY constant.

Story: 6.03
File under test: core/coherence/task_dag_pusher.py

ALL tests use mocks — NO real Redis connection is made.
NO SQLite anywhere in this module.
    )annotationsNz/mnt/e/genesis-system)	AsyncMock	MagicMockcall)TaskDAGPusher
STREAM_KEYDEFAULT_GROUPc                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_03.pyrunr   -   s    !!#66t<<    c                    t               }dgfd}t        |      |_        t        d      |_        t        |       |_        |S )u   
    Build a minimal mock Redis client that supports:
        xadd()         — returns incrementing fake stream entry IDs
        xgroup_create()— returns True by default
        xlen()         — returns stream_len
    r   c                :   K   dxx   dz  cc<   dd   ddS w)Nr      1696000000002dz-0 )keyfields_counters     r   _xaddz_make_redis.<locals>._xaddB   s+     qXa[-R00s   side_effectT)return_value)r   r   xaddxgroup_createxlen)
stream_len
mock_redisr   r   s      @r   _make_redisr$   6   sG     J sH1  E2JO(d;JZ8JOr   c                 N    t               } t        t        d            | _        | S )z
    Build a mock Redis client where xgroup_create raises a BUSYGROUP error.
    This simulates the consumer group already existing.
    z,BUSYGROUP Consumer Group name already existsr   r$   r   	Exceptionr    r#   s    r   _make_redis_busygroupr)   M   s)    
 J(LM J r   c                 N    t               } t        t        d            | _        | S )zU
    Build a mock Redis client where xgroup_create raises a non-BUSYGROUP error.
    z*CONNECTION_REFUSED Cannot connect to Redisr   r&   r(   s    r   _make_redis_xgroup_errorr+   Y   s)     J(JK J r   c                    t               } t        |       }dddiddddddid	d
dg}t        |j                  d|            }| j                  }|j
                  }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      dz  }t        j                  d| j                  j
                         dz   d|iz  }	t        t        j                  |	            dx}x}x}}t        |      }
d}|
|k(  }|st        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  }t        j                  dt        |             dz   d|iz  }t        t        j                  |            dx}
x}}y)uO   BB1: push_dag with 2 tasks → redis.xadd called twice → returns 2 entry IDs.researchqueryAIT2high	task_typepayloadtierpriority
synthesizetopicT1normalzsess-bb1   ==zL%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.xadd
}.call_count
} == %(py7)sr#   py0py2py4py7z Expected xadd called twice, got z
>assert %(py9)spy9Nz0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slen	entry_idsr@   py1py3py6zExpected 2 entry IDs, got 
>assert %(py8)spy8)r$   r   r   push_dagr   
call_count
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanationrF   )r#   pushertasksrG   @py_assert1@py_assert3@py_assert6@py_assert5@py_format8@py_format10@py_assert2@py_assert4@py_format7@py_format9s                 r   <test_bb1_push_dag_two_tasks_calls_xadd_twice_returns_two_idsre   i   s   J:&F !gt_dX^_"ZbcE
 FOOJ67I?? ?%%  %*  %                  &    *+    +:??+E+E*FG      y>MQM>QMMM>QMMMMMM3MMM3MMMMMMyMMMyMMM>MMMQMMM"<S^<L MMMMMMMMr   c                    t               } t        |       }dddidddg}t        |j                  d|             | j                  }|j
                  }d}||k(  }|st        j                  d	|fd
||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}| j                  j                  }	|	d   \  }
}h d}|t        |j!                               z
  }| }|s~t        j"                  d|       dz   ddt        j                         v st        j                  |      rt        j                  |      ndiz  }t        t        j                  |            d}y)uo   BB2: Stream entry contains all required fields — session_id, task_id,
    task_type, payload, tier, priority.auditurlzhttps://example.comT3lowr2   zsess-bb2r   r<   r>   r#   r?   assert %(py9)srD   Nr   >   r5   r4   task_idr6   r3   
session_idz)Missing required fields in stream entry: z
>assert not %(py0)sr@   missing)r$   r   r   rN   r   rO   rP   rQ   rR   rS   rT   rU   rW   rX   	call_argssetkeysrV   )r#   rY   rZ   r[   r\   r]   r^   r_   r`   	xadd_call_r   required_fieldsrn   @py_format2s                  r   2test_bb2_stream_entry_contains_all_required_fieldsrv   {   sH    J:&F"7L/MW[inopE
E*+??*?%%**%****%******:***:***?***%**********))I!IAv[OFKKM 22G;M;MMCG9MMMMMMMwMMMwMMMMMMr   c                 ~   t               } t        |       }t        |j                               }d}||u }|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                  }d	}||k(  }	|	st	        j
                  d
|	fd||f      dt        j                         v st	        j                  |       rt	        j                  |       ndt	        j                  |      t	        j                  |      t	        j                  |      dz  }
dd|
iz  }t        t	        j                  |            dx}x}x}	}y)uR   BB3: create_consumer_group succeeds on first call (no exception) → returns True.Tisz%(py0)s is %(py3)sresultr@   rJ   assert %(py5)spy5Nr   r<   )zU%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.xgroup_create
}.call_count
} == %(py7)sr#   r?   rk   rD   )r$   r   r   create_consumer_grouprP   rQ   rR   rS   rT   rU   rW   rX   r    rO   )r#   rY   r{   ra   r[   @py_format4@py_format6r\   r]   r^   r_   r`   s               r   5test_bb3_create_consumer_group_succeeds_on_first_callr      s   J:&F--/0F6T>6T66T##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33333r   c                    t               } t        |       }t        |j                               }d}||u }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d      dz   d|iz  }t        t	        j                  |            d	x}}y	)
uN   BB4: create_consumer_group with BUSYGROUP error → returns True (idempotent).Trx   rz   r{   r|   zEcreate_consumer_group must return True when BUSYGROUP error is raised
>assert %(py5)sr~   N)r)   r   r   r   rP   rQ   rR   rS   rT   rU   rV   rW   rX   )r#   rY   r{   ra   r[   r   r   s          r   @test_bb4_create_consumer_group_busygroup_returns_true_idempotentr      s    &(J:&F--/0F 6T>  6T                  	P    r   c                    t        d      } t        |       }t        |j                               }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}}| j                  j                  t               y	)
z9BB5: get_stream_length returns the value from redis.xlen.*   )r"   r<   z%(py0)s == %(py3)slengthr|   r}   r~   N)r$   r   r   get_stream_lengthrP   rQ   rR   rS   rT   rU   rW   rX   r!   assert_called_once_withr   )r#   rY   r   ra   r[   r   r   s          r   -test_bb5_get_stream_length_returns_xlen_valuer      s    +J:&F))+,F6R<6R66ROO++J7r   c            	     4   t               } t        |       }di ddddi ddddi dddg}t        |j                  d	|             | j                  }|j
                  }t        |      }||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      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                  |      dz  }t        j                  dt        |       d| j                  j
                         dz   d|iz  }t        t        j                  |            dx}x}x}}| j                  j                   D ]  }	|	d   d   }
|
t"        k(  }|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  }t        j                  d|
      dz   d|iz  }t        t        j                  |            d} y)z<WB1: XADD is called once per individual task, never batched.t1r9   r:   r2   t2t3r0   r1   zsess-wb1r<   )zj%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.xadd
}.call_count
} == %(py9)s
{%(py9)s = %(py6)s(%(py7)s)
}r#   rF   rZ   )r@   rA   rB   rK   rC   rD   z	Expected z  xadd calls (one per task), got z
>assert %(py11)spy11Nr   z%(py0)s == %(py2)skey_argr   r@   rA   zUnexpected stream key: 
>assert %(py4)srB   )r$   r   r   rN   r   rO   rF   rP   rQ   rR   rS   rT   rU   rV   rW   rX   call_args_listr   )r#   rY   rZ   r[   r\   @py_assert8r^   r`   @py_format12cr   @py_format3@py_format5s                r   ,test_wb1_xadd_called_once_per_task_not_batchr      sl   J:&F r4XNr4XNr4VLE 
E*+ ?? ?%% U %3   %                  &      *-    *-      .3    .3    *4    CJ<  ))*	,      __++ LA$q'*$KKKw*KKKKKKwKKKwKKKKKK*KKK*KKKK(?{&KKKKKKKLr   c                    t               } t        |       }di ddddi dddg}t        |j                  d|            }|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  }t	        j                  d|      dz   d|iz  }t        t	        j                  |            dx}}|j                  d      }	t        |	      }d}
||
k(  }|st	        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  }t	        j                  d|      dz   d|iz  }t        t	        j                  |            dx}x}}
 y)zrWB2: push_dag returns the list of stream entry IDs (from XADD return
    values), not the generated task_id UUIDs.pingr9   r:   r2   pongzsess-wb2-in)z%(py1)s in %(py3)seidrI   rJ   zBEntry ID should look like a Redis stream ID (e.g. '1234-0'), got: r   r~   Nr;   r<   rE   rF   partsrH   zEEntry ID should be a Redis stream ID (two '-'-separated parts), got: rL   rM   )r$   r   r   rN   rP   rQ   rU   rR   rS   rT   rV   rW   rX   splitrF   )r#   rY   rZ   rG   r   @py_assert0ra   r   r   r   r^   rb   rc   rd   s                 r   8test_wb2_returns_list_of_stream_entry_ids_not_task_uuidsr      s    J:&F TxPTxPE
 FOOJ67I  
 	
scz 	
 	
sc 	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
  QQTPWX	
 	
 	
 	
 	
 		#5z 	
Q 	
zQ 	
 	
 	
zQ 	
 	
	6	
 	
   	
 	
 		  	
 	
	6	
 	
   	
 	
 		  	
 	
 		  	
 	
 		  	
 	
  TTWSZ[	
 	
 	
 	
 	
 	

r   c                    t               } t        |       }di ddddi dddg}t        |j                  d|             | j                  }|j
                  }d	}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}}| j                  j                  D ]  }	|	d   \  }
}|d   }	 t        j                   |      }j(                  }d}||k(  }|st        j                  d
|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j*                  d|d|j(                   d      dz   d|iz  }t        t        j                  |            dx}x}} y# t"        $ r t%        j&                  d|d       Y w xY w)zEWB3: The task_id field in every stream entry is a valid UUID4 string.zjob-ar9   r:   r2   zjob-br0   r1   zsess-wb3r;   r<   r>   r#   r?   rk   rD   Nr   rl   ztask_id z is not a valid UUID   )z/%(py2)s
{%(py2)s = %(py0)s.version
} == %(py5)sparsed)r@   rA   r~   z is not UUID version 4 (got v)z
>assert %(py7)srC   )r$   r   r   rN   r   rO   rP   rQ   rR   rS   rT   rU   rW   rX   r   uuidUUID
ValueErrorpytestfailversionrV   )r#   rY   rZ   r[   r\   r]   r^   r_   r`   r   rs   r   task_id_strr   rb   r   s                   r   (test_wb3_task_id_in_entry_is_valid_uuid4r      s   J:&F "dQ"dOE
 
E*+??*?%%**%****%******:***:***?***%**********__++ 

aD	6Y'	HYY{+F ~~ 	
 	
~" 	
 	
~ 	
 	
	6	
 	
   	
 	
 		  	
 	
 		  	
 	
 		 "# 	
 	
  {o%B6>>BRRST	
 	
 	
 	
 	
 	


  	HKK(;/1EFG	Hs   I"I?>I?c                 \   t               } t        |       }ddd}d|dddg}t        |j                  d|             | j                  j
                  }|d	   \  }}|d
   }t        |t              }|s7t        j                  dt        |      j                         dz   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%        j&                  |      }
|
|k(  }|st        j(                  d|fd|
|f      dt        j                         v st        j                  |
      rt        j                  |
      nddt        j                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|d|
      dz   d|iz  }	t!        t        j"                  |	            d}y)z_WB4: The payload field in the stream entry is a JSON-encoded string,
    not a raw Python dict.zhttps://genesis.ai   )rh   depthcrawlr0   r:   r2   zsess-wb4r   r4   z)payload field must be a JSON string, got z7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancepayload_fieldstr)r@   rI   rA   rB   Nr<   r   decodedpayload_datar   z#Decoded payload mismatch: expected , got r   rB   )r$   r   r   rN   r   ro   r   r   rP   rV   type__name__rR   rS   rT   rU   rW   rX   jsonloadsrQ   )r#   rY   r   rZ   rr   rs   r   r   r\   r   r   r[   r   s                r   :test_wb4_payload_field_is_json_encoded_string_not_raw_dictr     s;    J:&F/!<L"|TW_`aE
E*+))I!IAv9%M mS) )   4D4G4P4P3QR               $    $      &)    &)    *     
 jj'Gl"  7l                #    #    .l-=VG;O    r   c                    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  }t        j                  dt               dz   d|iz  }t        t        j                  |            d	x}} y	)
zCWB5: STREAM_KEY module-level constant equals 'genesis:swarm:tasks'.genesis:swarm:tasksr<   r   r   r|   z0STREAM_KEY should be 'genesis:swarm:tasks', got r   r~   N)
r   rP   rQ   rR   rS   rT   rU   rV   rW   rX   )ra   r[   r   r   s       r   3test_wb5_stream_key_constant_is_genesis_swarm_tasksr     s    . :..  :.              /    ;:.I    r   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: TaskDAGPusher importable from core.coherence.r   )r   rx   )z%(py0)s is %(py2)sTDPr   r   zassert %(py4)srB   N)
core.coherencer   rP   rQ   rR   rS   rT   rU   rW   rX   )r   r[   r   r   s       r   $test_package_exports_task_dag_pusherr   (  sm    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
)z9Package level: STREAM_KEY importable from core.coherence.r   )r   r   r<   r   SKr|   r}   r~   N)
r   r   rP   rQ   rR   rS   rT   rU   rW   rX   )r   ra   r[   r   r   s        r   test_package_exports_stream_keyr   .  sa    /&&2&&&&&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
)z<Package level: DEFAULT_GROUP importable from core.coherence.r   )r	   genesis_workersr<   r   DGr|   r}   r~   N)
r   r	   rP   rQ   rR   rS   rT   rU   rW   rX   )r   ra   r[   r   r   s        r   "test_package_exports_default_groupr   4  sa    2""2"""""2"""""""2"""2"""""""""""r   c                 "   t               } t        |       }t        j                  t              5 }t        |j                                ddd       d}j                  }t        |      }||v }|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# 1 sw Y   =xY w)
z5create_consumer_group re-raises non-BUSYGROUP errors.NCONNECTION_REFUSEDr   )zK%(py1)s in %(py8)s
{%(py8)s = %(py3)s(%(py6)s
{%(py6)s = %(py4)s.value
})
}r   exc_info)rI   rJ   rB   rK   rM   zassert %(py10)spy10)r+   r   r   raisesr'   r   r   valuer   rP   rQ   rU   rR   rS   rT   rW   rX   )	r#   rY   r   r   r^   @py_assert7ra   rd   @py_format11s	            r   7test_create_consumer_group_non_busygroup_error_reraisesr   :  s    )+J:&F	y	! ,XF((*+,  6x~~63~#66#66666#666666666636663666666x666x666~666#66666666, ,s   FFc                    t               } t        |       }t        |j                  dg             }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                          y)	zBpush_dag with empty task list returns empty list, xadd not called.z
sess-emptyr<   r   rG   r|   r}   r~   N)r$   r   r   rN   rP   rQ   rR   rS   rT   rU   rW   rX   r   assert_not_called)r#   rY   rG   ra   r[   r   r   s          r   ,test_push_dag_empty_tasks_returns_empty_listr   E  s    J:&FFOOL"56I9?999OO%%'r   c                    t               } t        |       }t        |j                  di g             | j                  j
                  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}||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}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}x}}t        j                  }	|d   } |	|      }
i }|
|k(  }|st        j                  d|fd|
|f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |	      t        j                  |      t        j                  |
      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}	x}x}
x}}y
)uo   Task with no optional fields → defaults: task_type='unknown', tier='T1',
    priority='normal', payload='{}'.zsess-defaultsr   r3   unknownr<   )z%(py1)s == %(py4)s)rI   rB   zassert %(py6)srK   Nr5   r9   r6   r:   r4   )zK%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.loads
}(%(py4)s)
} == %(py9)sr   )r@   rA   rB   rK   rD   zassert %(py11)sr   )r$   r   r   rN   r   ro   rP   rQ   rU   rW   rX   r   r   rR   rS   rT   )r#   rY   rs   r   r   r\   ra   r   rc   r[   r^   r   r   r`   r   s                  r   6test_push_dag_defaults_applied_for_missing_task_fieldsr   P  s    J:&F"./))!,IAv++)+)++++)++++++)+++++++&>!T!>T!!!!>T!!!>!!!T!!!!!!!*)))))))))))))))))))::.fY'.:'(.B.(B....(B......4...4...:...'...(...B........r   c                    t               } t        |       }d}t        d      D cg c]
  }d| i d }}t        |j	                  ||             | j
                  j                  D ]  }|d   \  }}|d   }||k(  }	|	st        j                  d|	fd||f      t        j                  |      d	t        j                         v st        j                  |      rt        j                  |      nd	d
z  }
t        j                  d|d|d         dz   d|
iz  }t        t        j                  |            dx}}	 yc c}w )z=Every stream entry carries the session_id passed to push_dag.zsess-propagate-abcr   t)r3   r4   r   rm   r<   )z%(py1)s == %(py3)ssessionr   zExpected session_id=r   r   r~   N)r$   r   ranger   rN   r   r   rP   rQ   rU   rR   rS   rT   rV   rW   rX   )r#   rY   r   irZ   r   rs   r   r   ra   r   r   s               r   2test_push_dag_session_id_propagated_to_every_entryr   _  s.   J:&F"G<A!HEqQqc7r2EEE'(__++ 
aD	6l# 	
#w. 	
 	
#w 	
 	
 		 $ 	
 	
	6	
 	
  (/ 	
 	
 		 (/ 	
 	
  #7+VF<4H3KL	
 	
 	
 	
 	

	 Fs   D=c                     t               } t        |       }t        |j                  d             | j                  j                  t        ddd       y)zHcreate_consumer_group calls XGROUP CREATE with id='0' and mkstream=True.
test_group0T)idmkstreamN)r$   r   r   r   r    r   r   )r#   rY   s     r   6test_create_consumer_group_passes_mkstream_and_id_zeror   p  sH    J:&F$$\2344LS4 5 r   __main__u;   BB1: push_dag 2 tasks → xadd called twice → 2 entry IDsz.BB2: Stream entry contains all required fieldsz1BB3: create_consumer_group succeeds on first calluB   BB4: create_consumer_group BUSYGROUP → returns True (idempotent)z)BB5: get_stream_length returns xlen valuez*WB1: XADD called once per task (not batch)z6WB2: Returns list of stream entry IDs (not task UUIDs)z)WB3: task_id in each entry is valid UUID4z8WB4: payload field is JSON-encoded string (not raw dict)z1WB5: STREAM_KEY constant is 'genesis:swarm:tasks'z1PKG: TaskDAGPusher importable from core.coherencez.PKG: STREAM_KEY importable from core.coherencez1PKG: DEFAULT_GROUP importable from core.coherencez4create_consumer_group re-raises non-BUSYGROUP errorsu4   push_dag empty tasks → empty list, xadd not calledu,   push_dag missing fields → defaults appliedz+session_id propagated to every stream entryz5create_consumer_group passes id='0' and mkstream=Truez	  [PASS] r   z	  [FAIL] z: 
/z tests passedz(ALL TESTS PASSED -- Story 6.03 (Track B))   )r"   intreturnr   )r   r   );__doc__
__future__r   builtinsrR   _pytest.assertion.rewrite	assertionrewriterP   syspathinsertr   r   r   r   unittest.mockr   r   r   core.coherence.task_dag_pusherr   r   r	   r   r$   r)   r+   re   rv   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   	tracebacktestspassedrF   totalnamefnprintr'   exc	print_excexitr   r   r   <module>r     sW    #   
 * +     4 4 =.	 N$N&4	8 L2
2
46 '#7(/
"	  z 
G	E	G	9	;	=	<	>	@	M	I	K	4	6	8	5	5	7	A	A	C	4	1	3	C	C	E	<	<	>	<	-	/	9	(	*	<	+	-	?	@	B	?	5	7	7	?	A	6	;	=	@	?	AG%EN FJE "b	"DIdV$%aKF	" 
Bvhawm
,-89s b  	"IdV2cU+,I!!	"s   4D>>E,E''E,