1import { describe, expect, it } from "vite-plus/test";
2import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";
3import {
4 getProviderStatusBannerKey,
5 getProviderStatusMessage,
6 shouldShowProviderStatusBanner,
7} from "./ProviderStatusBanner";
8
9const provider: ServerProvider = {
10 instanceId: ProviderInstanceId.make("codex-work"),
11 driver: ProviderDriverKind.make("codex"),
12 enabled: true,
13 installed: true,
14 version: "1.0.0",
15 status: "ready",
16 auth: { status: "authenticated" },
17 checkedAt: "2026-09-22T00:00:00Z",
18 models: [],
19 skills: [],
20 slashCommands: [],
21 compatibilityAdvisory: {
22 status: "unsupported",
23 message: "Unsupported version. Use 2.0.0.",
24 recommendedVersion: "2.0.0",
25 recommendedRange: null,
26 },
27};
28
29describe("compatibility banners", () => {
30 it("shows and dismisses a warning on a healthy provider, then clears it after policy relaxation", () => {
31 expect(shouldShowProviderStatusBanner(provider, null)).toBe(true);
32 expect(shouldShowProviderStatusBanner(provider, getProviderStatusBannerKey(provider))).toBe(
33 false,
34 );
35 expect(
36 shouldShowProviderStatusBanner(
37 { ...provider, version: "1.0.1" },
38 getProviderStatusBannerKey(provider),
39 ),
40 ).toBe(true);
41 const relaxed: ServerProvider = {
42 ...provider,
43 compatibilityAdvisory: {
44 ...provider.compatibilityAdvisory!,
45 status: "supported",
46 message: null,
47 },
48 };
49 expect(getProviderStatusBannerKey(relaxed)).toBeNull();
50 expect(getProviderStatusBannerKey({ ...provider, status: "disabled" })).toBeNull();
51 expect(
52 getProviderStatusBannerKey({
53 ...provider,
54 compatibilityAdvisory: { ...provider.compatibilityAdvisory!, status: "graceful" },
55 }),
56 ).toBeNull();
57 });
58
59 it("shows downgrade guidance instead of a broken OpenCode inventory timeout", () => {
60 const message = "This provider version is known to be incompatible. Use 1.14.19.";
61 const broken: ServerProvider = {
62 ...provider,
63 driver: ProviderDriverKind.make("opencode"),
64 version: "2.0.3",
65 status: "error",
66 auth: { status: "unknown" },
67 message: "Failed to load OpenCode provider inventory: Timed out waiting for server start.",
68 compatibilityAdvisory: {
69 status: "broken",
70 message,
71 recommendedVersion: "1.14.19",
72 recommendedRange: ">=1.14.19 <2.0.0",
73 },
74 };
75 expect(shouldShowProviderStatusBanner(broken, null)).toBe(true);
76 const timeoutOnly: ServerProvider = {
77 ...broken,
78 compatibilityAdvisory: {
79 ...broken.compatibilityAdvisory!,
80 status: "supported",
81 message: null,
82 },
83 };
84 expect(shouldShowProviderStatusBanner(broken, getProviderStatusBannerKey(timeoutOnly))).toBe(
85 true,
86 );
87 expect(shouldShowProviderStatusBanner(broken, getProviderStatusBannerKey(broken))).toBe(false);
88 expect(
89 shouldShowProviderStatusBanner(
90 {
91 ...broken,
92 compatibilityAdvisory: { ...broken.compatibilityAdvisory!, message: "Use 1.14.20." },
93 },
94 getProviderStatusBannerKey(broken),
95 ),
96 ).toBe(true);
97 expect(getProviderStatusMessage(broken)).toBe(message);
98 expect(
99 getProviderStatusMessage({
100 ...broken,
101 compatibilityAdvisory: {
102 ...broken.compatibilityAdvisory!,
103 status: "supported",
104 message: null,
105 },
106 }),
107 ).toBe(broken.message);
108 expect(getProviderStatusMessage({ ...broken, auth: { status: "unauthenticated" } })).toBe(
109 broken.message,
110 );
111 });
112
113 it("keeps authentication failures ahead of compatibility warnings even without a probe message", () => {
114 const unauthenticated: ServerProvider = {
115 ...provider,
116 status: "error",
117 auth: { status: "unauthenticated" },
118 };
119 expect(getProviderStatusMessage(unauthenticated)).toBe(
120 "Sign in via the CLI to authenticate again.",
121 );
122 expect(getProviderStatusMessage({ ...unauthenticated, message: "Credentials expired" })).toBe(
123 "Credentials expired",
124 );
125 });
126});