ui(alerts): RED tests for form-state fireMode toggle clearing
Three failing tests pinning Task 4.3's mode-toggle state hygiene:
- clears threshold+windowSeconds on COUNT_IN_WINDOW -> PER_EXCHANGE
- returns to defaults (not stale values) on PER_EXCHANGE -> COUNT_IN_WINDOW
- forces reNotifyMinutes=0 and forDurationSeconds=0 on PER_EXCHANGE
Targets a to-be-introduced pure helper `applyFireModeChange(form, newMode)`
in form-state.ts. Task 4.3 will implement the helper and wire it into
ExchangeMatchForm so the Fire-mode <Select> calls it instead of the current
raw patch({ fireMode }) that leaves stale fields.
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { initialForm, toRequest, validateStep } from './form-state';
|
||||
// `applyFireModeChange` will be introduced by Task 4.3 as a pure helper in
|
||||
// `form-state.ts` so that the ExchangeMatchForm can reuse it on mode-toggle
|
||||
// and guarantee state hygiene (no stale COUNT_IN_WINDOW fields leaking into a
|
||||
// PER_EXCHANGE rule payload and vice-versa). These tests pin the contract.
|
||||
import { applyFireModeChange, initialForm, toRequest, validateStep } from './form-state';
|
||||
|
||||
describe('initialForm', () => {
|
||||
it('defaults to env-wide ROUTE_METRIC with safe intervals', () => {
|
||||
@@ -80,3 +84,79 @@ describe('validateStep', () => {
|
||||
expect(validateStep('condition', f)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Task 4.2 RED: fire-mode toggle state hygiene
|
||||
//
|
||||
// `applyFireModeChange(form, newMode)` is the pure helper (Task 4.3) that the
|
||||
// ExchangeMatchForm's Fire-mode <Select> will call in place of the current
|
||||
// raw `patch({ fireMode })`. It must guarantee:
|
||||
//
|
||||
// - Switching to PER_EXCHANGE clears COUNT_IN_WINDOW-only fields
|
||||
// (condition.threshold, condition.windowSeconds) AND forces
|
||||
// top-level reNotifyMinutes = 0, forDurationSeconds = 0. PER_EXCHANGE
|
||||
// is exactly-once-per-exchange — re-notify cadence and hold-duration
|
||||
// are semantically meaningless and must not leak into toRequest().
|
||||
// - Switching back to COUNT_IN_WINDOW resets to defaults (zero), never
|
||||
// restoring stale values from the previous COUNT_IN_WINDOW session.
|
||||
//
|
||||
// These tests will fail until Task 4.3 introduces the helper.
|
||||
// ----------------------------------------------------------------------------
|
||||
describe('applyFireModeChange (fire-mode toggle hygiene)', () => {
|
||||
const exchangeMatchForm = () => {
|
||||
const f = initialForm();
|
||||
f.conditionKind = 'EXCHANGE_MATCH';
|
||||
f.condition = {
|
||||
kind: 'EXCHANGE_MATCH',
|
||||
scope: {},
|
||||
fireMode: 'COUNT_IN_WINDOW',
|
||||
} as unknown as typeof f.condition;
|
||||
return f;
|
||||
};
|
||||
|
||||
it('clears COUNT_IN_WINDOW fields when switching to PER_EXCHANGE', () => {
|
||||
let f = exchangeMatchForm();
|
||||
// Simulate a user who filled in COUNT_IN_WINDOW fields first.
|
||||
f.condition = {
|
||||
...(f.condition as Record<string, unknown>),
|
||||
threshold: 5,
|
||||
windowSeconds: 300,
|
||||
} as typeof f.condition;
|
||||
|
||||
f = applyFireModeChange(f, 'PER_EXCHANGE');
|
||||
|
||||
const c = f.condition as Record<string, unknown>;
|
||||
expect(c.fireMode).toBe('PER_EXCHANGE');
|
||||
expect(c.threshold ?? 0).toBe(0);
|
||||
expect(c.windowSeconds ?? 0).toBe(0);
|
||||
});
|
||||
|
||||
it('resets to defaults (not stale values) when switching back to COUNT_IN_WINDOW', () => {
|
||||
let f = exchangeMatchForm();
|
||||
f.condition = {
|
||||
...(f.condition as Record<string, unknown>),
|
||||
threshold: 5,
|
||||
windowSeconds: 300,
|
||||
} as typeof f.condition;
|
||||
|
||||
f = applyFireModeChange(f, 'PER_EXCHANGE');
|
||||
f = applyFireModeChange(f, 'COUNT_IN_WINDOW');
|
||||
|
||||
const c = f.condition as Record<string, unknown>;
|
||||
expect(c.fireMode).toBe('COUNT_IN_WINDOW');
|
||||
// Must be fresh defaults, not the stale 5 / 300 the user typed before.
|
||||
expect(c.threshold ?? 0).toBe(0);
|
||||
expect(c.windowSeconds ?? 0).toBe(0);
|
||||
});
|
||||
|
||||
it('forces reNotifyMinutes=0 and forDurationSeconds=0 when switching to PER_EXCHANGE', () => {
|
||||
let f = exchangeMatchForm();
|
||||
f.reNotifyMinutes = 60;
|
||||
f.forDurationSeconds = 120;
|
||||
|
||||
f = applyFireModeChange(f, 'PER_EXCHANGE');
|
||||
|
||||
expect(f.reNotifyMinutes).toBe(0);
|
||||
expect(f.forDurationSeconds).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user