
    @iF%                         d 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mZ ej        dedefd            Zej        defd	            Z G d
 d          Z G d d          Z G d d          ZdS )z
Integration tests for health data sync flow.

Tests the complete sync pipeline with mocked Garmin API,
verifying storage and repository layers are correctly updated.
    N)date)Path)	MagicMockpatchtmp_pathreturnc                 8    | dz  }|                                  |S )z0Create an isolated data directory for each test.health)mkdir)r   data_dirs     9/root/projects/butler/tests/integration/test_sync_flow.pyisolated_health_dirr      s#     ("HNNO    c                  j    t                      } d| j        _        d| j        _        d| j        _        | S )z8Mock HealthRepository that reports no pre-existing data.N)r   get_daily_metric_pathreturn_valueindex_daily_metricget_daily_metric_record)repos    r   	mock_repor      s3     ;;D.2D++/D(04D -Kr   c                   R    e Zd ZdZdeddfdZdeddfdZdeddfdZdeddfdZdS )	TestManualLogFlowz+Test manual logging write + read roundtrip.r   r   Nc                    ddl m} ddlm}  ||          }t	          j                    } |ddd          }|                    ||           |                    |          }|j        }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        |	          t          j        |
          dz  }dd|iz  }t!          t          j        |                    dx}x}	x}}
|j        d         }|j        }d}||k    }	|	st          j        d
|	fd||f          t          j        |          t          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    dx}x}x}	}|j        d         }|j        }d}||k    }	|	st          j        d
|	fd||f          t          j        |          t          j        |          t          j        |          dz  }dd|iz  }t!          t          j        |                    dx}x}x}	}dS )z,Log a diet entry and read it back correctly.r   ManualLogStorage	DietEntryr   z12:30u   鸡胸肉 + 糙米lunchtimedescription	meal_type   ==zR%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.diet_entries
})
} == %(py8)slenlogpy0py1py3py5py8assert %(py10)spy10N)z3%(py3)s
{%(py3)s = %(py1)s.description
} == %(py6)s)r,   r-   py6zassert %(py8)sr/   )z1%(py3)s
{%(py3)s = %(py1)s.meal_type
} == %(py6)s)"health.services.manual_log_storager   health.models.manual_logr   r   todayadd_diet_entryload_logdiet_entriesr(   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr"   r#   )selfr   r   r   storager5   entryr)   @py_assert2@py_assert4@py_assert7@py_assert6@py_format9@py_format11@py_assert0@py_assert5@py_format7s                    r   test_diet_log_roundtripz)TestManualLogFlow.test_diet_log_roundtrip#   sb   GGGGGG666666"",?@@@
	w4HT[\\\ue,,,u%%#)s#$$))$))))))))))$))))))))))))s)))))s)))))))))))))3)))))3))))))#))))$)))))))))))))))))))))))))))))))))"F".F2FF.2FFFFFFFFFFF.2FFFFFF"FFFF.FFFF2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"7",77,7777777777,77777"7777,77777777777777777777777777777777777r   c                 n   ddl m} ddlm}m}m}  ||          }t          j                    }|                    | |ddd                     |	                    | |d	d
d                     |
                    | |ddd                     |                    |          }|j        }	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        |
          t          j        |          dz  }dd|iz  }t)          t          j        |                    dx}	x}
x}}|j        }	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        |
          t          j        |          dz  }dd|iz  }t)          t          j        |                    dx}	x}
x}}|j        }	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        |
          t          j        |          dz  }dd|iz  }t)          t          j        |                    dx}	x}
x}}dS )z7Multiple log types written on the same day all persist.r   r   )r   AlcoholEntrySupplementEntryr   z08:00u   燕麦	breakfastr    z19:00u   红酒200ml)r!   
drink_typeamountz07:00u   镁400mg)r!   supplement_namedosager$   r%   r'   r(   r)   r*   r0   r1   NzU%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.alcohol_entries
})
} == %(py8)s)zX%(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.supplement_entries
})
} == %(py8)s)r3   r   r4   r   rO   rP   r   r5   r6   add_alcohol_entryadd_supplement_entryr7   r8   r(   r9   r:   r;   r<   r=   r>   r?   r@   alcohol_entriessupplement_entries)rA   r   r   r   rO   rP   rB   r5   r)   rD   rE   rF   rG   rH   rI   s                  r    test_multiple_log_types_same_dayz2TestManualLogFlow.test_multiple_log_types_same_day3   s   GGGGGGUUUUUUUUUU"",?@@@
uiiW(^i&j&j&jkkk!!%7x`g)h)h)hiii$$UOOZ_ho,p,p,pqqqu%%#)s#$$))$))))))))))$))))))))))))s)))))s)))))))))))))3)))))3))))))#))))$)))))))))))))))))))))))))))))))))&,s&'',1,'1,,,,,,,,,,'1,,,,,,,,,,,,s,,,,,s,,,,,,,,,,,,,3,,,,,3,,,,,,&,,,,',,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)/s)**/a/*a//////////*a////////////s/////s/////////////3/////3//////)////*////a///////////////////////////////r   c           	         ddl m} ddlm}  ||          }t	          j                    }dD ]'\  }}|                    | |d||                     (|                    |          }|j        }	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        |
          t          j        |          dz  }dd|iz  }t!          t          j        |                    dx}	x}
x}}d |j        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}}	dS )z;Multiple diet entries on the same day all append correctly.r   r   r   r   ))   早餐rQ   )u   午餐r   )   晚餐dinnerz09:00r       r%   r'   r(   r)   r*   r0   r1   Nc                     g | ]	}|j         
S  )r"   ).0es     r   
<listcomp>zETestManualLogFlow.test_appending_multiple_entries.<locals>.<listcomp>Q   s    @@@!@@@r   r_   )in)z%(py1)s in %(py3)sdescriptions)r,   r-   assert %(py5)sr.   r`   )r3   r   r4   r   r   r5   r6   r7   r8   r(   r9   r:   r;   r<   r=   r>   r?   r@   )rA   r   r   r   rB   r5   mealr#   r)   rD   rE   rF   rG   rH   rI   ri   rJ   @py_format4@py_format6s                      r   test_appending_multiple_entriesz1TestManualLogFlow.test_appending_multiple_entriesD   sv   GGGGGG666666"",?@@@
c 	j 	jOD)""5))d^g*h*h*hiiiiu%%#)s#$$))$))))))))))$))))))))))))s)))))s)))))))))))))3)))))3))))))#))))$)))))))))))))))))))))))))))))))))@@s/?@@@'x<''''''''''x<'''''x'''''''''''<'''''<''''''''''''''''''''''''''''x<''''''''''x<'''''x'''''''''''<'''''<'''''''''''''''''''''''''''''r   c                 *   ddl m}  ||          }|                    t          ddd                    }|j        }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        |          t          j        |          d
z  }	dd|	iz  }
t          t          j        |
                    dx}x}x}}|j        }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        |          t          j        |          d
z  }	dd|	iz  }
t          t          j        |
                    dx}x}x}}dS )zELoading logs for a date with no data returns an empty DailyManualLog.r   r   r   i  r$   r%   r'   r(   r)   r*   r0   r1   NrX   )r3   r   r7   r   r8   r(   r9   r:   r;   r<   r=   r>   r?   r@   r[   )rA   r   r   rB   r)   rD   rE   rF   rG   rH   rI   s              r   ,test_load_nonexistent_date_returns_empty_logz>TestManualLogFlow.test_load_nonexistent_date_returns_empty_logU   s   GGGGGG"",?@@@tD!Q//00#)s#$$))$))))))))))$))))))))))))s)))))s)))))))))))))3)))))3))))))#))))$)))))))))))))))))))))))))))))))))&,s&'',1,'1,,,,,,,,,,'1,,,,,,,,,,,,s,,,,,s,,,,,,,,,,,,,3,,,,,3,,,,,,&,,,,',,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,r   )	__name__
__module____qualname____doc__r   rM   r]   rn   rp   rd   r   r   r   r       s        5584 8D 8 8 8 8 0D 0T 0 0 0 0"(4 (D ( ( ( ("-PT -Y] - - - - - -r   r   c                   b    e Zd ZdZdededdfdZdededdfdZdededdfdZdededdfd	Z	dS )
TestHealthStoragez8Test JSON-based daily metric storage (with mocked repo).r   r   r   Nc                 p   ddl m} ddlm} t	          d|          5   ||          }ddd           n# 1 swxY w Y   t          j                    } ||dd	
          }|                    |d           |                    d|          }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}
}	|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S )z4Save a metric and load it back with correct content.r   HealthStorage	StepsData(health.services.storage.HealthRepositoryr   r   Ni@  g      @r   total_stepstotal_distance_metersstepsis notz%(py0)s is not %(py3)sloadedr+   r-   rj   r.   r   r%   z%(py1)s == %(py4)sr,   py4assert %(py6)sr2   )health.services.storagery   health.models.daily_metricsr{   r   r   r5   save_daily_metricload_daily_metricr9   r:   r;   r<   r=   r>   r?   r@   )rA   r   r   ry   r{   rB   r5   r   r   rD   @py_assert1rl   rm   rJ   @py_assert3@py_format5rL   s                    r   test_save_and_load_metricz+TestHealthStorage.test_save_and_load_metricb   sr   999999999999=IVVV 	B 	B#m-@AAAG	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 
	u$fUUU!!%111**7E::!!vT!!!!!!!!!!vT!!!!!!!!!!!!v!!!!!v!!!!!!T!!!!!!!!!!!!!!!!!!!!!!!!!m$,,$,,,,,,,,,,$,,,,,$,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,   7;;c           
         ddl m} t          d|          5   ||          }ddd           n# 1 swxY w Y   |j        }d}t          j        } |            } |||          }	|	 }
|
sd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
t          j
        |          t          j
        |          t          j
        |	          dz  }t          t          j        |                    dx}x}x}x}x}	}
dS )zCmetric_exists returns False when no index entry and no file exists.r   rx   r|   r}   r   Nsleepzassert not %(py11)s
{%(py11)s = %(py2)s
{%(py2)s = %(py0)s.metric_exists
}(%(py4)s, %(py9)s
{%(py9)s = %(py7)s
{%(py7)s = %(py5)s.today
}()
})
}rB   r   )r+   py2r   r.   py7py9py11)r   ry   r   metric_existsr   r5   r;   r<   r9   r=   r>   r?   r@   )rA   r   r   ry   rB   r   r   rG   @py_assert8@py_assert10@py_assert12@py_format13s               r   #test_metric_not_indexed_before_savez5TestHealthStorage.test_metric_not_indexed_before_saver   s   999999=IVVV 	B 	B#m-@AAAG	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B (??$*?**,,?((,??????????????????7?????7??????(???????????????$?????$??????*????,??????????????????????????????????s   155c                    ddl m} ddlm} t	          j                    }t          d|          5   ||          }ddd           n# 1 swxY w Y    ||d	          }|                    |d
           ddlm	} |j
        d
         d         }	t          |j                  |j        d}}
||	z  |
z  |z  |                                 dz  }t          |          |j        _        |j        }d
} |||          }|sddt%          j                    v st)          j        |          rt)          j        |          ndt)          j        |          t)          j        |          dt%          j                    v st)          j        |          rt)          j        |          ndt)          j        |          dz  }t/          t)          j        |                    dx}x}}dS )z3metric_exists returns True after a metric is saved.r   rx   rz   r|   r}   r   N  r   r   r   storage_path02d.jsonzXassert %(py7)s
{%(py7)s = %(py2)s
{%(py2)s = %(py0)s.metric_exists
}(%(py4)s, %(py5)s)
}rB   r5   )r+   r   r   r.   r   )r   ry   r   r{   r   r5   r   r   health.configconfigDATA_TYPE_CONFIGstryearmonth	isoformatr   r   r   r;   r<   r9   r=   r>   r?   r@   )rA   r   r   ry   r{   r5   rB   r   hconfigr   r   r   expected_pathr   r   rG   @py_format8s                    r   test_metric_exists_after_savez/TestHealthStorage.test_metric_exists_after_save|   sq   999999999999
=IVVV 	B 	B#m-@AAAG	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	u$777!!%111 	('''''/8H%*oo%+';';e+l:TAEIuO`O`LgLgLgg7:=7I7I	'4$4W4$$We444444444444444w44444w444444$4444W44444444444e44444e444444444444444444444444444444s   A

AAc                 f   ddl m} ddlm} t	          d|          5   ||          }ddd           n# 1 swxY w Y   t          j                    }|                     ||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S )z:Saving the same metric twice overwrites the previous data.r   rx   rz   r|   r}   r   Ni  r   r   i'  r   r%   r   r   r   r2   )r   ry   r   r{   r   r   r5   r   r   r9   r:   r>   r?   r@   )rA   r   r   ry   r{   rB   r5   r   rJ   r   rD   r   rL   s                r   test_overwrite_metricz'TestHealthStorage.test_overwrite_metric   s   999999999999=IVVV 	B 	B#m-@AAAG	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 
!!))D"I"I"I7SSS!!))D"I"I"I7SSS**7E::m$,,$,,,,,,,,,,$,,,,,$,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,r   )
rq   rr   rs   rt   r   r   r   r   r   r   rd   r   r   rv   rv   _   s        BB-T -i -\` - - - - @t @Xa @fj @ @ @ @5 5R[ 5`d 5 5 5 5,- -) -X\ - - - - - -r   rv   c                   N    e Zd ZdZdededefdZdededdfdZdededdfdZ	dS )	TestHealthDataSyncWithMocksz<Test HealthDataSync orchestration with mocked Garmin client.r   r   r   c                     ddl m} ddlm} t	          d|          5   ||          }ddd           n# 1 swxY w Y   t                      } ||||          }|||fS )	z7Helper to create sync service with mocked dependencies.r   rx   )HealthDataSyncr|   r}   r   N)clientrB   r   )r   ry   health.services.data_syncr   r   r   )rA   r   r   ry   r   rB   mock_garminsyncs           r   
_make_syncz&TestHealthDataSyncWithMocks._make_sync   s    999999<<<<<<=IVVV 	B 	B#m-@AAAG	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B  kk~['	RRRWk))r   Nc                 :   ddl m} |                     ||          \  }}}t          j                    }|                     ||d          d           ddlm} |j        d         d         }	t          |j
                  |j        d}}
||	z  |
z  |z  |                                 d	z  }t          |          |j        _        |                    d|d
          }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                                         dS )z>sync_daily_metric skips already-synced dates when force=False.r   rz   r   r   r   Nr   r   r   Fforceisz%(py0)s is %(py3)sresultr   rj   r.   )r   r{   r   r   r5   r   r   r   r   r   r   r   r   r   r   sync_daily_metricr9   r:   r;   r<   r=   r>   r?   r@   fetch_stepsassert_not_called)rA   r   r   r{   r   rB   r   r5   r   r   r   r   	file_pathr   rD   r   rl   rm   s                     r   test_sync_skips_existing_dataz9TestHealthDataSyncWithMocks.test_sync_skips_existing_data   s   999999%)__5H)%T%T"g{
 	!!))D"I"I"I7SSS 	('''''/8H%*oo%+';';e',6=E5??K\K\HcHcHcc	7:9~~	'4''e'DDvvvv1133333r   c                    ddl m} |                     ||          \  }}}t          j                    } ||dd          }||j        _        |                    d|d          }	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                            |           |                    d|          }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}}
|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S )z@sync_daily_metric re-fetches and saves new data when force=True.r   rz   i.  g     @r~   r   Tr   r   r   r   r   rj   r.   Nr   r   r   r   r%   r   r   r   r2   )r   r{   r   r   r5   r   r   r   r9   r:   r;   r<   r=   r>   r?   r@   assert_called_once_withr   )rA   r   r   r{   r   rB   r   r5   	new_stepsr   rD   r   rl   rm   r   rJ   r   r   rL   s                      r   test_sync_forces_overwritez6TestHealthDataSyncWithMocks.test_sync_forces_overwrite   s   999999%)__5H)%T%T"g{
 I5eSYZZZ	/8,''d'CCv~vvv77>>>**7E::!!vT!!!!!!!!!!vT!!!!!!!!!!!!v!!!!!v!!!!!!T!!!!!!!!!!!!!!!!!!!!!!!!!m$--$----------$-----$---------------------------------r   )
rq   rr   rs   rt   r   r   tupler   r   r   rd   r   r   r   r      s        FF
*d 
*y 
*U 
* 
* 
* 
*4 4R[ 4`d 4 4 4 4,.d .y .]a . . . . . .r   r   )rt   builtinsr;   _pytest.assertion.rewrite	assertionrewriter9   pytestdatetimer   pathlibr   unittest.mockr   r   fixturer   r   r   rv   r   rd   r   r   <module>r      sU                             * * * * * * * * $ 4     9    <- <- <- <- <- <- <- <-~@- @- @- @- @- @- @- @-F6. 6. 6. 6. 6. 6. 6. 6. 6. 6.r   