§
    §¿j,  ã                  óf  — 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
Z
ddlZddlmZ ddlmZ ddlmZ ddlmZmZ  ee¦  «                             ¦   «         j        j        j        Zedz  d	z  d
z  dz  dz  Zedz  dz  dz  dz  Z G d„ dej        ¦  «        Zedk    r ej        ¦   «          dS dS )zó
Unit Test Suite for FreeExile Chat Channel Permission Gating.
Validates Guild, Party, World Level, Recruit Level, and System channel restrictions
across Server (ChannelManager), Client WebApp (chat_ui.js), and Client Engine (ChatManager.ts).
é    )ÚannotationsN)ÚPath)ÚAny)ÚChannelManager)ÚChatChannelTypeÚSendChatRequestDTOÚclientÚwebappÚjsÚuiz
chat_ui.jsÚsrcÚchatzChatManager.tsc                  ój   — e Zd ZdZdd„Zdd„Zdd	„Zdd
„Zdd„Zdd„Z	dd„Z
dd„Zdd„Zdd„Zdd„ZdS )ÚTestChatChannelPermissionsz>Verifies strict channel permissions on both server and client.ÚreturnÚNonec                ó,   — t          ¦   «         | _        d S )N)r   Úchannel_mgr)Úselfs    úAC:\Projects\FreeExile\tests\unit\test_chat_channel_permissions.pyÚsetUpz TestChatChannelPermissions.setUp   s   € Ý)Ñ+Ô+ˆÔÐÐó    ÚscriptÚstrr   c                ó²  — t          t          ¦  «                             dd¦  «        }d|› d|› d�}t          j        ddg|                     d¦  «        d	¬
¦  «        }|j        dk    r,t          d|j         	                    dd¬¦  «        › �¦  «        ‚|j
         	                    d¦  «                             ¦   «         }|rt          j        |¦  «        nd S )Nú\ú/z
        import('file:///z$').then(chatModule => {
            zl
        }).catch(err => {
            console.error(err);
            process.exit(1);
        });
        Únodez--input-type=moduleúutf-8T)ÚinputÚcapture_outputr   zNode execution failed:
Úreplace)Úerrors)r   Ú
CHAT_UI_JSr"   Ú
subprocessÚrunÚencodeÚ
returncodeÚRuntimeErrorÚstderrÚdecodeÚstdoutÚstripÚjsonÚloads)r   r   Úescaped_pathÚfull_scriptÚresÚouts         r   Ú_eval_chat_ui_nodez-TestChatChannelPermissions._eval_chat_ui_node   sì   € Ý�:‘”×.Ò.¨t°SÑ9Ô9ˆðØ%ðð àðð ð ˆõ ŒnØÐ*Ð+Ø×$Ò$ WÑ-Ô-Øð
ñ 
ô 
ˆð
 Œ>˜QÒÐÝÐh¸#¼*×:KÒ:KÈGÐ\eÐ:KÑ:fÔ:fÐhÐhÑiÔiÐiØŒj×Ò Ñ(Ô(×.Ò.Ñ0Ô0ˆØ"%Ð/�tŒz˜#‰Œˆ¨4Ð/r   c                óÖ   — t          dddt          j        dd¬¦  «        }| j                             |¦  «        \  }}}|                      |¦  «         |                      |d¦  «         dS )zAServer must strictly reject Guild chat if player has no guild_id.éé  õ
   TÃ¢n Thá»§é   u   Xin chÃ o bang há»™iN©Ú	sender_idÚsender_nameÚsender_levelÚchannelÚcontentÚguild_idõ   ChÆ°a gia nháº­p Bang Há»™i.)r   r   ÚGUILDr   Úvalidate_send_permissionÚassertFalseÚassertEqual©r   ÚreqÚallowedÚreasonÚ_s        r   Ú/test_01_server_rejects_guild_chat_without_guildzJTestChatChannelPermissions.test_01_server_rejects_guild_chat_without_guild3   s{   € å ØØ$ØÝ#Ô)Ø*Øð
ñ 
ô 
ˆð "Ô-×FÒFÀsÑKÔKÑˆ�˜Ø×Ò˜Ñ!Ô!Ð!Ø×Ò˜Ð!?Ñ@Ô@Ð@Ð@Ð@r   c                óÖ   — t          dddt          j        dd¬¦  «        }| j                             |¦  «        \  }}}|                      |¦  «         |                      |d¦  «         dS )	z;Server must accept Guild chat if player belongs to a guild.r6   u   Ä�áº§u LÄ©nhr8   u   Táº­p há»£p bang há»™i!Úguild_feral_01r9   Ú N)r   r   rA   r   rB   Ú
assertTruerD   rE   s        r   Ú+test_02_server_allows_guild_chat_with_guildzFTestChatChannelPermissions.test_02_server_allows_guild_chat_with_guildA   sx   € å ØØ&ØÝ#Ô)Ø-Ø%ð
ñ 
ô 
ˆð "Ô-×FÒFÀsÑKÔKÑˆ�˜Ø�Š˜Ñ Ô Ð Ø×Ò˜ Ñ$Ô$Ð$Ð$Ð$r   c                óÖ   — t          dddt          j        dd¬¦  «        }| j                             |¦  «        \  }}}|                      |¦  «         |                      |d¦  «         dS )z8Server must reject Party chat if player has no party_id.iê  õ   Hiá»‡p KhÃ¡ché   u   Ä�i bÃ­ cáº£nh khÃ´ng?N)r:   r;   r<   r=   r>   Úparty_idõ   ChÆ°a gia nháº­p Tá»• Ä�á»™i.)r   r   ÚPARTYr   rB   rC   rD   rE   s        r   Ú/test_03_server_rejects_party_chat_without_partyzJTestChatChannelPermissions.test_03_server_rejects_party_chat_without_partyO   s{   € å ØØ'ØÝ#Ô)Ø,Øð
ñ 
ô 
ˆð "Ô-×FÒFÀsÑKÔKÑˆ�˜Ø×Ò˜Ñ!Ô!Ð!Ø×Ò˜Ð!@ÑAÔAÐAÐAÐAr   c                óÔ   — t          dddt          j        d¬¦  «        }| j                             |¦  «        \  }}}|                      |¦  «         |                      d|¦  «         dS )z(Server must gate World chat at level 20.ië  r7   é   u   TÃ¬m sÆ° phá»¥)r:   r;   r<   r=   r>   õ   Cáº§n Ä‘áº¡t cáº¥p 20N)r   r   ÚWORLDr   rB   rC   ÚassertInrE   s        r   Ú0test_04_server_rejects_world_chat_under_level_20zKTestChatChannelPermissions.test_04_server_rejects_world_chat_under_level_20]   sv   € å ØØ$ØÝ#Ô)Ø$ð
ñ 
ô 
ˆð "Ô-×FÒFÀsÑKÔKÑˆ�˜Ø×Ò˜Ñ!Ô!Ð!Ø�ŠÐ-¨vÑ6Ô6Ð6Ð6Ð6r   c                óÖ   — t          dddt          j        dd¬¦  «        }| j                             |¦  «        \  }}}|                      |¦  «         |                      d|¦  «         dS )z5Server must reject any attempt to whisper to oneself.iì  rQ   éd   u   Tá»± nÃ³i má»™t mÃ¬nh)r:   r;   r<   r=   Ú	target_idr>   u3   KhÃ´ng thá»ƒ tá»± gá»­i máº­t thÆ° cho chÃ­nh mÃ¬nhN)r   r   ÚWHISPERr   rB   rC   r[   rE   s        r   Ú&test_05_server_rejects_whisper_to_selfzATestChatChannelPermissions.test_05_server_rejects_whisper_to_selfj   sy   € å ØØ'ØÝ#Ô+ØØ+ð
ñ 
ô 
ˆð "Ô-×FÒFÀsÑKÔKÑˆ�˜Ø×Ò˜Ñ!Ô!Ð!Ø�ŠÐKÈVÑTÔTÐTÐTÐTr   c                óÖ  — |                       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         ¦  «         |                      |d         d         ¦  «         |                      d|d         d         ¦  «         dS )z^chat_ui.js validateChannelPermission must enforce Guild, Party, Level, and System constraints.a­  
            const rGuildNo = chatModule.validateChannelPermission(3, { guildId: '', partyId: '', level: 50 });
            const rGuildYes = chatModule.validateChannelPermission(3, { guildId: 'g_1', partyId: '', level: 50 });
            const rPartyNo = chatModule.validateChannelPermission(4, { guildId: '', partyId: '', level: 50 });
            const rPartyYes = chatModule.validateChannelPermission(4, { guildId: '', partyId: 'p_1', level: 50 });
            const rWorldLow = chatModule.validateChannelPermission(1, { guildId: '', partyId: '', level: 19 });
            const rWorldOk = chatModule.validateChannelPermission(1, { guildId: '', partyId: '', level: 20 });
            const rRecruitLow = chatModule.validateChannelPermission(7, { guildId: '', partyId: '', level: 9 });
            const rRecruitOk = chatModule.validateChannelPermission(7, { guildId: '', partyId: '', level: 10 });
            const rSystem = chatModule.validateChannelPermission(6, { guildId: 'g_1', partyId: 'p_1', level: 100 });
            console.log(JSON.stringify({
                rGuildNo, rGuildYes, rPartyNo, rPartyYes, rWorldLow, rWorldOk, rRecruitLow, rRecruitOk, rSystem
            }));
        ÚrGuildNorG   rH   r@   Ú	rGuildYesÚrPartyNorT   Ú	rPartyYesÚ	rWorldLowrY   ÚrWorldOkÚrRecruitLowu   Cáº§n Ä‘áº¡t cáº¥p 10Ú
rRecruitOkÚrSystemu   KÃªnh Há»‡ Thá»‘ngN)r4   rC   rD   rN   r[   )r   Údatas     r   Ú1test_06_webapp_validate_channel_permission_matrixzLTestChatChannelPermissions.test_06_webapp_validate_channel_permission_matrixz   sÇ  € à×&Ò&ð (ñ ô ˆð 	×Ò˜˜jÔ)¨)Ô4Ñ5Ô5Ð5Ø×Ò˜˜jÔ)¨(Ô3Ð5SÑTÔTÐTØ�Š˜˜[Ô)¨)Ô4Ñ5Ô5Ð5à×Ò˜˜jÔ)¨)Ô4Ñ5Ô5Ð5Ø×Ò˜˜jÔ)¨(Ô3Ð5TÑUÔUÐUØ�Š˜˜[Ô)¨)Ô4Ñ5Ô5Ð5à×Ò˜˜kÔ*¨9Ô5Ñ6Ô6Ð6Ø�ŠÐ-¨t°KÔ/@ÀÔ/JÑKÔKÐKØ�Š˜˜ZÔ(¨Ô3Ñ4Ô4Ð4à×Ò˜˜mÔ,¨YÔ7Ñ8Ô8Ð8Ø�ŠÐ-¨t°MÔ/BÀ8Ô/LÑMÔMÐMØ�Š˜˜\Ô*¨9Ô5Ñ6Ô6Ð6à×Ò˜˜iœ¨Ô3Ñ4Ô4Ð4Ø�ŠÐ*¨D°¬O¸HÔ,EÑFÔFÐFÐFÐFr   c                óæ   — d}|                       d|› d�¦  «        }|                      |d         d¦  «         |                      d|d         ¦  «         |                      |d         d	d
¦  «         dS )u[   When sending on Guild channel without a guild, chat_ui.js must emit a [Cáº£nh BÃ¡o] notice.uõ  
        const _createEl = (tag) => ({
            tagName: tag, id: '', className: '', innerHTML: '', textContent: '', value: '', children: [],
            classList: { _c: new Set(), add(c){this._c.add(c);}, remove(c){this._c.delete(c);}, contains(c){return this._c.has(c);} },
            appendChild(c){ this.children.push(c); return c; }, querySelectorAll(){ return []; }, addEventListener(){}
        });
        const elements = {};
        global.document = {
            getElementById: (id) => elements[id] || (elements[id] = _createEl('div')),
            querySelectorAll: () => [],
            createElement: (t) => _createEl(t)
        };
        global.window = { player: { guildId: '', partyId: '', level: 1, name: 'TÃ¢n Thá»§' } };
        ú
            ul  
            const input = document.getElementById('chat-input');
            input.value = 'Xin chÃ o bang chÃºng!';
            chatModule.switchChannel(3); // Switch to Guild channel
            chatModule.sendCurrentChatMessage();

            const guildHistory = chatModule.getChannelHistory(3);
            const lastMsg = guildHistory[guildHistory.length - 1];
            console.log(JSON.stringify({
                historyLength: guildHistory.length,
                sender: lastMsg?.senderName,
                content: lastMsg?.displayContent,
                inputVal: input.value
            }));
        Úsenderu   Há»‡ Thá»‘ngu*   [Cáº£nh BÃ¡o] ChÆ°a gia nháº­p Bang Há»™i.r>   ÚinputValrM   z#Input must be cleared after warningN)r4   rD   r[   ©r   Úmock_domrl   s      r   Ú4test_07_webapp_send_chat_blocked_with_system_warningzOTestChatChannelPermissions.test_07_webapp_send_chat_blocked_with_system_warning�   s�   € ðˆð ×&Ò&ð (Øð(ð (ð (ñ ô ˆð  	×Ò˜˜hœ¨Ñ8Ô8Ð8Ø�ŠÐBÀDÈÄOÑTÔTÐTØ×Ò˜˜jÔ)¨2Ð/TÑUÔUÐUÐUÐUr   c                óä   — d}|                       d|› d�¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         |                      |d         d	¦  «         d
S )zJWhen player is in a guild, sending on Guild channel must succeed normally.u  
        const _createEl = (tag) => ({
            tagName: tag, id: '', className: '', innerHTML: '', textContent: '', value: '', children: [],
            classList: { _c: new Set(), add(c){this._c.add(c);}, remove(c){this._c.delete(c);}, contains(c){return this._c.has(c);} },
            appendChild(c){ this.children.push(c); return c; }, querySelectorAll(){ return []; }, addEventListener(){}
        });
        const elements = {};
        global.document = {
            getElementById: (id) => elements[id] || (elements[id] = _createEl('div')),
            querySelectorAll: () => [],
            createElement: (t) => _createEl(t)
        };
        global.window = { player: { guildId: 'guild_iron_fist', partyId: '', level: 25, name: 'Ä�á»™c CÃ´ Cáº§u Báº¡i' } };
        ro   us  
            const input = document.getElementById('chat-input');
            input.value = 'Ä�á»“ng Ä‘áº¡o táº­p há»£p phá»¥ báº£n!';
            chatModule.switchChannel(3); // Guild channel
            chatModule.sendCurrentChatMessage();

            const guildHistory = chatModule.getChannelHistory(3);
            const lastMsg = guildHistory[guildHistory.length - 1];
            console.log(JSON.stringify({
                historyLength: guildHistory.length,
                sender: lastMsg?.senderName,
                content: lastMsg?.displayContent,
                inputVal: input.value
            }));
        rp   u   Ä�á»™c CÃ´ Cáº§u Báº¡ir>   u'   Ä�á»“ng Ä‘áº¡o táº­p há»£p phá»¥ báº£n!rq   rM   N)r4   rD   rr   s      r   Ú3test_08_webapp_send_chat_succeeds_when_guild_memberzNTestChatChannelPermissions.test_08_webapp_send_chat_succeeds_when_guild_memberÁ   s�   € ðˆð ×&Ò&ð (Øð(ð (ð (ñ ô ˆð  	×Ò˜˜hœÐ)AÑBÔBÐBØ×Ò˜˜iœÐ*SÑTÔTÐTØ×Ò˜˜jÔ)¨2Ñ.Ô.Ð.Ð.Ð.r   c                ó  — t                                d¬¦  «        }|                      d|¦  «         |                      d|¦  «         |                      d|¦  «         |                      d|¦  «         |                      d|¦  «         dS )	zCChatManager.ts must provide validateSendPermission and level gates.r   )ÚencodingÚvalidateSendPermissionzMIN_WORLD_LEVEL = 20zMIN_RECRUIT_LEVEL = 10ÚPlayerChatContextÚChannelPermissionResultN)ÚCHAT_MANAGER_TSÚ	read_textr[   )r   Úts_codes     r   Ú.test_09_typescript_chat_manager_permission_apizITestChatChannelPermissions.test_09_typescript_chat_manager_permission_apiç   s‡   € å!×+Ò+°WÐ+Ñ=Ô=ˆØ�ŠÐ.°Ñ8Ô8Ð8Ø�ŠÐ,¨gÑ6Ô6Ð6Ø�ŠÐ.°Ñ8Ô8Ð8Ø�ŠÐ)¨7Ñ3Ô3Ð3Ø�ŠÐ/°Ñ9Ô9Ð9Ð9Ð9r   N)r   r   )r   r   r   r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r4   rJ   rO   rV   r\   ra   rm   rt   rv   r   © r   r   r   r      s  € € € € € ØHÐHð,ð ,ð ,ð ,ð0ð 0ð 0ð 0ð,Að Að Að Að%ð %ð %ð %ðBð Bð Bð Bð7ð 7ð 7ð 7ðUð Uð Uð Uð !Gð !Gð !Gð !GðF"Vð "Vð "Vð "VðH"/ð "/ð "/ð "/ðL:ð :ð :ð :ð :ð :r   r   Ú__main__)rƒ   Ú
__future__r   ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arr.   r%   ÚunittestÚpathlibr   Útypingr   Úserver.chat.channel_managerr   Úserver.chat.chat_typesr   r   Ú__file__ÚresolveÚparentÚPROJECT_ROOTr$   r|   ÚTestCaser   r€   Úmainr„   r   r   ú<module>r˜      s_  ððð ð #Ð "Ð "Ð "Ð "Ð "à  € € € € € € € € € € € € €€€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð à 6Ð 6Ð 6Ð 6Ð 6Ð 6Ø FÐ FÐ FÐ FÐ FÐ FÐ FÐ Fàˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€Ø˜HÑ$ xÑ/°$Ñ6¸Ñ=ÀÑL€
Ø Ñ)¨EÑ1°FÑ:Ð=MÑM€ðW:ð W:ð W:ð W:ð W: Ô!2ñ W:ô W:ð W:ðt ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr   