




















@@ -1,3 +1,4 @@
1+import { randomUUID } from "node:crypto";
12import os from "node:os";
23import path from "node:path";
34import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -280,7 +281,7 @@ describe("runWithModelFallback – probe logic", () => {
280281setLoggerOverride({
281282level: "trace",
282283consoleLevel: "silent",
283-file: path.join(os.tmpdir(), `openclaw-model-fallback-probe-${Date.now()}.log`),
284+file: path.join(os.tmpdir(), `openclaw-model-fallback-probe-${randomUUID()}.log`),
284285});
285286286287const run = vi.fn().mockResolvedValue("probed-ok");
@@ -410,19 +411,26 @@ describe("runWithModelFallback – probe logic", () => {
410411expectPrimaryProbeSuccess(result, run, "recovered");
411412});
412413413-it("attempts non-primary fallbacks during rate-limit cooldown after primary probe failure", async () => {
414-await expectProbeFailureFallsBack({
415-reason: "rate_limit",
414+it.each([
415+{
416+label: "rate-limit",
417+reason: "rate_limit" as const,
416418probeError: Object.assign(new Error("rate limited"), { status: 429 }),
417-});
418-});
419-420-it("attempts non-primary fallbacks during overloaded cooldown after primary probe failure", async () => {
421-await expectProbeFailureFallsBack({
422-reason: "overloaded",
419+},
420+{
421+label: "overloaded",
422+reason: "overloaded" as const,
423423probeError: Object.assign(new Error("service overloaded"), { status: 503 }),
424-});
425-});
424+},
425+])(
426+"attempts non-primary fallbacks during $label cooldown after primary probe failure",
427+async ({ reason, probeError }) => {
428+await expectProbeFailureFallsBack({
429+ reason,
430+ probeError,
431+});
432+},
433+);
426434427435it("keeps walking remaining fallbacks after an abort-wrapped RESOURCE_EXHAUSTED probe failure", async () => {
428436const cfg = makeCfg({
@@ -556,38 +564,22 @@ describe("runWithModelFallback – probe logic", () => {
556564expect(_probeThrottleInternals.lastProbeAttempt.has("key-0")).toBe(true);
557565});
558566559-it("handles non-finite soonest safely (treats as probe-worthy)", async () => {
560-const cfg = makeCfg();
561-562-// Return Infinity — should be treated as "probe" per the guard
563-mockedGetSoonestCooldownExpiry.mockReturnValue(Infinity);
564-565-const run = vi.fn().mockResolvedValue("ok-infinity");
566-567-const result = await runPrimaryCandidate(cfg, run);
568-expectPrimaryProbeSuccess(result, run, "ok-infinity");
569-});
570-571-it("handles NaN soonest safely (treats as probe-worthy)", async () => {
572-const cfg = makeCfg();
573-574-mockedGetSoonestCooldownExpiry.mockReturnValue(Number.NaN);
575-576-const run = vi.fn().mockResolvedValue("ok-nan");
577-578-const result = await runPrimaryCandidate(cfg, run);
579-expectPrimaryProbeSuccess(result, run, "ok-nan");
580-});
581-582-it("handles null soonest safely (treats as probe-worthy)", async () => {
567+it("handles missing or non-finite soonest safely (treats as probe-worthy)", async () => {
583568const cfg = makeCfg();
584569585-mockedGetSoonestCooldownExpiry.mockReturnValue(null);
570+for (const [label, soonest] of [
571+["infinity", Infinity],
572+["nan", Number.NaN],
573+["null", null],
574+] as const) {
575+_probeThrottleInternals.lastProbeAttempt.clear();
576+mockedGetSoonestCooldownExpiry.mockReturnValue(soonest);
586577587-const run = vi.fn().mockResolvedValue("ok-null");
578+ const run = vi.fn().mockResolvedValue(`ok-${label}`);
588579589-const result = await runPrimaryCandidate(cfg, run);
590-expectPrimaryProbeSuccess(result, run, "ok-null");
580+const result = await runPrimaryCandidate(cfg, run);
581+expectPrimaryProbeSuccess(result, run, `ok-${label}`);
582+}
591583});
592584593585it("single candidate skips with rate_limit and exhausts candidates", async () => {
@@ -700,8 +692,4 @@ describe("runWithModelFallback – probe logic", () => {
700692701693expectPrimaryProbeSuccess(result, run, "billing-probe-ok");
702694});
703-704-it("skips billing-cooldowned primary with fallbacks when far from cooldown expiry", async () => {
705-await expectPrimarySkippedAfterLongCooldown("billing");
706-});
707695});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。