§
    ÜÅ½j0  ã                   ó6  — d Z ddlZddlZddlZddlmZ  ee¦  «                             ¦   «         j        j        j        Z	de
defd„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 Test Suite for Milestone 1:
- Dual Fluid HUD Orbs (Canvas 2D Sinusoidal Crest & Extreme Values)
- Skill Bar Controller (Cooldown Spam, Zero/Negative Durations, Slot Indexing)
- Concurrency, Layout Thrashing & State Machine Edge Cases
é    N)ÚPathÚjs_codeÚreturnc                 óð   — d| › d�}t          j        ddd|gddt          t          ¦  «        ¬¦  «        }|j        dk    rt          d	|j        › d
|j        › �¦  «        ‚t          j        |j	        ¦  «        S )zHExecutes ES module code in Node.js with mock DOM and canvas environment.ae  
const createMockElement = (id) => ({
  id, dataset: {},
  classList: {
    _c: new Set(),
    add(c) { this._c.add(c); }, remove(c) { this._c.delete(c); },
    toggle(c, f) { f = f === undefined ? !this._c.has(c) : f; f ? this._c.add(c) : this._c.delete(c); return f; },
    contains(c) { return this._c.has(c); }
  },
  style: { _p: {}, setProperty(k, v) { this._p[k] = v; }, getProperty(k) { return this._p[k]; } },
  innerText: '', textContent: '', offsetWidth: 44, offsetHeight: 44,
  addEventListener: () => {},
  getContext: () => ({
    clearRect: () => {}, save: () => {}, restore: () => {}, beginPath: () => {},
    arc: () => {}, clip: () => {}, fillRect: () => {}, moveTo: () => {}, lineTo: () => {},
    closePath: () => {}, fill: () => {}, stroke: () => {}, ellipse: () => {},
    createRadialGradient: () => ({ addColorStop: () => {} }),
    createLinearGradient: () => ({ addColorStop: () => {} })
  }),
  querySelector: (sel) => createMockElement('sub_' + sel)
});
global.document = {
  getElementById: (id) => createMockElement(id),
  querySelector: (sel) => createMockElement(sel),
  readyState: 'complete', addEventListener: () => {}
};
global.window = { addEventListener: () => {}, performance: { now: () => Date.now() } };
global.performance = global.window.performance;
global.requestAnimationFrame = () => 1;
global.cancelAnimationFrame = () => {};
try {
  z:
} catch (err) {
  console.error(err); process.exit(1);
}
Únodez--input-type=modulez-eT)Úcapture_outputÚtextÚcwdr   zNode execution failed (code z):
)
Ú
subprocessÚrunÚstrÚPROJECT_ROOTÚ
returncodeÚRuntimeErrorÚstderrÚjsonÚloadsÚstdout)r   Ú	bootstrapÚress      úAC:\Projects\FreeExile\tests\unit\test_challenger_m1_edge_cases.pyÚrun_node_evalr      s–   € ð#ð> ð?#ð #ð #€IõH Œ.Ø	Ð&¨¨iÐ8Ø $­CµÑ,=Ô,=ðñ ô €Cð „~˜ÒÐÝÐZ¸#¼.ÐZÐZÈcÌjÐZÐZÑ[Ô[Ð[ÝŒ:�c”jÑ!Ô!Ð!ó    c                   ó0   — e Zd ZdZd„ Zd„ Zd„ Zd„ Zd„ ZdS )ÚTestHudOrbsExtremeValueszMStress tests HudOrbs with adversarial, extreme, and malformed numeric values.c                 óÎ   — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         |                      |d         ¦  «         dS )zQAsserts negative HP is clamped to 0 and dead state does not trigger low-HP pulse.a9  
        const { HudOrbs } = await import('./client/webapp/js/ui/hud_orbs.js');
        HudOrbs.init(); HudOrbs.displayedHp = 50;
        HudOrbs.update(-50, 100, 50, 50, 0.016);
        console.log(JSON.stringify({ displayedHp: HudOrbs.displayedHp, isLowHp: HudOrbs.isLowHp }));
        process.exit(0);
        ÚdisplayedHpr   é2   ÚisLowHpN)r   ÚassertGreaterEqualÚassertLessEqualÚassertFalse©ÚselfÚjsÚdatas      r   Ú test_negative_hp_clamped_to_zeroz9TestHudOrbsExtremeValues.test_negative_hp_clamped_to_zeroB   sl   € ðˆõ ˜RÑ Ô ˆØ×Ò  ]Ô 3°QÑ7Ô7Ð7Ø×Ò˜T -Ô0°"Ñ5Ô5Ð5Ø×Ò˜˜iœÑ)Ô)Ð)Ð)Ð)r   c                 ó  — d}t          |¦  «        }|                      t          |d         ¦  «                             ¦   «         ¦  «         |                      t          |d         ¦  «                             ¦   «         ¦  «         dS )zBAsserts float HP/Mana values render properly rounded integer text.aO  
        const { HudOrbs } = await import('./client/webapp/js/ui/hud_orbs.js');
        HudOrbs.init();
        HudOrbs.update(33.33333, 100, 17.8888, 50, 0.016);
        console.log(JSON.stringify({
          lifeCur: HudOrbs.txtLifeCur?.innerText, manaCur: HudOrbs.txtManaCur?.innerText
        }));
        process.exit(0);
        ÚlifeCurÚmanaCurN)r   Ú
assertTruer   Úisdigitr#   s      r   Útest_float_precision_roundingz6TestHudOrbsExtremeValues.test_float_precision_roundingP   sr   € ðˆõ ˜RÑ Ô ˆØ�Š�˜D œOÑ,Ô,×4Ò4Ñ6Ô6Ñ7Ô7Ð7Ø�Š�˜D œOÑ,Ô,×4Ò4Ñ6Ô6Ñ7Ô7Ð7Ð7Ð7r   c                 ó˜   — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         dS )zCAsserts HP values > 10,000 scale smoothly within max HP boundaries.a@  
        const { HudOrbs } = await import('./client/webapp/js/ui/hud_orbs.js');
        HudOrbs.init();
        HudOrbs.update(15000, 20000, 8000, 10000, 0.016);
        console.log(JSON.stringify({
          maxHp: HudOrbs.displayedMaxHp, maxMana: HudOrbs.displayedMaxMana
        }));
        process.exit(0);
        ÚmaxHpi N  ÚmaxManai'  N©r   ÚassertEqualr#   s      r   Útest_extreme_large_hp_valuesz5TestHudOrbsExtremeValues.test_extreme_large_hp_values_   sR   € ðˆõ ˜RÑ Ô ˆØ×Ò˜˜gœ¨Ñ.Ô.Ð.Ø×Ò˜˜iœ¨%Ñ0Ô0Ð0Ð0Ð0r   c                 óÎ   — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         d¦  «         |                      |d         d¦  «         dS )zCAsserts 10% <-> 90% HP oscillation does not diverge or produce NaN.aÎ  
        const { HudOrbs } = await import('./client/webapp/js/ui/hud_orbs.js');
        HudOrbs.init(); HudOrbs.displayedHp = 50;
        let hp = 10;
        for (let i = 0; i < 60; i++) {
          hp = (hp === 10) ? 90 : 10;
          HudOrbs.update(hp, 100, 50, 50, 0.016);
        }
        console.log(JSON.stringify({
          displayedHp: HudOrbs.displayedHp, isFinite: Number.isFinite(HudOrbs.displayedHp)
        }));
        process.exit(0);
        ÚisFiniter   é
   éZ   N)r   r+   ÚassertGreaterÚ
assertLessr#   s      r   Ú test_rapid_oscillation_stabilityz9TestHudOrbsExtremeValues.test_rapid_oscillation_stabilityn   si   € ðˆõ ˜RÑ Ô ˆØ�Š˜˜ZÔ(Ñ)Ô)Ð)Ø×Ò˜4 Ô.°Ñ3Ô3Ð3Ø�Š˜˜]Ô+¨RÑ0Ô0Ð0Ð0Ð0r   c                 ó˜   — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         dS )zNAdversarial Test: Validates resilience against NaN input poisoning in HudOrbs.aÊ  
        const { HudOrbs } = await import('./client/webapp/js/ui/hud_orbs.js');
        HudOrbs.init(); HudOrbs.displayedHp = 100;
        HudOrbs.update(NaN, 100, 50, 50, 0.016);
        const isPoisoned = Number.isNaN(HudOrbs.displayedHp);
        HudOrbs.update(80, 100, 50, 50, 0.016);
        const recoveryFailed = Number.isNaN(HudOrbs.displayedHp);
        console.log(JSON.stringify({ isPoisoned, recoveryFailed }));
        process.exit(0);
        Ú
isPoisonedz)HudOrbs must not be poisoned by NaN inputÚrecoveryFailedz%HudOrbs must cleanly recover from NaNN©r   r"   r#   s      r   Ú&test_nan_input_poisoning_vulnerabilityz?TestHudOrbsExtremeValues.test_nan_input_poisoning_vulnerability‚   sW   € ð	ˆõ ˜RÑ Ô ˆØ×Ò˜˜lÔ+Ð-XÑYÔYÐYØ×Ò˜Ð.Ô/Ð1XÑYÔYÐYÐYÐYr   N)	Ú__name__Ú
__module__Ú__qualname__Ú__doc__r'   r-   r3   r:   r?   © r   r   r   r   ?   sj   € € € € € ØWÐWð*ð *ð *ð8ð 8ð 8ð1ð 1ð 1ð1ð 1ð 1ð(Zð Zð Zð Zð Zr   r   c                   ó*   — e Zd ZdZd„ Zd„ Zd„ Zd„ ZdS )Ú"TestSkillBarCooldownAndConcurrencyzSStress tests SkillBarController under high-frequency cooldown calls and edge cases.c                 óš   — d}t          |¦  «        }|                      |d         dd¦  «         |                      |d         d¦  «         dS )zMAsserts 1,000 rapid cooldown triggers execute without timing out or crashing.aÃ  
        const { SkillBarController } = await import('./client/webapp/js/ui/skill_bar_controller.js');
        const ctrl = new SkillBarController();
        ctrl.init();
        const t0 = Date.now();
        for (let i = 0; i < 1000; i++) ctrl.triggerCooldown('1', 1.5);
        const elapsed = Date.now() - t0;
        console.log(JSON.stringify({ elapsedMs: elapsed, remaining: ctrl.getRemainingCooldown('1') }));
        process.exit(0);
        Ú	elapsedMsiô  z1000 calls must take < 500msÚ	remainingg      ø?N)r   r9   r2   r#   s      r   Útest_cooldown_spam_1000_callsz@TestSkillBarCooldownAndConcurrency.test_cooldown_spam_1000_calls–   sU   € ð	ˆõ ˜RÑ Ô ˆØ�Š˜˜[Ô)¨3Ð0NÑOÔOÐOØ×Ò˜˜kÔ*¨CÑ0Ô0Ð0Ð0Ð0r   c                 óœ   — d}t          |¦  «        }|                      |d         dd¬¦  «         |                      |d         d¦  «         dS )	zLAsserts re-triggering cooldown during an active sweep resets remaining time.aç  
        const { SkillBarController } = await import('./client/webapp/js/ui/skill_bar_controller.js');
        const ctrl = new SkillBarController();
        ctrl.init();
        ctrl.triggerCooldown('1', 5.0);
        ctrl._updateAllCooldowns(2.0);
        const midRemaining = ctrl.getRemainingCooldown('1');
        ctrl.triggerCooldown('1', 4.0);
        console.log(JSON.stringify({ midRemaining, resetRemaining: ctrl.getRemainingCooldown('1') }));
        process.exit(0);
        ÚmidRemainingg      @é   )ÚplacesÚresetRemainingg      @N)r   ÚassertAlmostEqualr2   r#   s      r   Ú!test_cooldown_during_active_sweepzDTestSkillBarCooldownAndConcurrency.test_cooldown_during_active_sweep¦   sZ   € ð
ˆõ ˜RÑ Ô ˆØ×Ò˜t NÔ3°SÀÐÑCÔCÐCØ×Ò˜Ð.Ô/°Ñ5Ô5Ð5Ð5Ð5r   c                 ó˜   — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         dd¦  «         dS )z?Adversarial Test: durationSec <= 0 clears cooldown immediately.aÅ  
        const { SkillBarController } = await import('./client/webapp/js/ui/skill_bar_controller.js');
        const ctrl = new SkillBarController();
        ctrl.init();
        ctrl.registerSlot('zero_test', { cooldownTotal: 3.0 });
        const resZero = ctrl.triggerCooldown('zero_test', 0);
        const remZero = ctrl.getRemainingCooldown('zero_test');
        console.log(JSON.stringify({ resZero, remZero }));
        process.exit(0);
        ÚresZeroÚremZeror   z%Passing 0 clears cooldown immediatelyN)r   r+   r2   r#   s      r   Ú0test_cooldown_duration_lte_zero_fallback_anomalyzSTestSkillBarCooldownAndConcurrency.test_cooldown_duration_lte_zero_fallback_anomaly·   sQ   € ð	ˆõ ˜RÑ Ô ˆØ�Š˜˜YœÑ(Ô(Ð(Ø×Ò˜˜iœ¨!Ð-TÑUÔUÐUÐUÐUr   c                 ó  — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         ¦  «         |                      |d         d¦  «         |                      |d         ¦  «         dS )	zFAsserts charged skills clamp charges at 0 and restore charges on tick.a  
        const { SkillBarController } = await import('./client/webapp/js/ui/skill_bar_controller.js');
        const ctrl = new SkillBarController();
        ctrl.init();
        ctrl.registerSlot('charge_slot', { cooldownTotal: 2.0, charges: 3, rechargeDuration: 2.0 });
        for (let i = 0; i < 10; i++) ctrl.activateSlot('charge_slot');
        const chargesAtDepletion = ctrl.slots.get('charge_slot').charges;
        const isOnCd = ctrl.isSlotOnCooldown('charge_slot');
        ctrl._updateAllCooldowns(2.1);
        console.log(JSON.stringify({
          chargesAtDepletion, isOnCd,
          chargesRecovered: ctrl.slots.get('charge_slot').charges,
          canActivateNow: !ctrl.isSlotOnCooldown('charge_slot')
        }));
        process.exit(0);
        ÚchargesAtDepletionr   ÚisOnCdÚchargesRecoveredrM   ÚcanActivateNowN)r   r2   r+   r#   s      r   Ú/test_charged_skill_depletion_and_recovery_cyclezRTestSkillBarCooldownAndConcurrency.test_charged_skill_depletion_and_recovery_cycleÇ   s„   € ðˆõ  ˜RÑ Ô ˆØ×Ò˜Ð2Ô3°QÑ7Ô7Ð7Ø�Š˜˜XœÑ'Ô'Ð'Ø×Ò˜Ð0Ô1°1Ñ5Ô5Ð5Ø�Š˜Ð-Ô.Ñ/Ô/Ð/Ð/Ð/r   N)r@   rA   rB   rC   rJ   rQ   rU   r[   rD   r   r   rF   rF   “   sY   € € € € € Ø]Ð]ð1ð 1ð 1ð 6ð 6ð 6ð"Vð Vð Vð 0ð 0ð 0ð 0ð 0r   rF   c                   ó$   — e Zd ZdZd„ Zd„ Zd„ ZdS )Ú!TestSkillSlotIndexingAndEdgeCaseszPStress tests SkillBarController slot registration, lookups, and unmapped inputs.c                 ó¤  — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         |                      |d         d¦  «         |                      |d	         ¦  «         d
S )zEAsserts querying out-of-bounds or non-existent slots returns false/0.aq  
        const { SkillBarController } = await import('./client/webapp/js/ui/skill_bar_controller.js');
        const ctrl = new SkillBarController();
        ctrl.init();
        console.log(JSON.stringify({
          negTrigger: ctrl.triggerCooldown(-1, 2.0),
          oobTrigger: ctrl.triggerCooldown(999, 2.0),
          emptyTrigger: ctrl.triggerCooldown('', 2.0),
          nullTrigger: ctrl.triggerCooldown(null, 2.0),
          oobCooldown: ctrl.isSlotOnCooldown(999),
          oobRemaining: ctrl.getRemainingCooldown(999),
          oobActivate: ctrl.activateSlot(999)
        }));
        process.exit(0);
        Ú
negTriggerÚ
oobTriggerÚemptyTriggerÚnullTriggerÚoobCooldownÚoobRemainingr   ÚoobActivateN)r   r"   r2   r#   s      r   Ú+test_out_of_bounds_and_invalid_slot_lookupszMTestSkillSlotIndexingAndEdgeCases.test_out_of_bounds_and_invalid_slot_lookupsã   sÏ   € ðˆõ ˜RÑ Ô ˆØ×Ò˜˜lÔ+Ñ,Ô,Ð,Ø×Ò˜˜lÔ+Ñ,Ô,Ð,Ø×Ò˜˜nÔ-Ñ.Ô.Ð.Ø×Ò˜˜mÔ,Ñ-Ô-Ð-Ø×Ò˜˜mÔ,Ñ-Ô-Ð-Ø×Ò˜˜nÔ-¨qÑ1Ô1Ð1Ø×Ò˜˜mÔ,Ñ-Ô-Ð-Ð-Ð-r   c                 ó`   — d}t          |¦  «        }|                      |d         d¦  «         dS )zHAsserts setting an unknown skill ID handles missing metadata gracefully.aV  
        const { SkillBarController } = await import('./client/webapp/js/ui/skill_bar_controller.js');
        const ctrl = new SkillBarController();
        ctrl.init();
        ctrl.setSlotSkill('1', 'unknown_martial_art_xyz');
        console.log(JSON.stringify({ skillId: ctrl.slots.get('1').skillId }));
        process.exit(0);
        ÚskillIdÚunknown_martial_art_xyzNr1   r#   s      r   Ú%test_unregistered_skill_atlas_mappingzGTestSkillSlotIndexingAndEdgeCases.test_unregistered_skill_atlas_mappingý   s9   € ðˆõ ˜RÑ Ô ˆØ×Ò˜˜iœÐ*CÑDÔDÐDÐDÐDr   c                 ó`   — d}t          |¦  «        }|                      |d         d¦  «         dS )zJAdversarial Test: Tests whether handleKeyPress('') triggers unkeyed slots.aL  
        const { SkillBarController } = await import('./client/webapp/js/ui/skill_bar_controller.js');
        const ctrl = new SkillBarController();
        ctrl.init();
        ctrl.registerSlot('unkeyed', { key: '' });
        console.log(JSON.stringify({ triggered: ctrl.handleKeyPress('') }));
        process.exit(0);
        Ú	triggeredz=Remediated: handleKeyPress('') must not trigger unkeyed slotsNr>   r#   s      r   Ú,test_handle_key_press_empty_string_edge_casezNTestSkillSlotIndexingAndEdgeCases.test_handle_key_press_empty_string_edge_case
  s:   € ðˆõ ˜RÑ Ô ˆØ×Ò˜˜kÔ*Ð,kÑlÔlÐlÐlÐlr   N)r@   rA   rB   rC   rf   rj   rm   rD   r   r   r]   r]   à   sO   € € € € € ØZÐZð.ð .ð .ð4Eð Eð Eðmð mð mð mð mr   r]   Ú__main__)rC   r   r   ÚunittestÚpathlibr   Ú__file__ÚresolveÚparentr   r   Údictr   ÚTestCaser   rF   r]   r@   ÚmainrD   r   r   ú<module>rw      sV  ððð ð €€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€ð,"˜3ð ," 4ð ,"ð ,"ð ,"ð ,"ð^QZð QZð QZð QZð QZ˜xÔ0ñ QZô QZð QZðhJ0ð J0ð J0ð J0ð J0¨Ô):ñ J0ô J0ð J0ðZ5mð 5mð 5mð 5mð 5m¨Ô(9ñ 5mô 5mð 5mðp ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr   