























@@ -379,7 +379,7 @@ describe("discordPlugin outbound", () => {
379379expect(runtimeProbeDiscord).not.toHaveBeenCalled();
380380});
381381382-it("uses direct Discord startup helpers before monitoring", async () => {
382+it("uses direct Discord startup helpers for async startup enrichment", async () => {
383383const runtimeProbeDiscord = vi.fn(async () => {
384384throw new Error("runtime Discord probe should not be used");
385385});
@@ -407,9 +407,11 @@ describe("discordPlugin outbound", () => {
407407const cfg = createCfg();
408408await startDiscordAccount(cfg);
409409410-expect(probeDiscordMock).toHaveBeenCalledWith("discord-token", 2500, {
411-includeApplication: true,
412-});
410+await vi.waitFor(() =>
411+expect(probeDiscordMock).toHaveBeenCalledWith("discord-token", 2500, {
412+includeApplication: true,
413+}),
414+);
413415expect(monitorDiscordProviderMock).toHaveBeenCalledWith(
414416expect.objectContaining({
415417token: "discord-token",
@@ -421,6 +423,98 @@ describe("discordPlugin outbound", () => {
421423expect(runtimeMonitorDiscordProvider).not.toHaveBeenCalled();
422424});
423425426+it("does not block Discord monitor startup on the startup probe", async () => {
427+let resolveProbe!: (value: {
428+ok: true;
429+bot: { username: string };
430+application: { intents: { messageContent: "limited" } };
431+elapsedMs: number;
432+}) => void;
433+probeDiscordMock.mockReturnValue(
434+new Promise((resolve) => {
435+resolveProbe = resolve;
436+}),
437+);
438+monitorDiscordProviderMock.mockResolvedValue(undefined);
439+440+const cfg = createCfg();
441+const statusPatches: Array<Record<string, unknown>> = [];
442+const ctx = createStartAccountContext({
443+account: resolveAccount(cfg),
444+ cfg,
445+statusPatchSink: (next) => statusPatches.push({ ...next }),
446+});
447+448+await discordPlugin.gateway!.startAccount!(ctx);
449+450+expect(monitorDiscordProviderMock).toHaveBeenCalledWith(
451+expect.objectContaining({
452+token: "discord-token",
453+accountId: "default",
454+}),
455+);
456+await vi.waitFor(() =>
457+expect(probeDiscordMock).toHaveBeenCalledWith("discord-token", 2500, {
458+includeApplication: true,
459+}),
460+);
461+expect(statusPatches.some((patch) => "bot" in patch || "application" in patch)).toBe(false);
462+463+resolveProbe({
464+ok: true,
465+bot: { username: "AsyncBob" },
466+application: { intents: { messageContent: "limited" } },
467+elapsedMs: 1,
468+});
469+470+await vi.waitFor(() =>
471+expect(
472+statusPatches.some(
473+(patch) =>
474+(patch.bot as { username?: string } | undefined)?.username === "AsyncBob" &&
475+Boolean(patch.application),
476+),
477+).toBe(true),
478+);
479+});
480+481+it("clears stale Discord probe metadata when the async startup probe degrades", async () => {
482+probeDiscordMock.mockResolvedValue({
483+ok: false,
484+status: 401,
485+error: "getMe failed (401)",
486+elapsedMs: 1,
487+});
488+monitorDiscordProviderMock.mockResolvedValue(undefined);
489+490+const cfg = createCfg();
491+const statusPatches: Array<Record<string, unknown>> = [];
492+const ctx = createStartAccountContext({
493+account: resolveAccount(cfg),
494+ cfg,
495+statusPatchSink: (next) => statusPatches.push({ ...next }),
496+});
497+ctx.setStatus({
498+accountId: "default",
499+bot: { username: "OldBot" },
500+application: { intents: { messageContent: "enabled" } },
501+});
502+503+await discordPlugin.gateway!.startAccount!(ctx);
504+505+await vi.waitFor(() =>
506+expect(
507+statusPatches.some(
508+(patch) =>
509+"bot" in patch &&
510+"application" in patch &&
511+patch.bot === undefined &&
512+patch.application === undefined,
513+),
514+).toBe(true),
515+);
516+});
517+424518it("stagger starts later accounts in multi-bot setups", async () => {
425519probeDiscordMock.mockResolvedValue({
426520ok: true,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。