File size: 4,951 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
 * tests/unit/ui/comboFlowModel-cooldown.test.ts
 *
 * TDD for F5.1 (Combo U1b Slice 2) — enrichRunWithConnectionCooldown: overlays the
 * real per-provider connection-cooldown summary (from GET /api/monitoring/health →
 * connectionHealth[provider]) onto a combo run's targets, so the cascade can badge
 * "cooldown 2/3 · 28s" alongside the circuit-breaker badge.
 *
 * Mirrors comboFlowModel-breakers.test.ts (Slice 1).
 * Run: node --import tsx/esm --test tests/unit/ui/comboFlowModel-cooldown.test.ts
 */

import { describe, it } from "node:test";
import assert from "node:assert/strict";

import {
  enrichRunWithConnectionCooldown,
  comboRunToFlow,
  type ComboRunModel,
  type TargetNodeModel,
} from "../../../src/app/(dashboard)/dashboard/combos/live/comboFlowModel.ts";

function mkRun(targets: Array<Partial<TargetNodeModel> & { provider: string }>): ComboRunModel {
  return {
    comboName: "c",
    strategy: "priority",
    outcome: "running",
    startedAt: 0,
    targets: targets.map((t, i) => ({
      targetIndex: i,
      provider: t.provider,
      model: t.model ?? "m",
      state: t.state ?? "idle",
      ...t,
    })),
  };
}

describe("enrichRunWithConnectionCooldown", () => {
  it("returns null for a null run", () => {
    assert.equal(enrichRunWithConnectionCooldown(null, {}), null);
  });

  it("returns the same run reference when no health map is provided", () => {
    const run = mkRun([{ provider: "openai" }]);
    assert.equal(enrichRunWithConnectionCooldown(run, null), run);
    assert.equal(enrichRunWithConnectionCooldown(run, undefined), run);
  });

  it("attaches cooldown count/total/retry for a provider with cooling connections", () => {
    const run = mkRun([{ provider: "anthropic" }, { provider: "openai" }]);
    const out = enrichRunWithConnectionCooldown(run, {
      anthropic: { coolingDown: 2, total: 3, soonestRetryAfterMs: 28_000 },
    });
    assert.ok(out);
    assert.equal(out.targets[0].cooldownCount, 2);
    assert.equal(out.targets[0].cooldownTotal, 3);
    assert.equal(out.targets[0].cooldownRetryAfterMs, 28_000);
    assert.equal(
      out.targets[1].cooldownCount,
      undefined,
      "provider with no cooldown gets no badge"
    );
  });

  it("does not attach a badge when coolingDown is 0 or absent", () => {
    const run = mkRun([{ provider: "a" }, { provider: "b" }]);
    const out = enrichRunWithConnectionCooldown(run, {
      a: { coolingDown: 0, total: 2, soonestRetryAfterMs: 0 },
      b: {},
    });
    assert.equal(out?.targets[0].cooldownCount, undefined);
    assert.equal(out?.targets[1].cooldownCount, undefined);
  });

  it("does not mutate the input run (pure)", () => {
    const run = mkRun([{ provider: "openai" }]);
    enrichRunWithConnectionCooldown(run, {
      openai: { coolingDown: 1, total: 1, soonestRetryAfterMs: 5000 },
    });
    assert.equal(run.targets[0].cooldownCount, undefined, "original run must be untouched");
  });

  it("strips a stale cooldown badge when the provider's connections recover", () => {
    const run = mkRun([
      { provider: "openai", cooldownCount: 2, cooldownTotal: 3, cooldownRetryAfterMs: 9000 },
    ]);
    const out = enrichRunWithConnectionCooldown(run, {
      openai: { coolingDown: 0, total: 3, soonestRetryAfterMs: 0 },
    });
    assert.equal(out?.targets[0].cooldownCount, undefined);
    assert.equal(out?.targets[0].cooldownTotal, undefined);
    assert.equal(out?.targets[0].cooldownRetryAfterMs, undefined);
  });

  it("comboRunToFlow carries the cooldown fields into the target node data", () => {
    const run = mkRun([{ provider: "anthropic" }, { provider: "openai" }]);
    const enriched = enrichRunWithConnectionCooldown(run, {
      anthropic: { coolingDown: 1, total: 2, soonestRetryAfterMs: 12_000 },
    });
    const { nodes } = comboRunToFlow(enriched as ComboRunModel);

    const target0 = nodes.find((n) => n.id === "target-0");
    assert.equal(target0?.data.cooldownCount, 1);
    assert.equal(target0?.data.cooldownTotal, 2);
    assert.equal(target0?.data.cooldownRetryAfterMs, 12_000);

    const target1 = nodes.find((n) => n.id === "target-1");
    assert.equal(target1?.data.cooldownCount, undefined, "healthy provider node has no cooldown");
  });

  it("composes with enrichRunWithBreakers without clobbering cbState", async () => {
    const { enrichRunWithBreakers } =
      await import("../../../src/app/(dashboard)/dashboard/combos/live/comboFlowModel.ts");
    const run = mkRun([{ provider: "anthropic" }]);
    const withBreaker = enrichRunWithBreakers(run, {
      anthropic: { state: "OPEN", retryAfterMs: 41_000 },
    });
    const both = enrichRunWithConnectionCooldown(withBreaker, {
      anthropic: { coolingDown: 1, total: 2, soonestRetryAfterMs: 5000 },
    });
    assert.equal(both?.targets[0].cbState, "OPEN", "breaker overlay survives the cooldown overlay");
    assert.equal(both?.targets[0].cooldownCount, 1);
  });
});