






















@@ -32,30 +32,58 @@ function createSimpleTwitchConfig(overrides: Record<string, unknown>) {
3232};
3333}
343435+function expectSingleIssue(
36+issues: ReturnType<typeof collectTwitchStatusIssues>,
37+expected: ReturnType<typeof collectTwitchStatusIssues>[number],
38+): void {
39+expect(issues).toEqual([expected]);
40+}
41+42+function expectIssues(
43+issues: ReturnType<typeof collectTwitchStatusIssues>,
44+expected: ReturnType<typeof collectTwitchStatusIssues>,
45+): void {
46+expect(issues).toEqual(expected);
47+}
48+49+function neverConnectedIssue(): ReturnType<typeof collectTwitchStatusIssues>[number] {
50+return {
51+channel: "twitch",
52+accountId: "default",
53+kind: "runtime",
54+message: "Account has never connected successfully",
55+fix: "Start the Twitch gateway to begin receiving messages. Check logs for connection errors.",
56+};
57+}
58+3559describe("status", () => {
3660describe("collectTwitchStatusIssues", () => {
3761it("should detect unconfigured accounts", () => {
3862const snapshots: ChannelAccountSnapshot[] = [createSnapshot({ configured: false })];
39634064const issues = collectTwitchStatusIssues(snapshots);
416542-expect(issues.length).toBeGreaterThan(0);
43-expect(issues[0]?.kind).toBe("config");
44-expect(issues[0]?.message).toContain("not properly configured");
66+expectSingleIssue(issues, {
67+channel: "twitch",
68+accountId: "default",
69+kind: "config",
70+message: "Twitch account is not properly configured",
71+fix: "Add required fields: username, accessToken, and clientId to your account configuration",
72+});
4573});
46744775it("should detect disabled accounts", () => {
4876const snapshots: ChannelAccountSnapshot[] = [createSnapshot({ enabled: false })];
49775078const issues = collectTwitchStatusIssues(snapshots);
517952-expect(issues.length).toBeGreaterThan(0);
53-expect(issues).toContainEqual(
54-expect.objectContaining({
55- kind: "config",
56- message: "Twitch account is disabled",
57-}),
58-);
80+expectSingleIssue(issues, {
81+ channel: "twitch",
82+accountId: "default",
83+kind: "config",
84+message: "Twitch account is disabled",
85+fix: "Set enabled: true in your account configuration to enable this account",
86+});
5987});
60886189it("should detect missing clientId when account configured (simplified config)", () => {
@@ -68,12 +96,16 @@ describe("status", () => {
68966997const issues = collectTwitchStatusIssues(snapshots, () => mockCfg as never);
709871-expect(issues).toContainEqual(
72-expect.objectContaining({
99+expectIssues(issues, [
100+{
101+channel: "twitch",
102+accountId: "default",
73103kind: "config",
74104message: "Twitch client ID is required",
75-}),
76-);
105+fix: "Add clientId to your Twitch account configuration (from Twitch Developer Portal)",
106+},
107+neverConnectedIssue(),
108+]);
77109});
7811079111it("should warn about oauth: prefix in token (simplified config)", () => {
@@ -86,12 +118,16 @@ describe("status", () => {
8611887119const issues = collectTwitchStatusIssues(snapshots, () => mockCfg as never);
8812089-expect(issues).toContainEqual(
90-expect.objectContaining({
121+expectIssues(issues, [
122+{
123+channel: "twitch",
124+accountId: "default",
91125kind: "config",
92126message: "Token contains 'oauth:' prefix (will be stripped)",
93-}),
94-);
127+fix: "The 'oauth:' prefix is optional. You can use just the token value, or keep it as-is (it will be normalized automatically).",
128+},
129+neverConnectedIssue(),
130+]);
95131});
9613297133it("should detect clientSecret without refreshToken (simplified config)", () => {
@@ -106,12 +142,23 @@ describe("status", () => {
106142107143const issues = collectTwitchStatusIssues(snapshots, () => mockCfg as never);
108144109-expect(issues).toContainEqual(
110-expect.objectContaining({
145+expectIssues(issues, [
146+{
147+channel: "twitch",
148+accountId: "default",
149+kind: "config",
150+message: "Token contains 'oauth:' prefix (will be stripped)",
151+fix: "The 'oauth:' prefix is optional. You can use just the token value, or keep it as-is (it will be normalized automatically).",
152+},
153+{
154+channel: "twitch",
155+accountId: "default",
111156kind: "config",
112157message: "clientSecret provided without refreshToken",
113-}),
114-);
158+fix: "For automatic token refresh, provide both clientSecret and refreshToken. Otherwise, clientSecret is not needed.",
159+},
160+neverConnectedIssue(),
161+]);
115162});
116163117164it("should detect empty allowFrom array (simplified config)", () => {
@@ -125,12 +172,16 @@ describe("status", () => {
125172126173const issues = collectTwitchStatusIssues(snapshots, () => mockCfg as never);
127174128-expect(issues).toContainEqual(
129-expect.objectContaining({
175+expectIssues(issues, [
176+{
177+channel: "twitch",
178+accountId: "default",
130179kind: "config",
131180message: "allowFrom is configured but empty",
132-}),
133-);
181+fix: "Either add user IDs to allowFrom, remove the allowFrom field, or use allowedRoles instead.",
182+},
183+neverConnectedIssue(),
184+]);
134185});
135186136187it("should detect allowedRoles 'all' with allowFrom conflict (simplified config)", () => {
@@ -145,12 +196,16 @@ describe("status", () => {
145196146197const issues = collectTwitchStatusIssues(snapshots, () => mockCfg as never);
147198148-expect(issues).toContainEqual(
149-expect.objectContaining({
199+expectIssues(issues, [
200+{
201+channel: "twitch",
202+accountId: "default",
150203kind: "intent",
151204message: "allowedRoles is set to 'all' but allowFrom is also configured",
152-}),
153-);
205+fix: "When allowedRoles is 'all', the allowFrom list is not needed. Remove allowFrom or set allowedRoles to specific roles.",
206+},
207+neverConnectedIssue(),
208+]);
154209});
155210156211it("should detect runtime errors", () => {
@@ -160,12 +215,16 @@ describe("status", () => {
160215161216const issues = collectTwitchStatusIssues(snapshots);
162217163-expect(issues).toContainEqual(
164-expect.objectContaining({
218+expectIssues(issues, [
219+{
220+channel: "twitch",
221+accountId: "default",
165222kind: "runtime",
166223message: "Last error: Connection timeout",
167-}),
168-);
224+fix: "Check your token validity and network connection. Ensure the bot has the required OAuth scopes.",
225+},
226+neverConnectedIssue(),
227+]);
169228});
170229171230it("should detect accounts that never connected", () => {
@@ -179,12 +238,13 @@ describe("status", () => {
179238180239const issues = collectTwitchStatusIssues(snapshots);
181240182-expect(issues).toContainEqual(
183-expect.objectContaining({
184-kind: "runtime",
185-message: "Account has never connected successfully",
186-}),
187-);
241+expectSingleIssue(issues, {
242+channel: "twitch",
243+accountId: "default",
244+kind: "runtime",
245+message: "Account has never connected successfully",
246+fix: "Start the Twitch gateway to begin receiving messages. Check logs for connection errors.",
247+});
188248});
189249190250it("should detect long-running connections", () => {
@@ -199,12 +259,13 @@ describe("status", () => {
199259200260const issues = collectTwitchStatusIssues(snapshots);
201261202-expect(issues).toContainEqual(
203-expect.objectContaining({
204-kind: "runtime",
205-message: "Connection has been running for 8 days",
206-}),
207-);
262+expectSingleIssue(issues, {
263+channel: "twitch",
264+accountId: "default",
265+kind: "runtime",
266+message: "Connection has been running for 8 days",
267+fix: "Consider restarting the connection periodically to refresh the connection. Twitch tokens may expire after long periods.",
268+});
208269});
209270210271it("should handle empty snapshots array", () => {
@@ -225,13 +286,13 @@ describe("status", () => {
225286226287const issues = collectTwitchStatusIssues(snapshots);
227288228-expect(issues).toEqual([
229-expect.objectContaining({
230- accountId: "unknown",
231- kind: "config",
232- message: "Twitch account is not properly configured",
233-}),
234-]);
289+expectSingleIssue(issues, {
290+channel: "twitch",
291+accountId: "unknown",
292+kind: "config",
293+message: "Twitch account is not properly configured",
294+fix: "Add required fields: username, accessToken, and clientId to your account configuration",
295+});
235296});
236297});
237298});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。