mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:20:43 +00:00
fix(mattermost): make replay retries explicit
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { createClaimableDedupe } from "openclaw/plugin-sdk/persistent-dedupe";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../runtime-api.js";
|
||||
import { resolveMattermostAccount } from "./accounts.js";
|
||||
import {
|
||||
evaluateMattermostMentionGate,
|
||||
MattermostRetryableInboundError,
|
||||
processMattermostReplayGuardedPost,
|
||||
resolveMattermostReactionChannelId,
|
||||
resolveMattermostEffectiveReplyToId,
|
||||
resolveMattermostReplyRootId,
|
||||
@@ -281,6 +284,100 @@ describe("resolveMattermostThreadSessionContext", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("processMattermostReplayGuardedPost", () => {
|
||||
it("skips duplicate message batches after a successful commit", async () => {
|
||||
const replayGuard = createClaimableDedupe({
|
||||
ttlMs: 10_000,
|
||||
memoryMaxSize: 100,
|
||||
});
|
||||
const handlePost = vi.fn(async () => undefined);
|
||||
|
||||
await expect(
|
||||
processMattermostReplayGuardedPost({
|
||||
replayGuard,
|
||||
accountId: "acct",
|
||||
messageIds: ["post-1"],
|
||||
handlePost,
|
||||
}),
|
||||
).resolves.toBe("processed");
|
||||
await expect(
|
||||
processMattermostReplayGuardedPost({
|
||||
replayGuard,
|
||||
accountId: "acct",
|
||||
messageIds: ["post-1"],
|
||||
handlePost,
|
||||
}),
|
||||
).resolves.toBe("duplicate");
|
||||
|
||||
expect(handlePost).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("releases claims for explicit retryable failures", async () => {
|
||||
const replayGuard = createClaimableDedupe({
|
||||
ttlMs: 10_000,
|
||||
memoryMaxSize: 100,
|
||||
});
|
||||
let attempts = 0;
|
||||
const handlePost = vi.fn(async () => {
|
||||
attempts += 1;
|
||||
if (attempts === 1) {
|
||||
throw new MattermostRetryableInboundError("retry me");
|
||||
}
|
||||
});
|
||||
|
||||
await expect(
|
||||
processMattermostReplayGuardedPost({
|
||||
replayGuard,
|
||||
accountId: "acct",
|
||||
messageIds: ["post-2"],
|
||||
handlePost,
|
||||
}),
|
||||
).rejects.toThrow("retry me");
|
||||
await expect(
|
||||
processMattermostReplayGuardedPost({
|
||||
replayGuard,
|
||||
accountId: "acct",
|
||||
messageIds: ["post-2"],
|
||||
handlePost,
|
||||
}),
|
||||
).resolves.toBe("processed");
|
||||
|
||||
expect(handlePost).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("keeps replay committed after a non-retryable failure", async () => {
|
||||
const replayGuard = createClaimableDedupe({
|
||||
ttlMs: 10_000,
|
||||
memoryMaxSize: 100,
|
||||
});
|
||||
const visibleSideEffect = vi.fn();
|
||||
const handlePost = vi.fn(async () => {
|
||||
visibleSideEffect();
|
||||
throw new Error("post-send failure");
|
||||
});
|
||||
|
||||
await expect(
|
||||
processMattermostReplayGuardedPost({
|
||||
replayGuard,
|
||||
accountId: "acct",
|
||||
messageIds: ["post-3"],
|
||||
handlePost,
|
||||
}),
|
||||
).rejects.toThrow("post-send failure");
|
||||
await expect(
|
||||
processMattermostReplayGuardedPost({
|
||||
replayGuard,
|
||||
accountId: "acct",
|
||||
messageIds: ["post-3"],
|
||||
handlePost,
|
||||
}),
|
||||
).resolves.toBe("duplicate");
|
||||
|
||||
expect(handlePost).toHaveBeenCalledTimes(1);
|
||||
expect(visibleSideEffect).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveMattermostReactionChannelId", () => {
|
||||
it("prefers broadcast channel_id when present", () => {
|
||||
expect(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user