§
    ¹¿jÒ3  ã                  ó|  — d Z ddlmZ ddlZddlZddlZddlZddlZddl	m
Z
 ddlmZmZ ddlZddlmZ ddlmZ ddlmZ  ej        d	¬
¦  «        dd„¦   «         Z ej        d	¬
¦  «        dd„¦   «         Zej        dd„¦   «         Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        Z G d„ d¦  «        ZdS )zÙ
Adversarial Stress Test Suite for FreeExile 30 Map Styles Database & Service.
Verifies WAL concurrency, boundary edge cases, SQL injection immunity,
schema CHECK constraints, and missing/corrupted database recovery.
é    )ÚannotationsN)ÚPath)Ú	GeneratorÚList)Úinit_db©ÚMapStyleService)ÚMapStyleDefinitionÚmodule)ÚscopeÚreturnr   c                 óx   — t          t          ¦  «                             ¦   «         j        j        j        dz  dz  S )z,Return canonical path to data/map_styles.db.Údataúmap_styles.db)r   Ú__file__ÚresolveÚparent© ó    ú?C:\Projects\FreeExile\tests\unit\test_map_styles_adversarial.pyÚcanonical_db_pathr      s/   € õ •‰>Œ>×!Ò!Ñ#Ô#Ô*Ô1Ô8¸6ÑAÀOÑSÐSr   r   r	   c                ó"   — t          | ¬¦  «        S )z0Provide service connected to canonical database.©Údb_pathr   )r   s    r   Úservicer      s   € õ Ð#4Ð5Ñ5Ô5Ð5r   úGenerator[str, None, None]c               #  ó¨   K  — t          j        d¬¦  «        } t          j                             | d¦  «        }|V — t          j        | d¬¦  «         dS )zLProvide isolated temporary directory and database path for mutation testing.Úfe_adv_test_©Úprefixzisolated_map_styles.dbT©Úignore_errorsN)ÚtempfileÚmkdtempÚosÚpathÚjoinÚshutilÚrmtree)ÚtmpdirÚdb_files     r   Úisolated_temp_dbr,   #   sU   è è € õ Ô ^Ð4Ñ4Ô4€FÝŒg�lŠl˜6Ð#;Ñ<Ô<€GØ
€M€M€Må
„M�&¨Ð-Ñ-Ô-Ð-Ð-Ð-r   c                  ó*   — e Zd ZdZdd„Zdd	„Zdd
„ZdS )ÚTestMapStyleWALConcurrencyzFAdversarial testing of SQLite WAL mode and multi-threaded concurrency.r   r	   r   ÚNonec                ór  ‡‡‡— dˆfd„Št           j                             d¬¦  «        5 Šˆˆfd„t          d	¦  «        D ¦   «         }d
„ t           j                             |¦  «        D ¦   «         }ddd¦  «         n# 1 swxY w Y   t          |¦  «        d	k    sJ ‚t          d„ |D ¦   «         ¦  «        sJ ‚dS )zA50 worker threads querying styles, tiles, and props concurrently.Ú	worker_idÚintr   Úboolc                óp  •— | dz  dz   }‰                      |¦  «        }|�|j        |k    sJ ‚‰                     |j        ¦  «        }t	          |¦  «        dk    sJ ‚‰                     |j        d¦  «        }|�|j        dk    sJ ‚‰                     |j        ¦  «        }t	          |¦  «        dk    sJ ‚dS )Né   é   é   é   T)Úget_style_by_codeÚ
biome_codeÚget_tiles_for_styleÚstyle_idÚlenÚget_tile_mappingÚ	tile_codeÚget_props_for_style)r1   ÚcodeÚsÚtilesÚtileÚpropsr   s         €r   Úreader_taskzSTestMapStyleWALConcurrency.test_high_volume_concurrent_readers.<locals>.reader_task2   sÃ   ø€ Ø ‘N aÑ'ˆDØ×)Ò)¨$Ñ/Ô/ˆAØ�= Q¤\°TÒ%9Ð%9Ð%9Ð9Ø×/Ò/°´
Ñ;Ô;ˆEÝ�u‘:”: Ò#Ð#Ð#Ð#Ø×+Ò+¨A¬J¸Ñ:Ô:ˆDØÐ#¨¬¸!Ò(;Ð(;Ð(;Ð;Ø×/Ò/°´
Ñ;Ô;ˆEÝ�u‘:”: ’?�?�?�?Ø�4r   é2   ©Úmax_workersc                ó<   •— g | ]}‰                      ‰|¦  «        ‘ŒS r   ©Úsubmit©Ú.0ÚiÚpoolrF   s     €€r   ú
<listcomp>zRTestMapStyleWALConcurrency.test_high_volume_concurrent_readers.<locals>.<listcomp>?   s'   ø€ ÐGÐGÐG°q�t—{’{ ;°Ñ2Ô2ÐGÐGÐGr   éú   c                ó6   — g | ]}|                      ¦   «         ‘ŒS r   ©Úresult©rN   Úfs     r   rQ   zRTestMapStyleWALConcurrency.test_high_volume_concurrent_readers.<locals>.<listcomp>@   s    € ÐTÐTÐT a�q—x’x‘z”zÐTÐTÐTr   Nc              3  ó   K  — | ]}|d u V — Œ	dS ©TNr   ©rN   Úrs     r   ú	<genexpr>zQTestMapStyleWALConcurrency.test_high_volume_concurrent_readers.<locals>.<genexpr>C   ó&   è è € Ð.Ð. �1˜�9Ð.Ð.Ð.Ð.Ð.Ð.r   )r1   r2   r   r3   )Ú
concurrentÚfuturesÚThreadPoolExecutorÚrangeÚas_completedr=   Úall)Úselfr   r_   ÚresultsrP   rF   s    `  @@r   Ú#test_high_volume_concurrent_readersz>TestMapStyleWALConcurrency.test_high_volume_concurrent_readers0   s)  øøø€ ð
	ð 
	ð 
	ð 
	ð 
	ð 
	õ Ô×2Ò2¸rÐ2ÑBÔBð 	UÀdØGÐGÐGÐGÐG½EÀ#¹J¼JÐGÑGÔGˆGØTÐT­:Ô+=×+JÒ+JÈ7Ñ+SÔ+SÐTÑTÔTˆGð	Uð 	Uð 	Uñ 	Uô 	Uð 	Uð 	Uð 	Uð 	Uð 	Uð 	Uøøøð 	Uð 	Uð 	Uð 	Uõ �7‰|Œ|˜sÒ"Ð"Ð"Ð"ÝÐ.Ð. gÐ.Ñ.Ô.Ñ.Ô.Ð.Ð.Ð.Ð.Ð.s   ªAA<Á<B ÂB r,   Ústrc                óâ  ‡‡‡‡	— t          |¬¦  «        Šdˆfd„Š	dˆfd„Št          j                             d¬	¦  «        5 Šˆˆ	fd
„t	          d¦  «        D ¦   «         }ˆˆfd„t	          d¦  «        D ¦   «         }||z   }d„ t          j                             |¦  «        D ¦   «         }ddd¦  «         n# 1 swxY w Y   t          |¦  «        dk    sJ ‚t          d„ |D ¦   «         ¦  «        sJ ‚dS )zUConcurrent readers querying database while writers execute seed_database(force=True).r   r   r3   c                 óh   •— t          d¦  «        D ] } ‰                     d¬¦  «        }|dk    sJ ‚Œ!dS )Nr8   T)Úforcer5   )ra   Úseed_database)Ú_ÚcntÚmut_services     €r   Úwriter_taskzXTestMapStyleWALConcurrency.test_concurrent_read_during_force_reseed.<locals>.writer_taskI   sB   ø€ Ý˜1‘X”Xð !ð !�Ø!×/Ò/°dÐ/Ñ;Ô;�Ø˜b’y�y�y�y�yØ�4r   Úidxr2   c                óÈ   •— t          d¦  «        D ]P}‰                     | dz  dz   ¦  «        }|€J ‚‰                     |j        ¦  «        }t	          |¦  «        dk    sJ ‚ŒQdS )Né   r5   r6   r7   T)ra   r9   r;   r<   r=   )rp   rl   ÚstylerC   rn   s       €r   rF   zXTestMapStyleWALConcurrency.test_concurrent_read_during_force_reseed.<locals>.reader_taskO   ss   ø€ Ý˜2‘Y”Yð (ð (�Ø#×5Ò5°s¸R±xÀ1±nÑEÔE�ØÐ(Ð(Ð(Ø#×7Ò7¸¼ÑGÔG�Ý˜5‘z”z RÒ'Ð'Ð'Ð'Ð'Ø�4r   r7   rH   c                ó:   •— g | ]}‰                      ‰¦  «        ‘ŒS r   rK   )rN   rl   rP   ro   s     €€r   rQ   zWTestMapStyleWALConcurrency.test_concurrent_read_during_force_reseed.<locals>.<listcomp>X   s%   ø€ ÐHÐHÐH¸!˜TŸ[š[¨Ñ5Ô5ÐHÐHÐHr   é   c                ó<   •— g | ]}‰                      ‰|¦  «        ‘ŒS r   rK   rM   s     €€r   rQ   zWTestMapStyleWALConcurrency.test_concurrent_read_during_force_reseed.<locals>.<listcomp>Y   s'   ø€ ÐKÐKÐK¸A˜DŸKšK¨°QÑ7Ô7ÐKÐKÐKr   é   c                ó6   — g | ]}|                      ¦   «         ‘ŒS r   rT   rV   s     r   rQ   zWTestMapStyleWALConcurrency.test_concurrent_read_during_force_reseed.<locals>.<listcomp>[   s    € ÐXÐXÐX a�q—x’x‘z”zÐXÐXÐXr   Nc              3  ó   K  — | ]}|d u V — Œ	dS rY   r   rZ   s     r   r\   zVTestMapStyleWALConcurrency.test_concurrent_read_during_force_reseed.<locals>.<genexpr>^   r]   r   )r   r3   )rp   r2   r   r3   )r	   r^   r_   r`   ra   rb   r=   rc   )
rd   r,   Úwrite_futuresÚread_futuresÚall_futuresre   rn   rP   rF   ro   s
         @@@@r   Ú(test_concurrent_read_during_force_reseedzCTestMapStyleWALConcurrency.test_concurrent_read_during_force_reseedE   s…  øøøø€ å%Ð.>Ð?Ñ?Ô?ˆð	ð 	ð 	ð 	ð 	ð 	ð	ð 	ð 	ð 	ð 	ð 	õ Ô×2Ò2¸rÐ2ÑBÔBð 	YÀdØHÐHÐHÐHÐH½uÀQ¹x¼xÐHÑHÔHˆMØKÐKÐKÐKÐKÅÀrÁÄÐKÑKÔKˆLØ'¨,Ñ6ˆKØXÐX­:Ô+=×+JÒ+JÈ;Ñ+WÔ+WÐXÑXÔXˆGð		Yð 	Yð 	Yñ 	Yô 	Yð 	Yð 	Yð 	Yð 	Yð 	Yð 	Yøøøð 	Yð 	Yð 	Yð 	Yõ �7‰|Œ|˜rÒ!Ð!Ð!Ð!ÝÐ.Ð. gÐ.Ñ.Ô.Ñ.Ô.Ð.Ð.Ð.Ð.Ð.s   ÁA'B4Â4B8Â;B8c                óê  ‡— t          d¬¦  «        Š‰                     d¦  «        €J ‚d
ˆfd„}t          j                             d¬¦  «        5 }|                     |¦  «        }t          j        t          j	        ¦  «        5 }| 
                    ¦   «          ddd¦  «         n# 1 swxY w Y   d	t          |j        ¦  «        v sJ ‚	 ddd¦  «         n# 1 swxY w Y   ‰                     ¦   «          dS )zJIn-memory SQLite connection is bound to creation thread unless configured.z:memory:r   r6   Nr   r/   c                 ó2   •— ‰                       d¦  «         d S )Nr6   ©r9   )Úmem_services   €r   Úcross_thread_callz\TestMapStyleWALConcurrency.test_in_memory_service_thread_boundary.<locals>.cross_thread_calle   s   ø€ Ø×)Ò)¨!Ñ,Ô,Ð,Ð,Ð,r   rH   zGSQLite objects created in a thread can only be used in that same thread©r   r/   )r	   r9   r^   r_   r`   rL   ÚpytestÚraisesÚsqlite3ÚProgrammingErrorrU   rg   ÚvalueÚclose)rd   r‚   rP   ÚfutureÚexc_infor�   s        @r   Ú&test_in_memory_service_thread_boundaryzATestMapStyleWALConcurrency.test_in_memory_service_thread_boundary`   sˆ  ø€ å%¨jÐ9Ñ9Ô9ˆØ×,Ò,¨QÑ/Ô/Ð;Ð;Ð;ð	-ð 	-ð 	-ð 	-ð 	-ð 	-õ Ô×2Ò2¸qÐ2ÑAÔAð 	tÀTØ—[’[Ð!2Ñ3Ô3ˆFÝ”�wÔ7Ñ8Ô8ð  ¸HØ—’‘”�ð ð  ð  ñ  ô  ð  ð  ð  ð  ð  ð  øøøð  ð  ð  ð  à\Õ`cÐdlÔdrÑ`sÔ`sÐsÐsÐsÐsÐsð		tð 	tð 	tñ 	tô 	tð 	tð 	tð 	tð 	tð 	tð 	tøøøð 	tð 	tð 	tð 	tð
 	×ÒÑÔÐÐÐs6   Á4CÂB$ÂCÂ$B(	Â(CÂ+B(	Â,CÃCÃCN©r   r	   r   r/   )r,   rg   r   r/   rƒ   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rf   r}   rŒ   r   r   r   r.   r.   -   sV   € € € € € ØPÐPð/ð /ð /ð /ð*/ð /ð /ð /ð6ð ð ð ð ð r   r.   c                  ód  — e Zd ZdZej                             dg d¢¦  «        dd	„¦   «         Zej                             d
g d¢¦  «        dd„¦   «         Zej                             dddddg i  e	¦   «         g¦  «        dd„¦   «         Z
ej                             dg d¢¦  «        dd„¦   «         ZdS )Ú"TestMapStyleEdgeCasesAndBoundarieszIStress-test out-of-range boundaries, invalid types, and malformed inputs.Úinvalid_code)r   éÿÿÿÿéüÿÿé   é    éd   éÿ   é   éç  éÿÿÿi   €r   r	   r2   r   r/   c                ó4   — |                      |¦  «        �J ‚dS )zBAll out-of-range integer biome codes return None without crashing.Nr€   )rd   r   r”   s      r   Ú)test_out_of_range_biome_codes_return_nonezLTestMapStyleEdgeCasesAndBoundaries.test_out_of_range_biome_codes_return_nones   s$   € ð ×(Ò(¨Ñ6Ô6Ð>Ð>Ð>Ð>Ð>r   Úinvalid_tile)	r•   iöÿÿÿr–   r7   é   rG   r™   rœ   r�   c                ó:   — |                      d|¦  «        }|�J ‚dS )zAAll out-of-range integer tile codes return None without crashing.ÚSTY_01_HOANG_MANG_CO_LON)r>   )rd   r   r    Úmappings       r   Ú(test_out_of_range_tile_codes_return_nonezKTestMapStyleEdgeCasesAndBoundaries.test_out_of_range_tile_codes_return_nonex   s'   € ð ×*Ò*Ð+DÀlÑSÔSˆØˆˆˆˆˆr   Útype_mismatchÚ1g      ð?g      ø?NÚobjectc                ó¸   — 	 |                      |¦  «        }|�t          |t          ¦  «        sJ ‚dS dS # t          j        t          j        t          f$ r Y dS w xY w)z\Non-integer biome code inputs either return None or raise standard Python errors gracefully.N)r9   Ú
isinstancer
   r†   ÚInterfaceErrorr‡   Ú	TypeError)rd   r   r¦   Úress       r   Ú.test_biome_code_type_mismatches_handled_safelyzQTestMapStyleEdgeCasesAndBoundaries.test_biome_code_type_mismatches_handled_safely~   sr   € ð	Ø×+Ò+¨MÑ:Ô:ˆCàˆÝ! #Õ'9Ñ:Ô:Ð:Ð:Ð:ð ˆØ:Ð:øÝÔ&­Ô(@Å)ÐLð 	ð 	ð 	ØˆDˆDð	øøøs   ‚.4 ´!AÁAÚbad_style_id)Ú z   z	
Ústy_01_lowercaseÚUNKNOWN_NONEXISTENTrg   c                óÐ   — |                      |¦  «        �J ‚|                     |¦  «        g k    sJ ‚|                     |d¦  «        �J ‚|                     |¦  «        g k    sJ ‚dS )zJInvalid, empty, or uncanonical style_ids return None or empty collections.Nr6   ©Ú	get_styler;   r>   r@   )rd   r   r¯   s      r   Ú+test_invalid_style_ids_return_empty_or_nonezNTestMapStyleEdgeCasesAndBoundaries.test_invalid_style_ids_return_empty_or_none‰   s~   € ð × Ò  Ñ.Ô.Ð6Ð6Ð6Ø×*Ò*¨<Ñ8Ô8¸BÒ>Ð>Ð>Ð>Ø×'Ò'¨°aÑ8Ô8Ð@Ð@Ð@Ø×*Ò*¨<Ñ8Ô8¸BÒ>Ð>Ð>Ð>Ð>Ð>r   )r   r	   r”   r2   r   r/   )r   r	   r    r2   r   r/   )r   r	   r¦   r¨   r   r/   )r   r	   r¯   rg   r   r/   )rŽ   r�   r�   r‘   r„   ÚmarkÚparametrizerŸ   r¥   r¨   r®   r¶   r   r   r   r“   r“   p   s  € € € € € ØSÐSà„[×Ò˜^Ð-iÐ-iÐ-iÑjÔjð?ð ?ð ?ñ kÔjð?ð „[×Ò˜^Ð-]Ð-]Ð-]Ñ^Ô^ðð ð ñ _Ô^ðð
 „[×Ò˜_¨s°C¸¸dÀBÈÈFÈFÉHÌHÐ.UÑVÔVðð ð ñ WÔVðð „[×Ò˜^Ð-kÐ-kÐ-kÑlÔlð?ð ?ð ?ñ mÔlð?ð ?ð ?r   r“   c                  ó”   — e Zd ZdZdddddddd	d
ddddddddddz  dgZej                             de¦  «        dd„¦   «         Zd d„Z	dS )!Ú TestMapStyleSQLInjectionImmunityzCStress-test SQL injection attacks across all service query methods.z' OR '1'='1z'; DROP TABLE map_styles; --z!'; DROP TABLE map_style_tiles; --z!'; DROP TABLE map_style_props; --z#' UNION SELECT * FROM map_styles --z"STY_01_HOANG_MANG_CO_LO' OR '1'='1zadmin'--z' OR 1=1; --zSLEEP(5)z'; VACUUM; --zSTY_01' /* comment */z<script>alert('xss')</script>z' OR biome_code=1 --ú%rl   z&' AND (SELECT 1 FROM sqlite_master) --zSTY_01 _INJECTIONÚAi'  u   ðŸ�‰_LORE_STYLE_' OR 1=1;--Úpayloadr   r	   rg   r   r/   c                óà   — |                      |¦  «        }|�J ‚|                     |¦  «        }|g k    sJ ‚|                     |d¦  «        }|�J ‚|                     |¦  «        }|g k    sJ ‚dS )zTVerify queries with SQL injection payloads safely return None/empty and preserve DB.Nr6   r´   )rd   r   r½   r­   rC   rD   rE   s          r   Útest_sqli_payload_resistancez=TestMapStyleSQLInjectionImmunity.test_sqli_payload_resistance«   sˆ   € ð ×Ò Ñ(Ô(ˆØˆ{ˆ{ˆ{ð ×+Ò+¨GÑ4Ô4ˆØ˜Š{ˆ{ˆ{ˆ{ð ×'Ò'¨°Ñ3Ô3ˆØˆ|ˆ|ˆ|ð ×+Ò+¨GÑ4Ô4ˆØ˜Š{ˆ{ˆ{ˆ{ˆ{ˆ{r   c                ó²   — |                      ¦   «         }t          |¦  «        dk    sJ ‚d„ |D ¦   «         t          t          dd¦  «        ¦  «        k    sJ ‚dS )zLEnsure map_styles table remains exactly 30 records after injection attempts.r5   c                ó   — g | ]	}|j         ‘Œ
S r   )r:   )rN   rB   s     r   rQ   z_TestMapStyleSQLInjectionImmunity.test_database_integrity_after_sqli_barrage.<locals>.<listcomp>Â   s   € Ð-Ð-Ð- �”Ð-Ð-Ð-r   r6   r—   N)Úlist_stylesr=   Úlistra   )rd   r   Ústyless      r   Ú*test_database_integrity_after_sqli_barragezKTestMapStyleSQLInjectionImmunity.test_database_integrity_after_sqli_barrage¾   s_   € à×$Ò$Ñ&Ô&ˆÝ�6‰{Œ{˜bÒ Ð Ð Ð Ø-Ð- fÐ-Ñ-Ô-µµe¸A¸r±l´lÑ1CÔ1CÒCÐCÐCÐCÐCÐCr   N)r   r	   r½   rg   r   r/   r�   )
rŽ   r�   r�   r‘   ÚSQLI_PAYLOADSr„   r·   r¸   r¿   rÅ   r   r   r   rº   rº   ’   s­   € € € € € ØMÐMð 	Ø&Ø+Ø+Ø-Ø,ØØØØØØ'ØØØØ0ØØˆe‰Ø%ð'€Mð, „[×Ò˜Y¨Ñ6Ô6ðð ð ñ 7Ô6ðð$Dð Dð Dð Dð Dð Dr   rº   c                  óÎ   — e Zd ZdZdd„Zdd„Zdd„Zdd„Zej	         
                    dd	d
g¦  «        dd„¦   «         Zej	         
                    dddg¦  «        dd„¦   «         ZdS )Ú&TestMapStyleRecoveryAndSchemaIntegrityzKTest auto-creation, recovery from partial/empty DBs, and CHECK constraints.r   r/   c                ó  — t          j        d¬¦  «        }	 t          j                             |ddd¦  «        }t          j                             |¦  «        rJ ‚t          |¬¦  «        }t          j                             |¦  «        sJ ‚t          | 	                    ¦   «         ¦  «        dk    sJ ‚t          | 
                    d¦  «        ¦  «        d	k    sJ ‚	 t          j        |d
¬¦  «         dS # t          j        |d
¬¦  «         w xY w)zTService automatically creates missing directory, database file, and seeds 30 styles.Úfe_recovery_r   ÚnestedÚdeepr   r   r5   r£   r7   Tr!   N)r#   r$   r%   r&   r'   Úexistsr	   Úisfiler=   rÂ   r;   r(   r)   )rd   r*   Ú
missing_dbÚsvcs       r   Ú0test_missing_database_file_and_dir_auto_recoveryzWTestMapStyleRecoveryAndSchemaIntegrity.test_missing_database_file_and_dir_auto_recoveryÈ   sû   € åÔ!¨Ð8Ñ8Ô8ˆð		6ÝœŸš f¨h¸ÀÑPÔPˆJÝ”w—~’~ jÑ1Ô1Ð1Ð1Ð1å!¨*Ð5Ñ5Ô5ˆCÝ”7—>’> *Ñ-Ô-Ð-Ð-Ð-Ý�s—’Ñ(Ô(Ñ)Ô)¨RÒ/Ð/Ð/Ð/Ý�s×.Ò.Ð/HÑIÔIÑJÔJÈbÒPÐPÐPÐPÐPåŒM˜&°Ð5Ñ5Ô5Ð5Ð5Ð5ø�FŒM˜&°Ð5Ñ5Ô5Ð5Ð5øøøs   —CC3 Ã3Dc                ó*  — t          j        d¬¦  «        }	 t          j                             |d¦  «        }t          |d¦  «        5 }	 ddd¦  «         n# 1 swxY w Y   t          j                             |¦  «        dk    sJ ‚t          |¬¦  «        }t          j                             |¦  «        dk    sJ ‚t          | 	                    ¦   «         ¦  «        dk    sJ ‚	 t          j        |d	¬
¦  «         dS # t          j        |d	¬
¦  «         w xY w)zQService recovers from a pre-existing 0-byte file by initializing DDL and seeding.Ú	fe_empty_r   zempty.dbÚwbNr   r   r5   Tr!   )r#   r$   r%   r&   r'   ÚopenÚgetsizer	   r=   rÂ   r(   r)   )rd   r*   Úempty_dbrW   rÐ   s        r   Ú'test_zero_byte_empty_file_auto_recoveryzNTestMapStyleRecoveryAndSchemaIntegrity.test_zero_byte_empty_file_auto_recoveryÖ   s>  € åÔ!¨Ð5Ñ5Ô5ˆð
	6Ý”w—|’| F¨JÑ7Ô7ˆHÝ�h Ñ%Ô%ð ¨Øðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð å”7—?’? 8Ñ,Ô,°Ò1Ð1Ð1Ð1å!¨(Ð3Ñ3Ô3ˆCÝ”7—?’? 8Ñ,Ô,¨qÒ0Ð0Ð0Ð0Ý�s—’Ñ(Ô(Ñ)Ô)¨RÒ/Ð/Ð/Ð/Ð/åŒM˜&°Ð5Ñ5Ô5Ð5Ð5Ð5ø�FŒM˜&°Ð5Ñ5Ô5Ð5Ð5øøøs0   —0C: ÁAÁ	C: ÁAÁC: ÁAÁBC: Ã:Dc                óú  — t          j        d¬¦  «        }	 t          j                             |d¦  «        }t          |¬¦  «        }|                     ¦   «         5 }|                     d¦  «         |                     ¦   «          ddd¦  «         n# 1 swxY w Y   |                     ¦   «         5 }| 	                    ¦   «         }|                     d¦  «         | 
                    ¦   «         d         d	k    sJ ‚	 ddd¦  «         n# 1 swxY w Y   t          |¬¦  «        }t          |                     ¦   «         ¦  «        d
k    sJ ‚	 t          j        |d¬¦  «         dS # t          j        |d¬¦  «         w xY w)zEIf database has fewer than 30 styles, MapStyleService re-seeds to 30.Úfe_partial_r   z
partial.dbr   z+DELETE FROM map_styles WHERE biome_code > 5NzSELECT count(*) FROM map_stylesr   é   r5   Tr!   )r#   r$   r%   r&   r'   r	   Ú_get_connectionÚexecuteÚcommitÚcursorÚfetchoner=   rÂ   r(   r)   )rd   r*   Ú
partial_dbrÐ   ÚconnÚcurÚrecovered_svcs          r   Ú*test_partial_database_auto_reseed_recoveryzQTestMapStyleRecoveryAndSchemaIntegrity.test_partial_database_auto_reseed_recoveryå   sÚ  € åÔ!¨Ð7Ñ7Ô7ˆð	6ÝœŸš f¨lÑ;Ô;ˆJÝ!¨*Ð5Ñ5Ô5ˆCà×$Ò$Ñ&Ô&ð ¨$Ø—’ÐJÑKÔKÐKØ—’‘”�ðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð
 ×$Ò$Ñ&Ô&ð .¨$Ø—k’k‘m”m�Ø—’Ð=Ñ>Ô>Ð>Ø—|’|‘~”~ aÔ(¨AÒ-Ð-Ð-Ð-Ð-ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .õ ,°JÐ?Ñ?Ô?ˆMÝ�}×0Ò0Ñ2Ô2Ñ3Ô3°rÒ9Ð9Ð9Ð9Ð9åŒM˜&°Ð5Ñ5Ô5Ð5Ð5Ð5ø�FŒM˜&°Ð5Ñ5Ô5Ð5Ð5øøøsU   —AE" Á*BÂE" ÂBÂE" ÂBÂE" Â0A
DÃ;E" ÄDÄE" ÄDÄ:E" Å"E:c                óÚ  — t          j        d¬¦  «        }	 t          j                             |d¦  «        }t          |d¦  «        5 }|                     d¦  «         ddd¦  «         n# 1 swxY w Y   t          j        t          j
        ¦  «        5  t          |¬¦  «         ddd¦  «         n# 1 swxY w Y   t          j        |d¬	¦  «         dS # t          j        |d¬	¦  «         w xY w)
zBGarbage-filled database file cleanly raises sqlite3.DatabaseError.Úfe_corrupt_r   z
corrupt.dbrÔ   s-   CORRUPTED_NON_SQLITE_GARBAGE_BYTES_1234567890Nr   Tr!   )r#   r$   r%   r&   r'   rÕ   Úwriter„   r…   r†   ÚDatabaseErrorr	   r(   r)   )rd   r*   Ú
corrupt_dbrW   s       r   Ú2test_corrupted_database_file_raises_database_errorzYTestMapStyleRecoveryAndSchemaIntegrity.test_corrupted_database_file_raises_database_errorü   sg  € åÔ!¨Ð7Ñ7Ô7ˆð	6ÝœŸš f¨lÑ;Ô;ˆJÝ�j $Ñ'Ô'ð J¨1Ø—’ÐHÑIÔIÐIðJð Jð Jñ Jô Jð Jð Jð Jð Jð Jð Jøøøð Jð Jð Jð Jõ ”�wÔ4Ñ5Ô5ð 4ð 4Ý¨
Ð3Ñ3Ô3Ð3ð4ð 4ð 4ñ 4ô 4ð 4ð 4ð 4ð 4ð 4ð 4øøøð 4ð 4ð 4ð 4õ ŒM˜&°Ð5Ñ5Ô5Ð5Ð5Ð5ø�FŒM˜&°Ð5Ñ5Ô5Ð5Ð5øøøsS   —0C ÁA)ÁC Á)A-Á-C Á0A-Á1!C ÂB/Â#C Â/B3Â3C Â6B3Â7C ÃC*Úinvalid_biome_coder   r›   r,   rg   r2   c                ó  — t          |¦  «        }|                     ¦   «         }t          j        t          j        ¦  «        5  |                     dd|› �|f¦  «         ddd¦  «         n# 1 swxY w Y   |                     ¦   «          dS )z=CHECK (biome_code BETWEEN 1 AND 255) rejects invalid inserts.a½  
                INSERT INTO map_styles (
                    style_id, biome_code, name_vi, name_en, theme_category,
                    theme_category_name, description, floor_color_hex, wall_color_hex,
                    path_color_hex, liquid_color_hex, ambient_light_hex, asset_dir, monster_family
                ) VALUES (?, ?, 'Vi', 'En', 'HOANG_DA_DA_NGOAI', 'T', 'D', '#111', '#222', '#333', '#444', '#555', 'a', 'm')
                Ú	STY_TEST_N)r   rß   r„   r…   r†   ÚIntegrityErrorrÝ   r‰   )rd   r,   rì   râ   rã   s        r   Ú-test_schema_check_constraint_biome_code_rangezTTestMapStyleRecoveryAndSchemaIntegrity.test_schema_check_constraint_biome_code_range	  sÉ   € õ Ð'Ñ(Ô(ˆØ�kŠk‰mŒmˆÝŒ]�7Ô1Ñ2Ô2ð 
	ð 
	Ø�KŠKðð 2Ð/Ð1Ð1Ð3EÐFñ	ô 	ð 	ð
	ð 
	ð 
	ñ 
	ô 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	øøøð 
	ð 
	ð 
	ð 
	ð 	�
Š
‰Œˆˆˆs   ÁA*Á*A.Á1A.Úinvalid_tile_type_idr•   r7   c                óB  — t          |¬¦  «        }|                     ¦   «         5 }|                     ¦   «         }t          j        t
          j        ¦  «        5  |                     d|f¦  «         ddd¦  «         n# 1 swxY w Y   ddd¦  «         dS # 1 swxY w Y   dS )z?CHECK (tile_type_id BETWEEN 0 AND 19) rejects invalid tile IDs.r   a:  
                    INSERT INTO map_style_tiles (
                        style_id, tile_type_id, tile_type_name, texture_filename,
                        normal_filename, base_color_fallback
                    ) VALUES ('STY_01_HOANG_MANG_CO_LO', ?, 'INVALID', 't.png', 'n.png', '#111111')
                    N)r	   rÜ   rß   r„   r…   r†   rï   rÝ   )rd   r,   rñ   rÐ   râ   rã   s         r   Ú/test_schema_check_constraint_tile_type_id_rangezVTestMapStyleRecoveryAndSchemaIntegrity.test_schema_check_constraint_tile_type_id_range  s   € õ Ð&6Ð7Ñ7Ô7ˆØ× Ò Ñ"Ô"ð 	 dØ—+’+‘-”-ˆCÝ”�wÔ5Ñ6Ô6ð 	ð 	Ø—’ðð *Ð+ñô ð ð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð 	ð 	s5   ¥3BÁA<Á0BÁ<B 	Â BÂB 	ÂBÂBÂBNrƒ   )r,   rg   rì   r2   r   r/   )r,   rg   rñ   r2   r   r/   )rŽ   r�   r�   r‘   rÑ   rØ   rå   rë   r„   r·   r¸   rð   ró   r   r   r   rÈ   rÈ   Å   sÚ   € € € € € ØUÐUð6ð 6ð 6ð 6ð6ð 6ð 6ð 6ð6ð 6ð 6ð 6ð.6ð 6ð 6ð 6ð „[×ÒÐ1°A°s°8Ñ<Ô<ðð ð ñ =Ô<ðð" „[×ÒÐ3°b¸"°XÑ>Ô>ðð ð ñ ?Ô>ðð ð r   rÈ   )r   r   )r   r   r   r	   )r   r   )r‘   Ú
__future__r   Úconcurrent.futuresr^   r%   r(   r†   r#   Úpathlibr   Útypingr   r   r„   Úserver.world.map_style_schemar   Úserver.world.map_style_servicer	   Úserver.world.map_style_typesr
   Úfixturer   r   r,   r.   r“   rº   rÈ   r   r   r   ú<module>rü      s	  ððð ð #Ð "Ð "Ð "Ð "Ð "Ø Ð Ð Ð Ø 	€	€	€	Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ð Ð Ø "Ð "Ð "Ð "Ð "Ð "Ð "Ð "à €€€à 1Ð 1Ð 1Ð 1Ð 1Ð 1Ø :Ð :Ð :Ð :Ð :Ð :Ø ;Ð ;Ð ;Ð ;Ð ;Ð ;ð €„�hÐÑÔðTð Tð Tñ  ÔðTð
 €„�hÐÑÔð6ð 6ð 6ñ  Ôð6ð
 „ð.ð .ð .ñ „ð.ð@ð @ð @ð @ð @ñ @ô @ð @ðF?ð ?ð ?ð ?ð ?ñ ?ô ?ð ?ðD0Dð 0Dð 0Dð 0Dð 0Dñ 0Dô 0Dð 0Dðfeð eð eð eð eñ eô eð eð eð er   