§
    KË½jó?  ã                  óü  — 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ZddlmZ ddlmZmZ  ee¦  «                             ¦   «         j        j        j        Zedz  dz  Zedz  d	z  d
z  Zedz  dz  dz  dz  Zd!d"d„Z G d„ dej        ¦  «        Z G d„ dej        ¦  «        Z G d„ dej        ¦  «        Z G d„ dej        ¦  «        Z G d„ dej        ¦  «        Ze d k    r ej!        ¦   «          dS dS )#aÃ  
Adversarial Stress, Edge-Case & Vulnerability Test Suite for Milestone M2.
Tests:
1. Ring buffer overflow (> 1000 messages added rapidly across 8 channels).
2. Malformed item tags, edge-case rarities, unclosed tags, and ReDoS resistance.
3. XSS and HTML injection vulnerabilities (attribute breakouts, senderName, script tags).
4. Cooldown bypass attempts, rapid channel switching, and Unicode zero-width characters.
5. Headless / SSR safety checks.
é    )ÚannotationsN)ÚPath)ÚAnyÚDictÚclientÚwebappÚjsÚuiz
chat_ui.jsÚdistÚchatzChatManager.jsç      .@ÚcodeÚstrÚtimeout_secÚfloatÚreturnúDict[str, Any]c           	     ó:  — d| › d�}t          j        ddd|gdddt          t          ¦  «        |¬¦  «        }|j        d	k    rt          d
|j        › d|j        › �¦  «        ‚t          |j         	                    ¦   «          
                    d¦  «        ¦  «        D ]€}| 	                    ¦   «         }|                     d¦  «        r|                     d¦  «        s*|                     d¦  «        r+|                     d¦  «        rt          j        |¦  «        c S Œ�i S )z8Helper to run JS code in Node.js and return parsed JSON.z.
    (async () => {
        try {
            z‹
            process.exit(0);
        } catch (err) {
            console.error(err);
            process.exit(1);
        }
    })();
    Únodez--input-type=modulez-eTzutf-8)Úcapture_outputÚtextÚencodingÚcwdÚtimeoutr   zNode execution failed (code z):
ú
Ú{Ú}ú[ú])Ú
subprocessÚrunr   ÚPROJECT_ROOTÚ
returncodeÚRuntimeErrorÚstderrÚreversedÚstdoutÚstripÚsplitÚ
startswithÚendswithÚjsonÚloads)r   r   ÚwrappedÚresÚlines        úGC:\Projects\FreeExile\tests\unit\test_challenger_m2_chat_adversarial.pyÚrun_noder2      s8  € ð
ð ð
ð 
ð 
€Gõ Œ.Ø	Ð&¨¨gÐ6ØØØÝ•ÑÔØðñ ô €Cð „~˜ÒÐÝÐZ¸#¼.ÐZÐZÈcÌjÐZÐZÑ[Ô[Ð[Ý˜œ×)Ò)Ñ+Ô+×1Ò1°$Ñ7Ô7Ñ8Ô8ð $ð $ˆØ�zŠz‰|Œ|ˆØ�OŠO˜CÑ Ô ð 	$ T§]¢]°3Ñ%7Ô%7ð 	$¸T¿_º_ÈSÑ=QÔ=Qð 	$ÐVZ×VcÒVcÐdgÑVhÔVhð 	$Ý”:˜dÑ#Ô#Ð#Ð#Ð#øØ€Ió    c                  ó"   — e Zd ZdZdd„Zdd„ZdS )ÚTestRingBufferAdversarialStresszKStress tests the 100-message FIFO ring buffer under rapid flood conditions.r   ÚNonec                óà  — d}t          |¦  «        }|d         }|                      t          |¦  «        d¦  «         |                     ¦   «         D ]\  }}t	          |¦  «        }|                      |d         dd|› d�¦  «         |                      |d         d	|› d
�d|› d�¦  «         |                      |d         d	|› d�d|› d�¦  «         Œ€|                      |d         dd¦  «         dS )zR2,000 rapid messages per channel must never exceed 100 capacity and maintain FIFO.a7  
        const m = await import('./client/webapp/js/ui/chat_ui.js');
        const initialMem = process.memoryUsage().heapUsed;

        for (let ch = 1; ch <= 8; ch++) {
            for (let i = 1; i <= 2000; i++) {
                m.addChatMessage({
                    messageId: `msg_${ch}_${i}`,
                    senderName: `Player_${i}`,
                    channel: ch,
                    displayContent: `Rapid flood message ${i} on channel ${ch}`,
                    linkedItems: [],
                    timestampMs: 1000000 + i
                });
            }
        }

        const stats = {};
        for (let ch = 1; ch <= 8; ch++) {
            const history = m.getChannelHistory(ch);
            stats[ch] = {
                length: history.length,
                oldestId: history[0]?.messageId,
                newestId: history[history.length - 1]?.messageId
            };
        }
        const finalMem = process.memoryUsage().heapUsed;
        console.log(JSON.stringify({ stats, memDeltaMb: (finalMem - initialMem) / 1024 / 1024 }));
        Ústatsé   Úlengthéd   zChannel z exceeded 100 messagesÚoldestIdÚmsg_Ú_1901z violated FIFO evictionÚnewestIdÚ_2000z missed newest messageÚ
memDeltaMbg      I@z#Memory delta excessive during floodN)r2   ÚassertEqualÚlenÚitemsÚintÚ
assertLess)Úselfr	   Údatar8   Úch_strÚstÚchs          r1   Ú.test_01_chat_ui_ring_buffer_overflow_2000_msgszNTestRingBufferAdversarialStress.test_01_chat_ui_ring_buffer_overflow_2000_msgs<   s  € ðˆõ: ˜‰|Œ|ˆØ�W”ˆØ×Ò�˜U™œ QÑ'Ô'Ð'ØŸ+š+™-œ-ð 	fð 	f‰JˆF�BÝ�V‘”ˆBØ×Ò˜R œ\¨3Ð0U¸2Ð0UÐ0UÐ0UÑVÔVÐVØ×Ò˜R 
œ^Ð-=°BÐ-=Ð-=Ð-=Ð?eÈ"Ð?eÐ?eÐ?eÑfÔfÐfØ×Ò˜R 
œ^Ð-=°BÐ-=Ð-=Ð-=Ð?dÈ"Ð?dÐ?dÐ?dÑeÔeÐeÐeØ�Š˜˜\Ô*¨DÐ2WÑXÔXÐXÐXÐXr3   c                ó  — d}t          |¦  «        }|                     ¦   «         D ]a\  }}|                      |d         d¦  «         |                      |d         d|› d�¦  «         |                      |d         d|› d�¦  «         Œbd	S )
zRClientChatManager engine must cap at 100 messages FIFO under 2,000 rapid messages.a¸  
        const { ClientChatManager, ChatChannel } = await import('./client/dist/chat/ChatManager.js');
        const mgr = new ClientChatManager();

        for (let ch = 1; ch <= 8; ch++) {
            for (let i = 1; i <= 2000; i++) {
                mgr.receiveMessage({
                    messageId: `msg_${ch}_${i}`,
                    senderName: `User_${i}`,
                    channel: ch,
                    displayContent: `Flood content ${i}`,
                    linkedItems: [],
                    timestampMs: 1000 + i
                });
            }
        }

        const stats = {};
        for (let ch = 1; ch <= 8; ch++) {
            const list = mgr.getMessagesForChannel(ch);
            stats[ch] = {
                length: list.length,
                oldestId: list[0]?.messageId,
                newestId: list[list.length - 1]?.messageId
            };
        }
        console.log(JSON.stringify(stats));
        r:   r;   r<   r=   r>   r?   r@   N)r2   rD   rB   )rG   r	   rH   rI   rJ   s        r1   Ú,test_02_chat_manager_ts_ring_buffer_overflowzLTestRingBufferAdversarialStress.test_02_chat_manager_ts_ring_buffer_overflowe   s¨   € ðˆõ8 ˜‰|Œ|ˆØŸ*š*™,œ,ð 	Cð 	C‰JˆF�BØ×Ò˜R œ\¨3Ñ/Ô/Ð/Ø×Ò˜R 
œ^Ð-A°FÐ-AÐ-AÐ-AÑBÔBÐBØ×Ò˜R 
œ^Ð-A°FÐ-AÐ-AÐ-AÑBÔBÐBÐBð	Cð 	Cr3   N©r   r6   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rL   rN   © r3   r1   r5   r5   9   sM   € € € € € ØUÐUð'Yð 'Yð 'Yð 'YðR"Cð "Cð "Cð "Cð "Cð "Cr3   r5   c                  ó"   — e Zd ZdZdd„Zdd„ZdS )ÚTestMalformedItemTagsAndReDoSzOChallenges item tag parser and renderer against malformed injections and ReDoS.r   r6   c                óà   — d}t          |¦  «        }|D ]Y}|                      |d         ¦  «         d|d         v s|d         dk    r&|                      |d         dd|d         › �¦  «         ŒZd	S )
zRMalformed item tags must not crash parser and must not extract corrupt structures.ax  
        const m = await import('./client/webapp/js/ui/chat_ui.js');
        const { ClientChatManager } = await import('./client/dist/chat/ChatManager.js');

        const inputs = [
            '[item:malformed]',
            '[item:::]',
            '[item:::::]',
            '[item:u1]',
            '[item:u1:s1:name:-5]',
            '[item:u1:s1:name:abc]',
            '[item:u1:s1:name:0]',
            '[item:u1:s1:name:999999]',
            '[item:u1:s1:name:1.23]',
            '[item:!@#$%:^&*():Sword:1]',
            'Unclosed [item:u1:s1:Sword:1 and tail',
            'Empty [item:]'
        ];

        const results = [];
        for (const input of inputs) {
            const parsedUI = m.parseItemTags(input);
            const parsedTS = ClientChatManager.parseItemTags(input);
            const rendered = m.renderMessageHtml(input);
            results.push({
                input,
                parsedCountUI: parsedUI.length,
                parsedCountTS: parsedTS.length,
                renderedSafe: typeof rendered === 'string'
            });
        }
        console.log(JSON.stringify(results));
        ÚrenderedSafeÚ	malformedÚinputz	[item:::]ÚparsedCountUIr   zShould not parse invalid tag: N)r2   Ú
assertTruerB   )rG   r	   rH   r/   s       r1   Ú&test_03_malformed_item_tags_resiliencezDTestMalformedItemTagsAndReDoS.test_03_malformed_item_tags_resilience�   s–   € ð ˆõB ˜‰|Œ|ˆØð 	kð 	kˆCØ�OŠO˜C Ô/Ñ0Ô0Ð0Ø˜c 'œlÐ*Ð*¨c°'¬l¸kÒ.IÐ.IØ× Ò   _Ô!5°qÐ:iÐ[^Ð_fÔ[gÐ:iÐ:iÑjÔjÐjøð	kð 	kr3   c                ój   — d}t          |¦  «        }|D ]}|                      |dd|d›d�¦  «         ŒdS )zXRegex parser must evaluate 100,000-character adversarial inputs in < 20ms without ReDoS.a¬  
        const m = await import('./client/webapp/js/ui/chat_ui.js');
        const adversarialStrings = [
            '[item:' + 'a'.repeat(100000),
            '[item:uuid:' + 'b'.repeat(100000),
            '[item:uuid:sig:' + 'c'.repeat(100000),
            '[item:uuid:sig:name:' + '9'.repeat(100000),
            '[item:'.repeat(20000)
        ];

        const timings = [];
        for (const str of adversarialStrings) {
            const t0 = performance.now();
            m.parseItemTags(str);
            m.renderMessageHtml(str);
            const t1 = performance.now();
            timings.push(t1 - t0);
        }
        console.log(JSON.stringify(timings));
        g      9@z Regex evaluation took too long: z.2fzms (potential ReDoS)N)r2   rF   )rG   r	   ÚtimingsÚts       r1   Ú*test_04_regex_redos_adversarial_resistancezHTestMalformedItemTagsAndReDoS.test_04_regex_redos_adversarial_resistance¶   s[   € ðˆõ( ˜2‘,”,ˆØð 	eð 	eˆAØ�OŠO˜A˜tÐ%cÈÐ%cÐ%cÐ%cÐ%cÑdÔdÐdÐdð	eð 	er3   NrO   )rP   rQ   rR   rS   r]   ra   rT   r3   r1   rV   rV   Š   sM   € € € € € ØYÐYð'kð 'kð 'kð 'kðReð eð eð eð eð er3   rV   c                  ó*   — e Zd ZdZdd„Zdd„Zdd„ZdS )	Ú&TestXssAndHtmlInjectionVulnerabilitieszKAdversarially tests for cross-site scripting (XSS) and attribute breakouts.r   r6   c                ó   — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         dS )zHStandard HTML tags in message text must be escaped by renderMessageHtml.aô  
        const m = await import('./client/webapp/js/ui/chat_ui.js');
        const raw = '<script>alert("xss")</script><b>bold</b><img src=x onerror=alert(1)>';
        const rendered = m.renderMessageHtml(raw);
        console.log(JSON.stringify({
            hasRawScript: rendered.includes('<script>'),
            hasRawImg: rendered.includes('<img'),
            hasEscapedScript: rendered.includes('&lt;script&gt;'),
            hasEscapedImg: rendered.includes('&lt;img')
        }));
        ÚhasRawScriptÚ	hasRawImgÚhasEscapedScriptÚhasEscapedImgN)r2   ÚassertFalser\   ©rG   r	   rH   s      r1   Ú'test_05_content_script_tag_sanitizationzNTestXssAndHtmlInjectionVulnerabilities.test_05_content_script_tag_sanitizationÔ   s}   € ð
ˆõ ˜‰|Œ|ˆØ×Ò˜˜nÔ-Ñ.Ô.Ð.Ø×Ò˜˜kÔ*Ñ+Ô+Ð+Ø�Š˜Ð/Ô0Ñ1Ô1Ð1Ø�Š˜˜_Ô-Ñ.Ô.Ð.Ð.Ð.r3   c                ót   — d}t          |¦  «        }|d         r|                      |d         d¦  «         dS dS )zOEMPIRICAL AUDIT: Detects whether item tag name attribute breakout executes XSS.al  
        const m = await import('./client/webapp/js/ui/chat_ui.js');
        const payload = '[item:u1:s1:\" autofocus onfocus=\"alert(1):1]';
        const rendered = m.renderMessageHtml(payload);
        const isVulnerable = rendered.includes('data-name="" autofocus onfocus="alert(1)"');
        console.log(JSON.stringify({ rendered, isVulnerable }));
        ÚisVulnerablezBAttribute breakout XSS vulnerability detected in renderMessageHtmlN©r2   r\   rj   s      r1   Ú2test_06_item_name_attribute_breakout_vulnerabilityzYTestXssAndHtmlInjectionVulnerabilities.test_06_item_name_attribute_breakout_vulnerabilityç   sP   € ðˆõ ˜‰|Œ|ˆà�Ôð 	xà�OŠO˜D Ô0Ð2vÑwÔwÐwÐwÐwð	xð 	xr3   c                ó`   — d}t          |¦  «        }|                      |d         d¦  «         dS )zSEMPIRICAL AUDIT: Detects whether senderName is concatenated raw into row.innerHTML.aU  
        const fs = await import('fs');
        const code = fs.readFileSync('./client/webapp/js/ui/chat_ui.js', 'utf-8');
        // Check line 244: row.innerHTML = ... ${msg.senderName} ...
        const rawSenderInterpolation = code.includes('${msg.senderName}:');
        console.log(JSON.stringify({ rawSenderInterpolation }));
        ÚrawSenderInterpolationz7msg.senderName is interpolated unescaped into innerHTMLNrn   rj   s      r1   Ú,test_07_sender_name_raw_html_injection_auditzSTestXssAndHtmlInjectionVulnerabilities.test_07_sender_name_raw_html_injection_auditö   s7   € ðˆõ ˜‰|Œ|ˆØ�Š˜Ð5Ô6Ð8qÑrÔrÐrÐrÐrr3   NrO   )rP   rQ   rR   rS   rk   ro   rr   rT   r3   r1   rc   rc   Ñ   s`   € € € € € ØUÐUð/ð /ð /ð /ð&xð xð xð xð
sð 
sð 
sð 
sð 
sð 
sr3   rc   c                  ó"   — e Zd ZdZdd„Zdd„ZdS )ÚTestCooldownAndChannelSwitchingzRVerifies that cooldowns cannot be bypassed by switching channels or rapid calling.r   r6   c                ó  — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         |                      |d         ¦  «         |                      |d         d¦  «         dS )	zGSwitching channels and returning must NOT bypass active cooldown timer.aº  
        const createMockElement = (id) => ({
            id, dataset: { channelId: '1' },
            classList: { _c: new Set(), add(c){this._c.add(c);}, remove(c){this._c.delete(c);}, contains(c){return this._c.has(c);} },
            disabled: false, innerText: '', textContent: '',
            addEventListener: () => {}, querySelectorAll: () => [], querySelector: () => null, appendChild: () => {}
        });
        global.document = {
            getElementById: (id) => createMockElement(id),
            querySelectorAll: () => [createMockElement('tab')],
            addEventListener: () => {}, createElement: (t) => createMockElement(t), readyState: 'complete'
        };

        const m = await import('./client/webapp/js/ui/chat_ui.js');
        const { ClientChatManager, ChatChannel } = await import('./client/dist/chat/ChatManager.js');

        // Trigger send on World channel (15s)
        m.recordMessageSent(1);
        const remInitial = m.getCooldownRemaining(1);

        // Rapid channel switching
        m.switchChannel(2);
        m.switchChannel(3);
        m.switchChannel(7);
        m.switchChannel(1);
        const remAfterSwitch = m.getCooldownRemaining(1);

        const mgr = new ClientChatManager();
        mgr.recordMessageSent(ChatChannel.World);
        const mgrBefore = mgr.checkCooldown(ChatChannel.World);
        mgr.setActiveChannel(ChatChannel.Zone);
        mgr.setActiveChannel(ChatChannel.World);
        const mgrAfter = mgr.checkCooldown(ChatChannel.World);

        console.log(JSON.stringify({
            remInitial,
            remAfterSwitch,
            mgrBeforeAllowed: mgrBefore.allowed,
            mgrAfterAllowed: mgrAfter.allowed
        }));
        Ú
remInitiali°6  ÚremAfterSwitchÚmgrBeforeAllowedÚmgrAfterAllowedz5Cooldown must not be bypassed by activeChannel changeN)r2   ÚassertGreaterri   rj   s      r1   Ú5test_08_cooldown_bypass_attempt_via_channel_switchingzUTestCooldownAndChannelSwitching.test_08_cooldown_bypass_attempt_via_channel_switching  s‹   € ð(ˆõR ˜‰|Œ|ˆØ×Ò˜4 Ô-¨uÑ5Ô5Ð5Ø×Ò˜4Ð 0Ô1°5Ñ9Ô9Ð9Ø×Ò˜Ð0Ô1Ñ2Ô2Ð2Ø×Ò˜Ð/Ô0Ð2iÑjÔjÐjÐjÐjr3   c                ó–   — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         ¦  «         dS )zIZero-width characters and homoglyphs inside item names must parse safely.u¾  
        const m = await import('./client/webapp/js/ui/chat_ui.js');
        const tag = '[item:u-99:s-99:V\u200B\u200C\u200DÃµ\uFEFFThuáº­t:5]';
        const parsed = m.parseItemTags(tag);
        const rendered = m.renderMessageHtml(tag);
        console.log(JSON.stringify({
            count: parsed.length,
            parsedName: parsed[0]?.itemName,
            renderedHasButton: rendered.includes('chat-item-link')
        }));
        Úcounté   ÚrenderedHasButtonN)r2   rB   r\   rj   s      r1   Ú'test_09_unicode_zero_width_tag_handlingzGTestCooldownAndChannelSwitching.test_09_unicode_zero_width_tag_handling7  sN   € ð
ˆõ ˜‰|Œ|ˆØ×Ò˜˜gœ¨Ñ*Ô*Ð*Ø�Š˜Ð0Ô1Ñ2Ô2Ð2Ð2Ð2r3   NrO   )rP   rQ   rR   rS   r{   r€   rT   r3   r1   rt   rt     sG   € € € € € Ø\Ð\ð/kð /kð /kð /kðb3ð 3ð 3ð 3ð 3ð 3r3   rt   c                  ó   — e Zd ZdZdd„ZdS )ÚTestPlaywrightBrowserExploitzBExecutes real browser exploit payloads in Chromium via Playwright.r   r6   c                ól  — ddl m} t          dz                       ¦   «                              ¦   «         } |¦   «         5 }|j                             dddg¬¦  «        }|                     ¦   «         }|                     |¦  «         | 	                    d¦  «         | 
                    d	¦  «         | 	                    d¦  «         | 
                    d
¦  «        }| 
                    d¦  «         | 	                    d¦  «         | 
                    d¦  «        }| 
                    d¦  «        }|                     ¦   «          ddd¦  «         n# 1 swxY w Y   |                      |d¦  «         |                      |d¦  «         |                      d|d¦  «         |                      d|d¦  «         dS )z\EMPIRICAL AUDIT: Real browser executes XSS if senderName or item tag attribute is unescaped.r   )Úsync_playwrightz
index.htmlÚmsedgeTz--allow-file-access-from-files)ÚchannelÚheadlessÚargsi,  a¶  () => {
                window.__xss_sender = false;
                window.chatUI.addChatMessage({
                    messageId: 'poc_sender_xss',
                    senderName: '<img src=invalid_poc onerror="window.__xss_sender=true">',
                    channel: 1,
                    displayContent: 'Normal text',
                    linkedItems: [],
                    timestampMs: Date.now()
                });
            }z() => window.__xss_sendera¸  () => {
                window.__xss_item = false;
                window.chatUI.addChatMessage({
                    messageId: 'poc_item_xss',
                    senderName: 'Attacker',
                    channel: 1,
                    displayContent: 'Weapon [item:u1:s1:" autofocus onfocus="window.__xss_item=true:1]',
                    linkedItems: [],
                    timestampMs: Date.now()
                });
            }z() => window.__xss_itemzB() => document.getElementById('chat-messages-container').innerHTMLNz=CRITICAL: Real Chromium executed arbitrary JS via senderName!zVCRITICAL: Real Chromium executed arbitrary JS via item tag attribute breakout onfocus!z&lt;imgz8Sanitized senderName must contain escaped entity &lt;imgz<img src=invalid_pocz,Raw <img> tag must never appear in innerHTML)Úplaywright.sync_apir„   Ú
WEBAPP_DIRÚresolveÚas_uriÚchromiumÚlaunchÚnew_pageÚgotoÚwait_for_timeoutÚevaluateÚcloseri   ÚassertInÚassertNotIn)	rG   r„   Ú	index_uriÚpÚbrowserÚpageÚexploit1_firedÚexploit2_firedÚcontainer_htmls	            r1   Ú4test_10_playwright_sender_name_and_item_breakout_xsszQTestPlaywrightBrowserExploit.test_10_playwright_sender_name_and_item_breakout_xssL  s  € à7Ð7Ð7Ð7Ð7Ð7å ,Ñ.×7Ò7Ñ9Ô9×@Ò@ÑBÔBˆ	Øˆ_ÑÔð +	 !Ø”j×'Ò'Ø ØØ6Ð7ð (ñ ô ˆGð
 ×#Ò#Ñ%Ô%ˆDð �IŠI�iÑ Ô Ð Ø×!Ò! #Ñ&Ô&Ð&ð �MŠMð 
ñ 
ô 
ð 
ð ×!Ò! #Ñ&Ô&Ð&Ø!Ÿ]š]Ð+FÑGÔGˆNð �MŠMð 
ñ 
ô 
ð 
ð ×!Ò! #Ñ&Ô&Ð&Ø!Ÿ]š]Ð+DÑEÔEˆNà!Ÿ]š]Ð+oÑpÔpˆNØ�MŠM‰OŒOˆOðW+	ð +	ð +	ñ +	ô +	ð +	ð +	ð +	ð +	ð +	ð +	øøøð +	ð +	ð +	ð +	ð^ 	×Ò˜Ð)hÑiÔiÐiØ×Ò˜ð  *Bñ  	Cô  	Cð  	CØ�Š�i Ð1kÑlÔlÐlØ×ÒÐ/°ÐAoÑpÔpÐpÐpÐps   ¿DEÅEÅENrO   )rP   rQ   rR   rS   r�   rT   r3   r1   r‚   r‚   I  s4   € € € € € ØLÐLð7qð 7qð 7qð 7qð 7qð 7qr3   r‚   Ú__main__)r   )r   r   r   r   r   r   )"rS   Ú
__future__r   ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriteÚ
@pytest_arr,   r    ÚtimeÚunittestÚpathlibr   Útypingr   r   Ú__file__r‹   Úparentr"   rŠ   Ú
CHAT_UI_JSÚCHAT_MGR_JSr2   ÚTestCaser5   rV   rc   rt   r‚   rP   ÚmainrT   r3   r1   ú<module>r°      sJ  ððð ð #Ð "Ð "Ð "Ð "Ð "à  € € € € € € € € € € € € €€€€Ø Ð Ð Ð Ø €€€Ø €€€Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ð Ð àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€Ø˜HÑ$ xÑ/€
Ø˜$Ñ Ñ%¨Ñ4€
Ø˜XÑ%¨Ñ.°Ñ7Ð:JÑJ€ðð ð ð ð ð>NCð NCð NCð NCð NC hÔ&7ñ NCô NCð NCðbDeð Deð Deð Deð De HÔ$5ñ Deô Deð DeðN/sð /sð /sð /sð /s¨XÔ->ñ /sô /sð /sðdC3ð C3ð C3ð C3ð C3 hÔ&7ñ C3ô C3ð C3ðL:qð :qð :qð :qð :q 8Ô#4ñ :qô :qð :qðz ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr3   