mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
fix(settings): name the real response-timeout defaults
The placeholder said "Default (~120)" while most surfaces fall back to 60 s and the briefing to 180 s. The placeholder and the help line now interpolate both numbers from the constants the server applies (PROVIDER_DEFAULT_TIMEOUT_MS, AI_BUDGETS.comprehensive.timeoutMs), in all seven locales, and a test fails if a locale writes a number of its own. Refs #1090
This commit is contained in:
1 parent
6207101006
commit
ae0bdd0390
9 files changed
+101
-16
No files matched your search
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* The response-timeout copy names the defaults the server applies.
|
||||
*
|
||||
* The placeholder said "Default (~120)" while most surfaces fell back to 60 s
|
||||
* and the briefing to 180 s. The numbers now come from the server constants
|
||||
* through interpolation; this pins that every locale interpolates them instead
|
||||
* of writing a number of its own, and that the rendered text carries them.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { AI_BUDGETS } from "@/lib/ai/ai-budgets";
|
||||
import { PROVIDER_DEFAULT_TIMEOUT_MS } from "@/lib/ai/effective-timeout";
|
||||
|
||||
import { RESPONSE_TIMEOUT_COPY_PARAMS } from "../response-timeout-card";
|
||||
|
||||
const LOCALES = ["de", "en", "es", "fr", "it", "ko", "pl"] as const;
|
||||
|
||||
function aiCopy(locale: string): Record<string, string> {
|
||||
const bundle = JSON.parse(
|
||||
readFileSync(join(process.cwd(), "messages", `${locale}.json`), "utf8"),
|
||||
) as { settings: { ai: Record<string, string> } };
|
||||
return bundle.settings.ai;
|
||||
}
|
||||
|
||||
function render(template: string): string {
|
||||
return template.replace(/\{(\w+)\}/g, (_, k: string) =>
|
||||
String(
|
||||
RESPONSE_TIMEOUT_COPY_PARAMS[
|
||||
k as keyof typeof RESPONSE_TIMEOUT_COPY_PARAMS
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
describe("response-timeout copy", () => {
|
||||
it("reads its numbers from the server constants", () => {
|
||||
expect(RESPONSE_TIMEOUT_COPY_PARAMS.seconds).toBe(
|
||||
PROVIDER_DEFAULT_TIMEOUT_MS / 1000,
|
||||
);
|
||||
expect(RESPONSE_TIMEOUT_COPY_PARAMS.briefingSeconds).toBe(
|
||||
AI_BUDGETS.comprehensive.timeoutMs! / 1000,
|
||||
);
|
||||
expect(RESPONSE_TIMEOUT_COPY_PARAMS.briefingSeconds).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it.each(LOCALES)(
|
||||
"%s interpolates the defaults and writes none itself",
|
||||
(locale) => {
|
||||
const copy = aiCopy(locale);
|
||||
const placeholder = copy.responseTimeoutPlaceholder;
|
||||
const body = copy.responseTimeoutBody;
|
||||
expect(placeholder).toContain("{seconds}");
|
||||
expect(body).toContain("{seconds}");
|
||||
expect(body).toContain("{briefingSeconds}");
|
||||
// No literal number outside the placeholders: a hardcoded one would drift.
|
||||
expect(placeholder.replace(/\{\w+\}/g, "")).not.toMatch(/\d/);
|
||||
expect(body.replace(/\{\w+\}/g, "")).not.toMatch(/\d/);
|
||||
|
||||
expect(render(placeholder)).toContain(
|
||||
String(PROVIDER_DEFAULT_TIMEOUT_MS / 1000),
|
||||
);
|
||||
expect(render(body)).toContain(
|
||||
String(AI_BUDGETS.comprehensive.timeoutMs! / 1000),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -18,12 +18,24 @@ import { Loader2, Save } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { AI_BUDGETS } from "@/lib/ai/ai-budgets";
|
||||
import { PROVIDER_DEFAULT_TIMEOUT_MS } from "@/lib/ai/effective-timeout";
|
||||
import { apiPatch } from "@/lib/api/api-fetch";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { aiInputDependentKeys, invalidateKeys } from "@/lib/query-keys";
|
||||
|
||||
import type { UserAIProvider } from "./shared";
|
||||
|
||||
/**
|
||||
* The defaults the copy names, read from the constants the server applies, so
|
||||
* the placeholder cannot drift from the real fallback again (it said "~120"
|
||||
* while most surfaces waited 60 s).
|
||||
*/
|
||||
export const RESPONSE_TIMEOUT_COPY_PARAMS = {
|
||||
seconds: PROVIDER_DEFAULT_TIMEOUT_MS / 1000,
|
||||
briefingSeconds: (AI_BUDGETS.comprehensive.timeoutMs ?? 0) / 1000,
|
||||
} as const;
|
||||
|
||||
export function ResponseTimeoutCard({
|
||||
userProvider,
|
||||
}: {
|
||||
@@ -98,7 +110,7 @@ export function ResponseTimeoutCard({
|
||||
{t("settings.ai.responseTimeoutHeading")}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.ai.responseTimeoutBody")}
|
||||
{t("settings.ai.responseTimeoutBody", RESPONSE_TIMEOUT_COPY_PARAMS)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@@ -113,7 +125,10 @@ export function ResponseTimeoutCard({
|
||||
max={600}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
placeholder={t("settings.ai.responseTimeoutPlaceholder")}
|
||||
placeholder={t(
|
||||
"settings.ai.responseTimeoutPlaceholder",
|
||||
RESPONSE_TIMEOUT_COPY_PARAMS,
|
||||
)}
|
||||
className="mt-1 sm:max-w-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user