@ingissa/navcore-simulator 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +2 -0
  2. package/dist/index.d.mts +87 -0
  3. package/dist/{simulator/src/react-native/useSimulator.d.ts → index.d.ts} +37 -9
  4. package/dist/index.js +1393 -0
  5. package/dist/index.mjs +1383 -0
  6. package/dist/node.d.mts +527 -0
  7. package/dist/node.d.ts +527 -0
  8. package/dist/node.js +957 -0
  9. package/dist/node.mjs +949 -0
  10. package/dist/react-native.d.mts +4 -0
  11. package/dist/react-native.d.ts +4 -0
  12. package/dist/react-native.js +1393 -0
  13. package/dist/react-native.mjs +1383 -0
  14. package/package.json +19 -16
  15. package/dist/core/src/NavCore.d.ts +0 -142
  16. package/dist/core/src/NavCore.d.ts.map +0 -1
  17. package/dist/core/src/NavCore.js +0 -390
  18. package/dist/core/src/bearing/BearingEngine.d.ts +0 -24
  19. package/dist/core/src/bearing/BearingEngine.d.ts.map +0 -1
  20. package/dist/core/src/bearing/BearingEngine.js +0 -50
  21. package/dist/core/src/constants.d.ts +0 -34
  22. package/dist/core/src/constants.d.ts.map +0 -1
  23. package/dist/core/src/constants.js +0 -41
  24. package/dist/core/src/corridor/DeviationDetector.d.ts +0 -33
  25. package/dist/core/src/corridor/DeviationDetector.d.ts.map +0 -1
  26. package/dist/core/src/corridor/DeviationDetector.js +0 -83
  27. package/dist/core/src/corridor/DynamicCorridor.d.ts +0 -13
  28. package/dist/core/src/corridor/DynamicCorridor.d.ts.map +0 -1
  29. package/dist/core/src/corridor/DynamicCorridor.js +0 -26
  30. package/dist/core/src/dead-reckoning/DeadReckoning.d.ts +0 -35
  31. package/dist/core/src/dead-reckoning/DeadReckoning.d.ts.map +0 -1
  32. package/dist/core/src/dead-reckoning/DeadReckoning.js +0 -49
  33. package/dist/core/src/directions/DirectionsProvider.d.ts +0 -70
  34. package/dist/core/src/directions/DirectionsProvider.d.ts.map +0 -1
  35. package/dist/core/src/directions/DirectionsProvider.js +0 -7
  36. package/dist/core/src/directions/OSRMDirectionsProvider.d.ts +0 -26
  37. package/dist/core/src/directions/OSRMDirectionsProvider.d.ts.map +0 -1
  38. package/dist/core/src/directions/OSRMDirectionsProvider.js +0 -90
  39. package/dist/core/src/directions/OpenRouteServiceProvider.d.ts +0 -30
  40. package/dist/core/src/directions/OpenRouteServiceProvider.d.ts.map +0 -1
  41. package/dist/core/src/directions/OpenRouteServiceProvider.js +0 -111
  42. package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts +0 -33
  43. package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts.map +0 -1
  44. package/dist/core/src/directions/ValhallaDirectionsProvider.js +0 -155
  45. package/dist/core/src/eta/ETAEngine.d.ts +0 -61
  46. package/dist/core/src/eta/ETAEngine.d.ts.map +0 -1
  47. package/dist/core/src/eta/ETAEngine.js +0 -74
  48. package/dist/core/src/features.d.ts +0 -76
  49. package/dist/core/src/features.d.ts.map +0 -1
  50. package/dist/core/src/features.js +0 -76
  51. package/dist/core/src/geo/bearing.d.ts +0 -11
  52. package/dist/core/src/geo/bearing.d.ts.map +0 -1
  53. package/dist/core/src/geo/bearing.js +0 -25
  54. package/dist/core/src/geo/cumulative.d.ts +0 -9
  55. package/dist/core/src/geo/cumulative.d.ts.map +0 -1
  56. package/dist/core/src/geo/cumulative.js +0 -20
  57. package/dist/core/src/geo/haversine.d.ts +0 -13
  58. package/dist/core/src/geo/haversine.d.ts.map +0 -1
  59. package/dist/core/src/geo/haversine.js +0 -39
  60. package/dist/core/src/geo/projection.d.ts +0 -7
  61. package/dist/core/src/geo/projection.d.ts.map +0 -1
  62. package/dist/core/src/geo/projection.js +0 -17
  63. package/dist/core/src/geofencing/GeofencingEngine.d.ts +0 -93
  64. package/dist/core/src/geofencing/GeofencingEngine.d.ts.map +0 -1
  65. package/dist/core/src/geofencing/GeofencingEngine.js +0 -154
  66. package/dist/core/src/index.d.ts +0 -66
  67. package/dist/core/src/index.d.ts.map +0 -1
  68. package/dist/core/src/index.js +0 -62
  69. package/dist/core/src/kalman/KalmanFilter2D.d.ts +0 -46
  70. package/dist/core/src/kalman/KalmanFilter2D.d.ts.map +0 -1
  71. package/dist/core/src/kalman/KalmanFilter2D.js +0 -77
  72. package/dist/core/src/license/LicenseManager.d.ts +0 -40
  73. package/dist/core/src/license/LicenseManager.d.ts.map +0 -1
  74. package/dist/core/src/license/LicenseManager.js +0 -119
  75. package/dist/core/src/license/verifyP256Signature.d.ts +0 -2
  76. package/dist/core/src/license/verifyP256Signature.d.ts.map +0 -1
  77. package/dist/core/src/license/verifyP256Signature.js +0 -26
  78. package/dist/core/src/off-route/NavigationFSM.d.ts +0 -42
  79. package/dist/core/src/off-route/NavigationFSM.d.ts.map +0 -1
  80. package/dist/core/src/off-route/NavigationFSM.js +0 -56
  81. package/dist/core/src/off-route/OffRouteFSM.d.ts +0 -70
  82. package/dist/core/src/off-route/OffRouteFSM.d.ts.map +0 -1
  83. package/dist/core/src/off-route/OffRouteFSM.js +0 -151
  84. package/dist/core/src/predictor/RouteOffsetPredictor.d.ts +0 -24
  85. package/dist/core/src/predictor/RouteOffsetPredictor.d.ts.map +0 -1
  86. package/dist/core/src/predictor/RouteOffsetPredictor.js +0 -55
  87. package/dist/core/src/progress/InstructionEditor.d.ts +0 -77
  88. package/dist/core/src/progress/InstructionEditor.d.ts.map +0 -1
  89. package/dist/core/src/progress/InstructionEditor.js +0 -151
  90. package/dist/core/src/progress/InstructionResolver.d.ts +0 -25
  91. package/dist/core/src/progress/InstructionResolver.d.ts.map +0 -1
  92. package/dist/core/src/progress/InstructionResolver.js +0 -78
  93. package/dist/core/src/progress/ProgressTracker.d.ts +0 -12
  94. package/dist/core/src/progress/ProgressTracker.d.ts.map +0 -1
  95. package/dist/core/src/progress/ProgressTracker.js +0 -22
  96. package/dist/core/src/renderer/MapRendererAdapter.d.ts +0 -72
  97. package/dist/core/src/renderer/MapRendererAdapter.d.ts.map +0 -1
  98. package/dist/core/src/renderer/MapRendererAdapter.js +0 -19
  99. package/dist/core/src/route-builder/CustomRouteBuilder.d.ts +0 -83
  100. package/dist/core/src/route-builder/CustomRouteBuilder.d.ts.map +0 -1
  101. package/dist/core/src/route-builder/CustomRouteBuilder.js +0 -160
  102. package/dist/core/src/route-plan/RoutePlan.d.ts +0 -38
  103. package/dist/core/src/route-plan/RoutePlan.d.ts.map +0 -1
  104. package/dist/core/src/route-plan/RoutePlan.js +0 -102
  105. package/dist/core/src/session/NavigationSession.d.ts +0 -81
  106. package/dist/core/src/session/NavigationSession.d.ts.map +0 -1
  107. package/dist/core/src/session/NavigationSession.js +0 -115
  108. package/dist/core/src/snapper/ParallelRoadResolver.d.ts +0 -86
  109. package/dist/core/src/snapper/ParallelRoadResolver.d.ts.map +0 -1
  110. package/dist/core/src/snapper/ParallelRoadResolver.js +0 -83
  111. package/dist/core/src/snapper/RouteSnapper.d.ts +0 -38
  112. package/dist/core/src/snapper/RouteSnapper.d.ts.map +0 -1
  113. package/dist/core/src/snapper/RouteSnapper.js +0 -80
  114. package/dist/core/src/voice/VoiceTriggerEngine.d.ts +0 -80
  115. package/dist/core/src/voice/VoiceTriggerEngine.d.ts.map +0 -1
  116. package/dist/core/src/voice/VoiceTriggerEngine.js +0 -108
  117. package/dist/simulator/src/core/NoiseEngine.d.ts +0 -46
  118. package/dist/simulator/src/core/NoiseEngine.d.ts.map +0 -1
  119. package/dist/simulator/src/core/NoiseEngine.js +0 -106
  120. package/dist/simulator/src/core/RouteWalker.d.ts +0 -78
  121. package/dist/simulator/src/core/RouteWalker.d.ts.map +0 -1
  122. package/dist/simulator/src/core/RouteWalker.js +0 -180
  123. package/dist/simulator/src/core/SimulatorEngine.d.ts +0 -53
  124. package/dist/simulator/src/core/SimulatorEngine.d.ts.map +0 -1
  125. package/dist/simulator/src/core/SimulatorEngine.js +0 -298
  126. package/dist/simulator/src/core/types.d.ts +0 -157
  127. package/dist/simulator/src/core/types.d.ts.map +0 -1
  128. package/dist/simulator/src/core/types.js +0 -7
  129. package/dist/simulator/src/fleet/FleetSimulator.d.ts +0 -60
  130. package/dist/simulator/src/fleet/FleetSimulator.d.ts.map +0 -1
  131. package/dist/simulator/src/fleet/FleetSimulator.js +0 -114
  132. package/dist/simulator/src/index.d.ts +0 -28
  133. package/dist/simulator/src/index.d.ts.map +0 -1
  134. package/dist/simulator/src/index.js +0 -29
  135. package/dist/simulator/src/node.d.ts +0 -26
  136. package/dist/simulator/src/node.d.ts.map +0 -1
  137. package/dist/simulator/src/node.js +0 -25
  138. package/dist/simulator/src/react-native/SimulatorControls.d.ts +0 -35
  139. package/dist/simulator/src/react-native/SimulatorControls.d.ts.map +0 -1
  140. package/dist/simulator/src/react-native/SimulatorControls.js +0 -254
  141. package/dist/simulator/src/react-native/useSimulator.d.ts.map +0 -1
  142. package/dist/simulator/src/react-native/useSimulator.js +0 -100
  143. package/dist/simulator/src/replay/GpxParser.d.ts +0 -40
  144. package/dist/simulator/src/replay/GpxParser.d.ts.map +0 -1
  145. package/dist/simulator/src/replay/GpxParser.js +0 -95
  146. package/dist/simulator/src/replay/SessionRecorder.d.ts +0 -37
  147. package/dist/simulator/src/replay/SessionRecorder.d.ts.map +0 -1
  148. package/dist/simulator/src/replay/SessionRecorder.js +0 -120
  149. package/dist/simulator/src/scenarios/ScenarioInjector.d.ts +0 -55
  150. package/dist/simulator/src/scenarios/ScenarioInjector.d.ts.map +0 -1
  151. package/dist/simulator/src/scenarios/ScenarioInjector.js +0 -150
  152. package/src/__tests__/integration/simulator-pipeline.test.ts +0 -233
  153. package/src/core/NoiseEngine.ts +0 -137
  154. package/src/core/RouteWalker.ts +0 -227
  155. package/src/core/SimulatorEngine.ts +0 -364
  156. package/src/core/__tests__/NoiseEngine.test.ts +0 -131
  157. package/src/core/__tests__/RouteWalker.test.ts +0 -139
  158. package/src/core/__tests__/SimulatorEngine.test.ts +0 -317
  159. package/src/core/types.ts +0 -191
  160. package/src/fleet/FleetSimulator.ts +0 -162
  161. package/src/index.ts +0 -54
  162. package/src/node.ts +0 -48
  163. package/src/react-native/SimulatorControls.tsx +0 -473
  164. package/src/react-native/useSimulator.ts +0 -155
  165. package/src/replay/GpxParser.ts +0 -122
  166. package/src/replay/SessionRecorder.ts +0 -138
  167. package/src/replay/__tests__/GpxParser.test.ts +0 -136
  168. package/src/scenarios/ScenarioInjector.ts +0 -170
  169. package/src/scenarios/__tests__/ScenarioInjector.test.ts +0 -171
@@ -1,170 +0,0 @@
1
- /**
2
- * ScenarioInjector — Registers and fires Scenario objects at geometry indices.
3
- *
4
- * Called internally by SimulatorEngine on each tick.
5
- * Also provides static factory methods for the built-in scenario types.
6
- */
7
-
8
- import type { Scenario } from '../core/types';
9
-
10
- export type ScenarioFireCallback = (scenario: Scenario) => void;
11
- export type ScenarioEndCallback = (scenario: Scenario) => void;
12
-
13
- export class ScenarioInjector {
14
- /** Map from triggerIndex → Scenario */
15
- private readonly scenarios = new Map<number, Scenario>();
16
- /** Tracks which scenarios have already fired (by name) to avoid double-fire */
17
- private readonly fired = new Set<string>();
18
- /** Currently active scenario, if any */
19
- private _activeScenario: Scenario | null = null;
20
- private _activeEndTimer: ReturnType<typeof setTimeout> | null = null;
21
-
22
- private onStart: ScenarioFireCallback;
23
- private onEnd: ScenarioEndCallback;
24
-
25
- constructor(onStart?: ScenarioFireCallback, onEnd?: ScenarioEndCallback) {
26
- this.onStart = onStart ?? (() => undefined);
27
- this.onEnd = onEnd ?? (() => undefined);
28
- }
29
-
30
- // ── Registration ────────────────────────────────────────────────────────────
31
-
32
- register(scenario: Scenario): void {
33
- this.scenarios.set(scenario.triggerIndex, scenario);
34
- }
35
-
36
- remove(name: string): void {
37
- for (const [idx, s] of this.scenarios) {
38
- if (s.name === name) {
39
- this.scenarios.delete(idx);
40
- break;
41
- }
42
- }
43
- }
44
-
45
- clear(): void {
46
- this.scenarios.clear();
47
- this.fired.clear();
48
- this._clearActiveTimer();
49
- this._activeScenario = null;
50
- }
51
-
52
- // ── Tick ────────────────────────────────────────────────────────────────────
53
-
54
- /**
55
- * Check and fire any scenario whose triggerIndex matches (or was passed) by
56
- * currentIndex. Called by SimulatorEngine on every tick.
57
- *
58
- * Returns the name of the active scenario (if any), for metadata injection.
59
- */
60
- tick(currentIndex: number): string | undefined {
61
- const floorIdx = Math.floor(currentIndex);
62
-
63
- for (const [triggerIdx, scenario] of this.scenarios) {
64
- if (floorIdx >= triggerIdx && !this.fired.has(scenario.name)) {
65
- this.fired.add(scenario.name);
66
- this._fire(scenario);
67
- }
68
- }
69
-
70
- return this._activeScenario?.name;
71
- }
72
-
73
- get activeScenario(): Scenario | null {
74
- return this._activeScenario;
75
- }
76
-
77
- /** Clean up any pending timers */
78
- destroy(): void {
79
- this._clearActiveTimer();
80
- }
81
-
82
- // ── Static factories ────────────────────────────────────────────────────────
83
-
84
- /**
85
- * GPS blackout: no position updates for `durationMs`.
86
- * Engine handles signal loss; this scenario signals the blackout period.
87
- */
88
- static gpsBlackout(atIndex: number, durationMs: number): Scenario {
89
- return {
90
- name: `gps-blackout-@${atIndex}`,
91
- triggerIndex: atIndex,
92
- type: 'GPS_BLACKOUT',
93
- durationMs,
94
- params: { durationMs },
95
- };
96
- }
97
-
98
- /**
99
- * Wrong-side road: offset the simulated position by `offsetM` metres
100
- * perpendicular to the route to stress-test the parallel road resolver.
101
- */
102
- static wrongSideRoad(atIndex: number, offsetM: number): Scenario {
103
- return {
104
- name: `wrong-side-@${atIndex}`,
105
- triggerIndex: atIndex,
106
- type: 'WRONG_SIDE_ROAD',
107
- params: { offsetM },
108
- };
109
- }
110
-
111
- /** U-turn at the given index — stress-tests U-turn protection. */
112
- static uTurn(atIndex: number): Scenario {
113
- return {
114
- name: `uturn-@${atIndex}`,
115
- triggerIndex: atIndex,
116
- type: 'UTURN',
117
- params: {},
118
- };
119
- }
120
-
121
- /** Speed spike: briefly report spikeKmh instead of the configured speed. */
122
- static speedSpike(atIndex: number, spikeKmh: number, durationMs = 2000): Scenario {
123
- return {
124
- name: `speed-spike-@${atIndex}`,
125
- triggerIndex: atIndex,
126
- type: 'SPEED_SPIKE',
127
- durationMs,
128
- params: { spikeKmh },
129
- };
130
- }
131
-
132
- /**
133
- * Accuracy drop: GPS accuracy degrades to newAccuracyM for durationMs.
134
- */
135
- static accuracyDrop(atIndex: number, newAccuracyM: number, durationMs: number): Scenario {
136
- return {
137
- name: `accuracy-drop-@${atIndex}`,
138
- triggerIndex: atIndex,
139
- type: 'ACCURACY_DROP',
140
- durationMs,
141
- params: { newAccuracyM },
142
- };
143
- }
144
-
145
- // ── Private ─────────────────────────────────────────────────────────────────
146
-
147
- private _fire(scenario: Scenario): void {
148
- this._activeScenario = scenario;
149
- try { this.onStart(scenario); } catch (_) { /* never throw from callback */ }
150
-
151
- if (scenario.durationMs != null && scenario.durationMs > 0) {
152
- this._clearActiveTimer();
153
- this._activeEndTimer = setTimeout(() => {
154
- this._activeScenario = null;
155
- try { this.onEnd(scenario); } catch (_) { /* never throw */ }
156
- }, scenario.durationMs);
157
- } else {
158
- // Instantaneous scenario — end immediately
159
- this._activeScenario = null;
160
- try { this.onEnd(scenario); } catch (_) { /* never throw */ }
161
- }
162
- }
163
-
164
- private _clearActiveTimer(): void {
165
- if (this._activeEndTimer !== null) {
166
- clearTimeout(this._activeEndTimer);
167
- this._activeEndTimer = null;
168
- }
169
- }
170
- }
@@ -1,171 +0,0 @@
1
- /**
2
- * ScenarioInjector unit tests
3
- */
4
- import { describe, it, expect, vi, beforeEach } from 'vitest';
5
- import { ScenarioInjector } from '@ingissa/navcore-simulator';
6
-
7
- describe('ScenarioInjector', () => {
8
- describe('register + tick', () => {
9
- it('fires callback at the correct index', () => {
10
- const onStart = vi.fn();
11
- const injector = new ScenarioInjector(onStart);
12
- const scenario = ScenarioInjector.gpsBlackout(5, 1000);
13
- injector.register(scenario);
14
-
15
- // Not yet
16
- injector.tick(3);
17
- expect(onStart).not.toHaveBeenCalled();
18
-
19
- // At trigger index
20
- injector.tick(5);
21
- expect(onStart).toHaveBeenCalledOnce();
22
- expect(onStart.mock.calls[0]![0].type).toBe('GPS_BLACKOUT');
23
- });
24
-
25
- it('fires when index passes the trigger (skipped frame)', () => {
26
- const onStart = vi.fn();
27
- const injector = new ScenarioInjector(onStart);
28
- injector.register(ScenarioInjector.gpsBlackout(10, 500));
29
-
30
- // Jump from 5 to 15 — trigger at 10 should still fire
31
- injector.tick(15);
32
- expect(onStart).toHaveBeenCalledOnce();
33
- });
34
-
35
- it('does NOT double-fire the same scenario', () => {
36
- const onStart = vi.fn();
37
- const injector = new ScenarioInjector(onStart);
38
- injector.register(ScenarioInjector.gpsBlackout(3, 500));
39
-
40
- injector.tick(3);
41
- injector.tick(3);
42
- injector.tick(5);
43
- expect(onStart).toHaveBeenCalledTimes(1);
44
- });
45
-
46
- it('fires onEnd after durationMs (via setTimeout)', () => {
47
- vi.useFakeTimers();
48
- const onEnd = vi.fn();
49
- const injector = new ScenarioInjector(undefined, onEnd);
50
- injector.register(ScenarioInjector.gpsBlackout(0, 2000));
51
-
52
- injector.tick(0);
53
- expect(onEnd).not.toHaveBeenCalled();
54
-
55
- vi.advanceTimersByTime(2000);
56
- expect(onEnd).toHaveBeenCalledOnce();
57
- vi.useRealTimers();
58
- });
59
-
60
- it('fires onEnd immediately for instantaneous scenario (no durationMs)', () => {
61
- const onEnd = vi.fn();
62
- const injector = new ScenarioInjector(undefined, onEnd);
63
- injector.register(ScenarioInjector.uTurn(5));
64
-
65
- injector.tick(5);
66
- expect(onEnd).toHaveBeenCalledOnce();
67
- });
68
- });
69
-
70
- describe('remove', () => {
71
- it('removes a scenario by name before it fires', () => {
72
- const onStart = vi.fn();
73
- const injector = new ScenarioInjector(onStart);
74
- const s = ScenarioInjector.gpsBlackout(4, 1000);
75
- injector.register(s);
76
- injector.remove(s.name);
77
-
78
- injector.tick(4);
79
- expect(onStart).not.toHaveBeenCalled();
80
- });
81
- });
82
-
83
- describe('clear', () => {
84
- it('removes all scenarios', () => {
85
- const onStart = vi.fn();
86
- const injector = new ScenarioInjector(onStart);
87
- injector.register(ScenarioInjector.gpsBlackout(1, 500));
88
- injector.register(ScenarioInjector.uTurn(2));
89
- injector.clear();
90
-
91
- injector.tick(5);
92
- expect(onStart).not.toHaveBeenCalled();
93
- });
94
- });
95
-
96
- describe('activeScenario', () => {
97
- it('is null before firing', () => {
98
- const injector = new ScenarioInjector();
99
- injector.register(ScenarioInjector.gpsBlackout(5, 5000));
100
- expect(injector.activeScenario).toBeNull();
101
- });
102
-
103
- it('is set after firing (while durationMs pending)', () => {
104
- vi.useFakeTimers();
105
- const injector = new ScenarioInjector();
106
- injector.register(ScenarioInjector.gpsBlackout(0, 5000));
107
- injector.tick(0);
108
- expect(injector.activeScenario?.type).toBe('GPS_BLACKOUT');
109
- vi.useRealTimers();
110
- });
111
- });
112
-
113
- describe('tick returns active scenario name', () => {
114
- it('returns undefined when no scenario is active', () => {
115
- const injector = new ScenarioInjector();
116
- injector.register(ScenarioInjector.gpsBlackout(10, 1000));
117
- const result = injector.tick(0);
118
- expect(result).toBeUndefined();
119
- });
120
-
121
- it('returns scenario name after it fires', () => {
122
- vi.useFakeTimers();
123
- const injector = new ScenarioInjector();
124
- const s = ScenarioInjector.gpsBlackout(0, 5000);
125
- injector.register(s);
126
- injector.tick(0);
127
- const name = injector.tick(1);
128
- expect(name).toBe(s.name);
129
- vi.useRealTimers();
130
- });
131
- });
132
-
133
- describe('Static factories', () => {
134
- it('gpsBlackout returns GPS_BLACKOUT type', () => {
135
- const s = ScenarioInjector.gpsBlackout(10, 3000);
136
- expect(s.type).toBe('GPS_BLACKOUT');
137
- expect(s.triggerIndex).toBe(10);
138
- expect(s.durationMs).toBe(3000);
139
- });
140
-
141
- it('wrongSideRoad returns WRONG_SIDE_ROAD type', () => {
142
- const s = ScenarioInjector.wrongSideRoad(20, 18);
143
- expect(s.type).toBe('WRONG_SIDE_ROAD');
144
- expect(s.params?.['offsetM']).toBe(18);
145
- });
146
-
147
- it('uTurn returns UTURN type', () => {
148
- const s = ScenarioInjector.uTurn(15);
149
- expect(s.type).toBe('UTURN');
150
- });
151
-
152
- it('speedSpike returns SPEED_SPIKE type with spikeKmh', () => {
153
- const s = ScenarioInjector.speedSpike(5, 120);
154
- expect(s.type).toBe('SPEED_SPIKE');
155
- expect(s.params?.['spikeKmh']).toBe(120);
156
- });
157
-
158
- it('accuracyDrop returns ACCURACY_DROP type', () => {
159
- const s = ScenarioInjector.accuracyDrop(8, 50, 4000);
160
- expect(s.type).toBe('ACCURACY_DROP');
161
- expect(s.durationMs).toBe(4000);
162
- expect(s.params?.['newAccuracyM']).toBe(50);
163
- });
164
-
165
- it('all factories produce unique names for different indices', () => {
166
- const a = ScenarioInjector.gpsBlackout(1, 1000);
167
- const b = ScenarioInjector.gpsBlackout(2, 1000);
168
- expect(a.name).not.toBe(b.name);
169
- });
170
- });
171
- });