SurfSense/surfsense_web/tests/unit/network-errors.test.ts
MODSetter b7ad5ecfff feat(models): drop catalogue models that cannot serve a turn
Passing OpenRouter's metadata filters does not prove a model can complete a
turn through this harness, so dead-but-advertised models kept reaching users
and failing mid-stream. A new model_compatibility table holds one verdict per
model, a weekly Celery sweep re-probes stale entries, and catalogue
generation reads the blocked ids back so those models are never offered.
Also excludes :batch variants, which advertise full chat metadata and then
reject completions outright.

Auto-pin now falls through to Auto when an explicitly selected global config
is absent from the current worker's catalogue. Returning it handed
load_llm_bundle an id it could not resolve, ending the turn with SERVER_ERROR
on one of the four uvicorn workers while the identical request succeeded on
the others. Catalogue refresh also re-materializes GLOBAL_MODELS, which
previously kept its boot snapshot until restart.

On the web side a single dropped connection surfaced as one toast per
in-flight query and dozens of PostHog exceptions, with no retry button.
Network errors now share a toast id, are deduped per endpoint before capture,
carry host and online context, and count as retryable.

Consolidates the model filter helpers duplicated between the model list and
OpenRouter services into openrouter_model_normalizer, renames OoxmlDefect to
OoxmlError, and includes a ruff format and import-order pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 18:59:11 +05:30

46 lines
1.9 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { shouldRetry } from "@/lib/auth-errors";
import { AppError, AuthenticationError, NetworkError, NotFoundError } from "@/lib/error";
import { shouldRetryQuery } from "@/lib/query-client/retry";
// Run with: pnpm exec tsx --test tests/unit/network-errors.test.ts
test("a network failure offers the user a retry", () => {
// register/page.tsx passes `err.message`, not `err.code`, so the retry
// affordance only appears if the sentence itself is recognised.
const network = new NetworkError(
"Unable to connect to the server. Check your internet connection and try again."
);
assert.equal(shouldRetry(network.message), true);
assert.equal(shouldRetry(network.code ?? ""), true);
});
test("a rejected password does not offer a retry", () => {
assert.equal(shouldRetry("REGISTER_USER_ALREADY_EXISTS"), false);
assert.equal(shouldRetry("Password should be at least 8 characters"), false);
});
test("queries retry transient failures", () => {
const network = new NetworkError("Unable to connect to the server.");
const serverFault = new AppError("Something went wrong", 500, "Internal Server Error");
assert.equal(shouldRetryQuery(0, network), true);
assert.equal(shouldRetryQuery(0, serverFault), true);
});
test("queries give up rather than hammering a failing endpoint", () => {
const network = new NetworkError("Unable to connect to the server.");
assert.equal(shouldRetryQuery(2, network), false);
});
test("queries never retry an answer the server meant", () => {
// Retrying these delays the redirect or the error the user needs to see,
// and cannot change the outcome.
assert.equal(shouldRetryQuery(0, new AuthenticationError("Please login again.", 401)), false);
assert.equal(shouldRetryQuery(0, new NotFoundError("Resource not found", 404)), false);
assert.equal(shouldRetryQuery(0, new AppError("Forbidden", 403)), false);
});