
    ΣSj4                        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
mZ ddlmZ ddlmZ  G d d      Z G 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 y)u  Compose/confirm card tests — the draft engine (voice/compose.py) and the
HUD→engine contract (control/card_action.json, applied by
ControlWatcher._card_action). The JSON payloads here are EXACTLY what the Mac
HUD's ComposePanel writes (hud/ClaudeHUD.swift), so these tests pin the
contract between the Swift card and the Python engine. No SMTP, no Google
API, no HUD, no audio (fakes throughout).

Run:  .venv/bin/python -m pytest test_compose_card.py -q
    )annotationsN)email_controlc                      e Zd Zd Zd Zy)	FakeInboxc                    g | _         y N)notesselfs    ?/Users/ahmed/devFolder/Ultron/claude-voice/test_compose_card.py__init__zFakeInbox.__init__   s	    
    c                :    | j                   j                  |       y r   )r	   append)r   notes     r   
put_nowaitzFakeInbox.put_nowait   s    

$r   N)__name__
__module____qualname__r   r    r   r   r   r      s     r   r   c                      e Zd Zd Zy)FakeAppc                "    t               | _        y r   )r   inboxr
   s    r   r   zFakeApp.__init__   s    [
r   N)r   r   r   r   r   r   r   r   r      s    !r   r   c                 ~    t         j                  j                  t         j                        } t               | _        | S )u   A ControlWatcher with no __init__ side effects (no control/ dir wipe,
    no subprocesses) — just enough to drive _card_action.)controlControlWatcher__new__r   app)ws    r   make_watcherr!   "   s.     	&&w'='=>AIAEHr   c                    t         j                  j                          t         j                  j                          g | j	                  t         dfd       S )z@Empty draft store + captured HUD events. Returns the event list.emitc                *    j                  | |f      S r   r   )etypedataeventss     r   <lambda>zfresh.<locals>.<lambda>0   s    fmmUDM.Jr   )compose_DRAFTSclear_ORDERsetattr)monkeypatchr(   s    @r   freshr0   *   sC    OONNFJLMr   c                   t        |       }t        j                  dddd      }t        j                  |      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                  } |       }||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                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }dd|iz  }
t        t	        j                  |
            d x}x}}	d|ddddg df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 )Nzelie@revalstudio.com z
Q3 numberszElie,

Numbers attached.kindemail==z%(py1)s == %(py4)spy1py4assert %(py6)spy6)zH%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.latest_id
}()
} == %(py6)sr*   did)py0py2r:   r<   assert %(py8)spy8)draft_idtoccsubjectbodyattachmentsz%(py0)s == %(py3)sr(   r>   py3assert %(py5)spy5)r0   r*   create_email_draft	get_draft
@pytest_ar_call_reprcompare	_safereprAssertionError_format_explanation	latest_id@py_builtinslocals_should_repr_global_name)r/   r(   r=   @py_assert0@py_assert3@py_assert2@py_format5@py_format7@py_assert1@py_assert5@py_format9@py_format4@py_format6s                r   test_email_draft_pops_cardrb   6   s   ;F

$
$L2NPCS!&)4W4)W4444)W444)444W4444444%%#%%%%#%%%%%%7%%%7%%%%%%%%%%%%#%%%#%%%%%%% 5R)E#   6    6    f      9    9     
	 r   c                   t        |       }t        j                  ddddddg      }|d   d   d	   }ddg}||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   d	   }ddg}||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dddd      }t        j                  |      d   d	   }ddg}||k(  }|slt        j                  d
|fd||f      t        j
                  |      t        j
                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)u   email_compose staged files → the compose event shows them on the card.
    Accepts a real list or the tool's comma-separated string form.z
elie@x.comr2   Reportz	Attached.z/tmp/q3.pdfz/tmp/chart.pngr      rG   r5   r7   r8   r;   r<   Nfieldsz /tmp/a.pdf , /tmp/b.xlsx z
/tmp/a.pdfz/tmp/b.xlsx)	r0   r*   rM   rO   rP   rQ   rR   rS   rN   )	r/   r(   r=   rX   rY   rZ   r[   r\   did2s	            r   ,test_email_draft_with_attachments_emits_themrh   C   s    ;F

$
$b(K	()+C !9Q<&K=:J*KK&*KKKKK&*KKKK&KKK*KKKKKKKKS!(+M: )'?) ): ?) ) )((): ?) ) )(y ; ) )(y?) ) ) ) )(() ) %%b(K1MODT"8,]; %m@% %; @% % %$$%; @% % %$9 < % %$9@% % % % %$$% % %r   c                   t        |       }t        j                  ddddddg      }|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   }d}||u }|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   }dg}||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} ||      }	|	|k(  }
|
s
t        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
                  |	      dt        j                         v st        j                  |      rt        j
                  |      nddz  }dd|iz  }t        t        j                  |            d x}x}x}	}
y )NzDesign review2026-07-14T15:00:00z2026-07-14T15:30:00
sara@x.comTz09:00  Standupr   
event_cardr5   r7   r8   r;   r<   re   meetisz%(py1)s is %(py4)scontextevent)zO%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.latest_id
}(%(py4)s)
} == %(py8)sr*   r=   )r>   r?   r:   r<   rA   assert %(py10)spy10)r0   r*   create_event_draftrO   rP   rQ   rR   rS   rT   rU   rV   rW   )r/   r(   r=   rX   rY   rZ   r[   r\   r]   r^   @py_assert7r_   @py_format11s                r   test_event_draft_pops_cardrx   T   s   ;F

$
$.0Ed-.0C !9Q<'<'<<''''<<'''<'''<'''''''!9Q<'4'4''''4''''''4'''''''!9Q<	"8'7&88"&88888"&8888"888&88888888,W,W%,%,,,,%,,,,,,7,,,7,,,,,,W,,,%,,,,,,,,,,,,,,,,,r   c                   t        |       }i fd}| j                  t        d|       t        j                  dddd      }t               }t        j                  |j                  d|dd	d
dddd             d	ddd
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}}t        j"                  } ||      }	d}
|	|
u }|s
t        j                  d|fd|	|
f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      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}}
dd|if}||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$ |j$                  j&                  j(                  D        }t+        |      }|sd%d&t        j                         v st        j                  t*              rt        j                  t*              nd&t        j                  |      t        j                  |      d'z  }t        t        j                   |            dx}}y)(u`   Ahmed edits the card, clicks Send → SMTP gets HIS values, card closes,
    Jarvis gets a note.c                @    j                  | |||       d| d|  dS )NrC   rE   rF   rD   zSent 'z' to .updaterC   rE   rF   rD   sents       r   	fake_sendz3test_hud_send_uses_edited_fields.<locals>.fake_sendg   s,    r7"=ybT++r   _sendzwrong@x.comr2   z
Draft subjz
Draft bodyr4   sendzright@x.comz
boss@x.comz
Fixed subjz
Fixed bodyrC   rD   rE   rF   r3   rB   actionrf   r{   r5   rH   r   rI   rK   rL   Nrn   zO%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.get_draft
}(%(py3)s)
} is %(py8)sr*   r=   r>   r?   rJ   rL   rA   rs   rt   
card_closerB   in)z%(py1)s in %(py3)sr(   )r9   rJ   c              3  $   K   | ]  }d |v  
 yw)z[compose] Ahmed clicked SendNr   .0ns     r   	<genexpr>z3test_hud_send_uses_edited_fields.<locals>.<genexpr>w   s      +)1 .2)   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr>   r?   r:   )r0   r.   r   r*   rM   r!   asynciorun_card_actionrO   rP   rU   rV   rW   rQ   rR   rS   rN   r   r   r	   r   )r/   r(   r   r=   r    rZ   r]   r`   ra   @py_assert4rv   @py_assert6r_   rw   rX   rY   r[   r   s                    @r    test_hud_send_uses_edited_fieldsr   a   s    ;FD, w	:

$
$]Bl
SCAKKSF&l*LB C D E (L(> >4 > > >,=,=>4 > > >7=v> >%=%=  > >4=I  > >4=I> > > > >*=*=> >)S!)T)!T))))!T))))))7)))7)))))))))S)))S)))!)))T))))))):s+,6,6666,666,6666666666666666+))+ +3 + + + + +$*F+ +**  + +!*  + +!*+ + +!*+ + + +**+ +r   c                r
   t        |       }t               }t        j                  dddd      }t	        j
                  |j                  d|dd             t        j                  } ||      }d }||u }|s
t        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
t        j                  |      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}}d |j                   j"                  j$                  D        }t'        |      }
|
sddt        j                         v st        j                  t&              rt        j                  t&              ndt        j                  |      t        j                  |
      dz  }t        t        j                  |            d x}}
t        j                  dddd      }t	        j
                  |j                  d|dd             t        j                  } ||      }d }||u }|s
t        j                  d|fd	||f      d
t        j                         v st        j                  t              rt        j                  t              nd
t        j                  |      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}}d |j                   j"                  j$                  D        }t'        |      }
|
sddt        j                         v st        j                  t&              rt        j                  t&              ndt        j                  |      t        j                  |
      dz  }t        t        j                  |            d x}}
|D cg c]  }|d   dk(  s| }}dd|ifdd|ifg}
||
k(  }|slt        j                  d|fd||
f      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            d x}x}}
y c c}w )Na@x.comr2   sbr4   cancel)r3   rB   r   rn   r   r*   r=   r   rs   rt   c              3  $   K   | ]  }d |v  
 yw)	cancelledNr   r   s     r   r   z,test_hud_cancel_and_close.<locals>.<genexpr>        ;):A{a):r   r   r   r   closerg   c              3  $   K   | ]  }d |v  
 yw)	dismissedNr   r   s     r   r   z,test_hud_cancel_and_close.<locals>.<genexpr>   r   r   r   r   rB   r5   r7   r8   r;   r<   )r0   r!   r*   rM   r   r   r   rN   rO   rP   rU   rV   rW   rQ   rR   rS   r   r   r	   r   )r/   r(   r    r=   r]   r   rv   r   r_   rw   rY   r[   rg   erX   rZ   r\   s                    r   test_hud_cancel_and_closer   {   s   ;FA

$
$YC
=CKKcX>@ A)S!)T)!T))))!T))))))7)))7)))))))))S)))S)))!)))T)))))));):):;;3;;;;;;;;;3;;;3;;;;;;;;;;;;;;%%iS#>DKKdg>@ A*T"*d*"d****"d******7***7*********T***T***"***d*******;):):;;3;;;;;;;;;3;;;3;;;;;;;;;;;;;;6v!1!5Av O6	
C()L:t:L+M;O O6 ;O O O=N=NO6 ;O O OENY 7 O OENY;O O O O O;N;NO O O6s   T4(T4c                   t        |        i fd}| j                  t        d|       t        j                  dddd      }t               }t        j                  |j                  d|dd	dd
ddd             |j                  }|j                  }|j                  }g }||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"                  |      t        j"                  |      dz  }	dd|	iz  }
t%        t        j&                  |
            dx}x}x}x}}t        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}}t        j                  t        j*                  |            \  }}g }|}|rd	d
d}|k(  }|}|s$dddt        j                         v st        j                   |      rt        j"                  |      ndiz  }|j-                  |       |rt        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-                  |	       t        j.                  |d#      i z  }d$d%|iz  }t%        t        j&                  |            dx}x}x}}y)&u   Typing in the card (debounced action 'edit') stores silently — no
    chatter — and a later spoken "send it" (compose.send_draft) sends the
    edited version.c                ,    j                  | |       y)NrC   rE   okr}   r   s       r   r   zCtest_hud_edit_is_silent_then_spoken_send_uses_it.<locals>.fake_send   s    r7+r   r   z	old@x.comr2   zold subjrF   r4   editz	new@x.comznew subjr   r   r5   za%(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.app
}.inbox
}.notes
} == %(py9)sr    r>   r?   r:   r<   py9assert %(py11)spy11Nrf   rC   r7   r8   r;   r<   r   %(py2)sr?   r   )z%(py4)s == %(py7)sr   )r:   py7z%(py9)sr   r   assert %(py12)spy12)r0   r.   r   r*   rM   r!   r   r   r   r   r   r	   rO   rP   rU   rV   rW   rQ   rR   rS   rN   
send_draftr   _format_boolop)r/   r   r=   r    r]   rY   r^   @py_assert8rv   @py_format10@py_format12rX   rZ   r[   r\   r   outr   @py_format3@py_format8rw   @py_format13r   s                         @r   0test_hud_edit_is_silent_then_spoken_send_uses_itr      s>    
+D w	:

$
$["j&
ICAKKSF$B:!# $ % & 55"5;;";""""""""""""1"""1"""5""";"""""""""""""S!(+D1@[@1[@@@@1[@@@1@@@[@@@@@@@kk',,S12GBD2D2DD$DDDDDDDD2DDD2DDDD2DDD$DDDDDDD$DDD$DDDDDDDDDDDDDDDDDr   c                ^   t        |        i | j                  t        dfd       t        j                  dddddg       }t	               }t        j                  |j                  d|dd	d
dddd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}||u }|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}
|	|
u }|s
t        j                  d|fd|	|
f      dt        j                         v st        j                   t              rt        j                  t              ndt        j                  |      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) uz   Event card: Send folds in edits including the Meet switch (False must
    stick — it's a real value, not a missing one)._create_eventc                .    j                  |       xs dS )NzEvent booked.r}   )fbookeds    r   r)   z4test_event_send_merges_meet_toggle.<locals>.<lambda>   s    &--"2"Eo"Er   Syncrj   r2   Trr   r   zSync (moved)z2026-07-14T16:00:00rk   F)titlestartend	attendeesrm   r   r   r5   r7   r8   r;   r<   Nrm   rn   rp   r   r   r*   r=   r   rs   rt   )r0   r.   r*   ru   r!   r   r   r   rO   rP   rQ   rR   rS   rN   rU   rV   rW   )r/   r=   r    rX   rY   rZ   r[   r\   r]   r   rv   r   r_   rw   r   s                 @r   "test_event_send_merges_meet_toggler      s    
+FEG

$
$%r2tR9CAKKSF*5J<H I J K '?,n,?n,,,,?n,,,?,,,n,,,,,,,&>"U">U"""">U""">"""U"""""""+.,.,....,......,.......)S!)T)!T))))!T))))))7)))7)))))))))S)))S)))!)))T)))))))r   c                   t        |       }d }| j                  t        d|       t        j                  dddd      }t        j                  t        j                  |            \  }}g }| }|}|rd}	|	|v }
|
}|s$dd	d
t        j                         v st        j                  |      rt        j                  |      nd
iz  }|j                  |       |rt        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  }|j                  |       t        j                  |d      i z  }dd|iz  }t!        t        j"                  |            dx}x}x}x}	}
t        j$                  } ||      }d}
||
u}|s
t        j                  d|fd||
f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      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}}
d |D        }t'        |      }|sddt        j                         v st        j                  t&              rt        j                  t&              ndt        j                  |      t        j                  |      dz  }t!        t        j"                  |            dx}}y)znSMTP blowing up must not eat the draft: send_draft reports the failure
    and the card stays for another try.c                    t        d      )N	smtp down)RuntimeErrorr{   s       r   boomz/test_send_failure_keeps_card_open.<locals>.boom   s    ;''r   r   r   r2   r   r   r   znot %(py2)sr?   r   r   )z%(py6)s in %(py8)sr   )r<   rA   %(py10)srt   r   assert %(py13)spy13N)is not)zS%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.get_draft
}(%(py3)s)
} is not %(py8)sr*   r=   r   rs   c              3  ,   K   | ]  }|d    dk7    yw)r   r   Nr   )r   r   s     r   r   z4test_send_failure_keeps_card_open.<locals>.<genexpr>   s     4Vqt|#Vs   r   allr   )r0   r.   r   r*   rM   r   r   r   rU   rV   rO   rW   rQ   r   rP   r   rR   rS   rN   r   )r/   r(   r   r=   r   r   r]   rY   rX   r^   rv   r`   r_   rw   r   @py_format14r   r   r[   s                      r   !test_send_failure_keeps_card_openr      s    ;F( w5

$
$YC
=Ckk',,S12GB(r6(6(6k(kS(((((((r(((r((((6(((kS(((k((((((S(((S((((((((((((((-S!--!----!------7---7---------S---S---!----------4V4434444444443444344444444444444r   c           	        t        |        t               }t        j                  |j	                  dddddid             |j
                  }|j                  }|j                  }g }||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                  |      t        j                  |      d
z  }dd|iz  }t        t        j                  |            dx}x}x}x}}y)ut   A card_action for a draft that no longer exists (double click, restart)
    must be a no-op — no crash, no speech.r4   deadbeefr   rC   r   r   r5   r   r    r   r   r   N)r0   r!   r   r   r   r   r   r	   rO   rP   rU   rV   rW   rQ   rR   rS   )	r/   r    r]   rY   r^   r   rv   r   r   s	            r   &test_stale_or_unknown_draft_is_ignoredr      s     
+AKKZ6# % & ' 55"5;;";""""""""""""1"""1"""5""";""""""""""""""r   c                   t        |        g dfd	}| j                  t        d|       t        j                  dddddg      }t        j                  t        j                  |            \  }}g }|}|rd	   d
   }dg}||k(  }	|	}|sdddt        j                         v st        j                  |      rt        j                  |      ndiz  }
|j                  |
       |r_t        j                  d	fdf      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}	}fd}| j                  t        d|       t        j                  dddd      }t        j                  t        j                  |            \  }}g }|}|rd	   d
   }d}||k(  }	|	}|sdddt        j                         v st        j                  |      rt        j                  |      ndiz  }
|j                  |
       |r_t        j                  d|	fd||f      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}	}y)zA draft holding attachments hands them to email_control._send; a draft
    without any uses the exact legacy 4-arg call (old fakes keep working).Nc                .    j                  | |d       yNrC   rG   r   r%   )rC   rE   rF   rD   rG   callss        r   r   z=test_send_draft_passes_attachments_to_send.<locals>.fake_send   s    B{;<r   r   r   r2   r   r   z/tmp/report.pdfrG   r   r?   r   r5   )z%(py5)s == %(py8)s)rL   rA   r   rt   r   r   r   c                .    j                  | dd       y)Nlegacyr   r   r%   )rC   rE   rF   rD   r   s       r   legacy_sendz?test_send_draft_passes_attachments_to_send.<locals>.legacy_send   s    Bx89r   r   r   )r0   r.   r   r*   rM   r   r   r   rU   rV   rO   rW   rQ   r   rP   r   rR   rS   )r/   r   r=   r   _r]   rX   r   rv   r   r   r_   rw   r   r   r   rg   r   s                    @r   *test_send_draft_passes_attachments_to_sendr      s    
+E w	:

$
$2sC"3!46CKK**3/0EBA2A2%)M*A/@.AA*.AAAAAAAA2AAA2AAAA2AAA*.AAAA*AAA.AAAAAAAAAAAAAAA w<%%iS#>DKK**401EB6262%)M*6h6*h66666662666266662666*h666*666h66666666666666r   c                   t        |        t               }t        j                  dddddg      }t	        j
                  |j                  d|dddddddgd	d
             |j                  }|j                  }|j                  }g }||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                  |      t        j                  |      dz  }dd|iz  }	t!        t        j"                  |	            dx}x}x}x}}t        j$                  |      d   d   }
ddg}|
|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
                  |j                  d|dddid
             t        j$                  |      d   d   }
ddg}|
|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
                  |j                  d|ddg id
             t        j$                  |      d   d   }
g }|
|k(  }|slt        j                  d|fd|
|f      t        j                  |
      t        j                  |      dz  }dd|iz  }t!        t        j"                  |            dx}
x}}y)zThe HUD's card_action fields.attachments (Finder-picked / dropped /
    chip-removed files) merges into the draft like to/cc/subject/body: a list
    is stored as-is, a comma string is coerced, [] clears the staged files.r   r2   r   r   z/tmp/original.pdfr4   r   z/tmp/added.pngrC   rD   rE   rF   rG   r   r5   r   r    r   r   r   Nrf   rG   r7   r8   r;   r<   z/tmp/x.txt, /tmp/y.txtz
/tmp/x.txtz
/tmp/y.txt)r0   r!   r*   rM   r   r   r   r   r   r	   rO   rP   rU   rV   rW   rQ   rR   rS   rN   )r/   r    r=   r]   rY   r^   r   rv   r   r   rX   rZ   r[   r\   s                 r   &test_hud_edit_merges_attachments_fieldr      s
    
+A

$
$2sC"5!68C KKSF""c#68H"IK L M N 55"5;;";""""""""""""1"""1"""5""";"""""""""""""S!(+M: /-?/ /: ?/ / /../: ?/ / /%.Y ; / /%.Y?/ / / / /../ / KKSF ":; = > ? S!(+M: $l?$ $: ?$ $ $##$: ?$ $ $#) ; $ $#)?$ $ $ $ $##$ $ KKSF "% ' ( ) S!(+M:@b@:b@@@@:b@@@:@@@b@@@@@@@r   c                   t        |        i dfd	}| j                  t        d|       t        j                  dddddg      }t               }t        j                  |j                  d	|d
dddddgdd             ddg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}}t        j"                  } ||      }d}	||	u }
|
s
t        j                  d|
fd||	f      dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      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)zaSend from the HUD: the fields' attachment list (after Ahmed's add/
    remove) is what SMTP gets.Nc                ,    j                  | |       yr   r}   )rC   rE   rF   rD   rG   r   s        r   r   z4test_hud_send_carries_attachments.<locals>.fake_send  s    r{3r   r   r   r2   r   r   z/tmp/old.pdfr4   r   z/tmp/final.pdfr   r   r   r5   rH   r   rI   rK   rL   rn   r   r*   r=   r   rs   rt   r   )r0   r.   r   r*   rM   r!   r   r   r   rO   rP   rU   rV   rW   rQ   rR   rS   rN   )r/   r   r=   r    rZ   r]   r`   ra   r   rv   r   r_   rw   r   s                @r   !test_hud_send_carries_attachmentsr     s    
+D w	:

$
$YC~>N
OCAKKSF""c#3"46 7 8 9 $5E4FGG4GGGGG4GGGGGGG4GGG4GGGGGGGGGGG)S!)T)!T))))!T))))))7)))7)))))))))S)))S)))!)))T)))))))r   c                  # ddl }g # G #fdd      }| j                  |d|       | j                  dd       | j                  dd	       |d
z  }|j                  d       t	        j
                  ddddt        |      t        |dz        g      }#d   }|j                  } |       }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }	t        t        j                  |	            dx}}|j                         D 
cg c]  }
|
 }}
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   }|j$                  } |       }d}||k(  }|st        j"                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }d d!|iz  }t        t        j                  |            dx}x}x}x}}|d   }|j&                  } |       }d
}||k(  }|st        j"                  d|fd"||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }d d!|iz  }t        t        j                  |            dx}x}x}x}}d#}|d   }|j(                  }d$} ||      }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't        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      d(z  }d)d*|iz  }t        t        j                  |            dx}x}x}x}x}x}}|d   }|j*                  }d+} ||,      }d}||k(  }|st        j"                  d|fd-||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |      d.z  }d/d0|iz  }t        t        j                  |            dx}x}x}x}x}}g }d1}||v }|}|rd}||v }|}|sXt        j"                  d%|fd2||f      t        j                  |      d3t        j                         v st        j                  |      rt        j                  |      nd3d4z  }d5d6|iz  }|j-                  |       |rt        j"                  d%fd7||f      t        j                  |      d3t        j                         v st        j                  |      rt        j                  |      nd3d8z  }d9d:|iz  }|j-                  |       t        j.                  |d      i z  }d;d<|iz  } t        t        j                  |             dx}x}x}x}x}}t	        j
                  ddd=d      }!#d   }|j                  } |       }| }|s`d>t        j                  |      t        j                  |      t        j                  |      d?z  }t        t        j                  |            dx}x}x}}d@}|!|k(  }|st        j"                  d|fdA|!|f      dBt        j                         v st        j                  |!      rt        j                  |!      ndBt        j                  |      dCz  }"dDdE|"iz  }t        t        j                  |            dx}}yc c}
w )Fu   email_control._send with attachments: real file → an application/
    octet-stream part with Content-Disposition attachment; missing file →
    skipped + noted, never fatal. No attachments → NOT multipart (legacy
    message untouched). SMTP is faked — nothing leaves the machine.r   Nc                  4    e Zd ZddZd Zd Zd Zd fd	Zy)3test_send_builds_mime_attachments.<locals>.FakeSMTPNc                     y r   r   )r   hostporttimeouts       r   r   z<test_send_builds_mime_attachments.<locals>.FakeSMTP.__init__1      r   c                    | S r   r   r
   s    r   	__enter__z=test_send_builds_mime_attachments.<locals>.FakeSMTP.__enter__4  s    Kr   c                     y)NFr   )r   as     r   __exit__z<test_send_builds_mime_attachments.<locals>.FakeSMTP.__exit__7  s    r   c                     y r   r   )r   userpws      r   loginz9test_send_builds_mime_attachments.<locals>.FakeSMTP.login:  r   r   c                (    j                  |       y r   r%   )r   msgto_addrs	sent_msgss      r   send_messagez@test_send_builds_mime_attachments.<locals>.FakeSMTP.send_message=  s    S!r   r   )r   r   r   r   r   r   r   r  )r   s   r   FakeSMTPr   0  s    					"r   r  SMTP_SSLEMAIL_ADDRESSzjarvis@x.comEMAIL_PASSWORDr   zq3 report.pdfs	   %PDF-faker   NumberszSee attached.r2   zmissing.binr   zGassert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.is_multipart
}()
}r   r   re   r5   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenparts)r>   r9   rJ   r<   r@   rA   zapplication/octet-stream)zO%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.get_content_type
}()
} == %(py8)s)r9   rJ   rL   rA   rs   rt   )zK%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.get_filename
}()
} == %(py8)s
attachmentzContent-Dispositionr   )zk%(py1)s in %(py13)s
{%(py13)s = %(py3)s(%(py11)s
{%(py11)s = %(py7)s
{%(py7)s = %(py5)s.get
}(%(py9)s)
})
}str)r9   rJ   rL   r   r   r   r   zassert %(py15)spy15T)decode)zY%(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.get_payload
}(decode=%(py5)s)
} == %(py10)s)r9   rJ   rL   r   rt   r   r   z1 attachment)z%(py3)s in %(py5)sr   )rJ   rL   z%(py7)sr   )z%(py10)s in %(py12)s)rt   r   z%(py14)spy14zassert %(py17)spy17zBody.zKassert not %(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.is_multipart
}()
})r9   rJ   rL   zSent 'Numbers' to a@x.com.rH   out2rI   rK   rL   )smtplibr.   setenvwrite_bytesr   r   r
  is_multipartrU   rV   rO   rW   rQ   rR   rS   iter_attachmentsr  rP   get_content_typeget_filenamegetget_payloadr   r   )$r/   tmp_pathr  r  r   r   r   r]   rY   r[   pr  rZ   r^   r   r\   r_   rX   rv   r   rw   r   @py_assert10@py_assert12r   @py_format16@py_assert9r   @py_assert11ra   r   @py_format15@py_format18r  r`   r   s$                                      @r   !test_send_builds_mime_attachmentsr"  '  s   
 I" "  X67'.?"AMM,


iOR"1vs8m+C'DEGC
B-C33,,./.1Q.E/u::?:33uu:8D8$$D$&D*DD&*DDDDD&*DDDD8DDD$DDD&DDD*DDDDDDDD858  5 "5o5"o5555"o5558555 555"555o5555555CuQxCx||C,AC|,ABC3BCC<CCCCC<CCCC<CCCCCC3CCC3CCCxCCC|CCC,ACCCBCCCCCCCCCCCC8<8<t<t,<<,<<<<,<<<8<<<<<<t<<<,<<<<<<<<<<<9>9>S 9]9]c%99999>S999>999999S999S9999999]c999]999999c999c99999999999999 y)WbAD}+}))+)++++++++}+++)++++++++++//4/////4///////4///4/////////// 0s   	c)!__doc__
__future__r   builtinsrU   _pytest.assertion.rewrite	assertionrewriterO   r   voice.composer*   voice.controlr   voicer   r   r   r!   r0   rb   rh   rx   r   r   r   r   r   r   r   r   r   r"  r   r   r   <module>r,     s    #      
   ! !

%"-+4O(E0*(5 #74A@**.0r   