test: align envelope timestamps with local tz

This commit is contained in:
Peter Steinberger
2026-01-22 04:49:34 +00:00
parent af42cb3ded
commit b60db040e2
4 changed files with 92 additions and 67 deletions

View File

@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js"; import { resetInboundDedupe } from "../auto-reply/reply/inbound-dedupe.js";
import { createTelegramBot } from "./bot.js"; import { createTelegramBot } from "./bot.js";
@@ -89,6 +89,7 @@ vi.mock("grammy", () => ({
const sequentializeMiddleware = vi.fn(); const sequentializeMiddleware = vi.fn();
const sequentializeSpy = vi.fn(() => sequentializeMiddleware); const sequentializeSpy = vi.fn(() => sequentializeMiddleware);
let _sequentializeKey: ((ctx: unknown) => string) | undefined; let _sequentializeKey: ((ctx: unknown) => string) | undefined;
let originalTz: string | undefined;
vi.mock("@grammyjs/runner", () => ({ vi.mock("@grammyjs/runner", () => ({
sequentialize: (keyFn: (ctx: unknown) => string) => { sequentialize: (keyFn: (ctx: unknown) => string) => {
_sequentializeKey = keyFn; _sequentializeKey = keyFn;
@@ -120,6 +121,8 @@ const getOnHandler = (event: string) => {
describe("createTelegramBot", () => { describe("createTelegramBot", () => {
beforeEach(() => { beforeEach(() => {
originalTz = process.env.TZ;
process.env.TZ = "UTC";
resetInboundDedupe(); resetInboundDedupe();
loadConfig.mockReturnValue({ loadConfig.mockReturnValue({
channels: { channels: {
@@ -138,6 +141,10 @@ describe("createTelegramBot", () => {
_sequentializeKey = undefined; _sequentializeKey = undefined;
}); });
afterEach(() => {
process.env.TZ = originalTz;
});
// groupPolicy tests // groupPolicy tests
it("accepts group messages when mentionPatterns match (without @botUsername)", async () => { it("accepts group messages when mentionPatterns match (without @botUsername)", async () => {
@@ -176,7 +183,9 @@ describe("createTelegramBot", () => {
expect(payload.WasMentioned).toBe(true); expect(payload.WasMentioned).toBe(true);
expect(payload.SenderName).toBe("Ada"); expect(payload.SenderName).toBe("Ada");
expect(payload.SenderId).toBe("9"); expect(payload.SenderId).toBe("9");
expect(payload.Body).toMatch(/^\[Telegram Test Group id:7 (\+\d+[smhd] )?2025-01-09T00:00Z\]/); expect(payload.Body).toMatch(
/^\[Telegram Test Group id:7 (\+\d+[smhd] )?2025-01-09 00:00 [^\]]+\]/,
);
}); });
it("keeps group envelope headers stable (sender identity is separate)", async () => { it("keeps group envelope headers stable (sender identity is separate)", async () => {
onSpy.mockReset(); onSpy.mockReset();
@@ -217,7 +226,7 @@ describe("createTelegramBot", () => {
expect(payload.SenderName).toBe("Ada Lovelace"); expect(payload.SenderName).toBe("Ada Lovelace");
expect(payload.SenderId).toBe("99"); expect(payload.SenderId).toBe("99");
expect(payload.SenderUsername).toBe("ada"); expect(payload.SenderUsername).toBe("ada");
expect(payload.Body).toMatch(/^\[Telegram Ops id:42 (\+\d+[smhd] )?2025-01-09T00:00Z\]/); expect(payload.Body).toMatch(/^\[Telegram Ops id:42 (\+\d+[smhd] )?2025-01-09 00:00 [^\]]+\]/);
}); });
it("reacts to mention-gated group messages when ackReaction is enabled", async () => { it("reacts to mention-gated group messages when ackReaction is enabled", async () => {
onSpy.mockReset(); onSpy.mockReset();

View File

@@ -329,7 +329,7 @@ describe("createTelegramBot", () => {
expect(replySpy).toHaveBeenCalledTimes(1); expect(replySpy).toHaveBeenCalledTimes(1);
const payload = replySpy.mock.calls[0][0]; const payload = replySpy.mock.calls[0][0];
expect(payload.Body).toMatch( expect(payload.Body).toMatch(
/^\[Telegram Ada Lovelace \(@ada_bot\) id:1234 (\+\d+[smhd] )?2025-01-09T00:00Z\]/, /^\[Telegram Ada Lovelace \(@ada_bot\) id:1234 (\+\d+[smhd] )?2025-01-09 01:00 [^\]]+\]/,
); );
expect(payload.Body).toContain("hello world"); expect(payload.Body).toContain("hello world");
} finally { } finally {

View File

@@ -451,7 +451,7 @@ describe("createTelegramBot", () => {
expect(replySpy).toHaveBeenCalledTimes(1); expect(replySpy).toHaveBeenCalledTimes(1);
const payload = replySpy.mock.calls[0][0]; const payload = replySpy.mock.calls[0][0];
expect(payload.Body).toMatch( expect(payload.Body).toMatch(
/^\[Telegram Ada Lovelace \(@ada_bot\) id:1234 (\+\d+[smhd] )?2025-01-09T00:00Z\]/, /^\[Telegram Ada Lovelace \(@ada_bot\) id:1234 (\+\d+[smhd] )?2025-01-09 01:00 [^\]]+\]/,
); );
expect(payload.Body).toContain("hello world"); expect(payload.Body).toContain("hello world");
} finally { } finally {
@@ -551,86 +551,102 @@ describe("createTelegramBot", () => {
}); });
it("accepts group messages when mentionPatterns match (without @botUsername)", async () => { it("accepts group messages when mentionPatterns match (without @botUsername)", async () => {
const originalTz = process.env.TZ;
process.env.TZ = "UTC";
onSpy.mockReset(); onSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>; const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ try {
identity: { name: "Bert" }, loadConfig.mockReturnValue({
messages: { groupChat: { mentionPatterns: ["\\bbert\\b"] } }, identity: { name: "Bert" },
channels: { messages: { groupChat: { mentionPatterns: ["\\bbert\\b"] } },
telegram: { channels: {
groupPolicy: "open", telegram: {
groups: { "*": { requireMention: true } }, groupPolicy: "open",
groups: { "*": { requireMention: true } },
},
}, },
}, });
});
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>; const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
await handler({ await handler({
message: { message: {
chat: { id: 7, type: "group", title: "Test Group" }, chat: { id: 7, type: "group", title: "Test Group" },
text: "bert: introduce yourself", text: "bert: introduce yourself",
date: 1736380800, date: 1736380800,
message_id: 1, message_id: 1,
from: { id: 9, first_name: "Ada" }, from: { id: 9, first_name: "Ada" },
}, },
me: { username: "clawdbot_bot" }, me: { username: "clawdbot_bot" },
getFile: async () => ({ download: async () => new Uint8Array() }), getFile: async () => ({ download: async () => new Uint8Array() }),
}); });
expect(replySpy).toHaveBeenCalledTimes(1); expect(replySpy).toHaveBeenCalledTimes(1);
const payload = replySpy.mock.calls[0][0]; const payload = replySpy.mock.calls[0][0];
expectInboundContextContract(payload); expectInboundContextContract(payload);
expect(payload.WasMentioned).toBe(true); expect(payload.WasMentioned).toBe(true);
expect(payload.Body).toMatch(/^\[Telegram Test Group id:7 (\+\d+[smhd] )?2025-01-09T00:00Z\]/); expect(payload.Body).toMatch(
expect(payload.SenderName).toBe("Ada"); /^\[Telegram Test Group id:7 (\+\d+[smhd] )?2025-01-09 00:00 [^\]]+\]/,
expect(payload.SenderId).toBe("9"); );
expect(payload.SenderName).toBe("Ada");
expect(payload.SenderId).toBe("9");
} finally {
process.env.TZ = originalTz;
}
}); });
it("includes sender identity in group envelope headers", async () => { it("includes sender identity in group envelope headers", async () => {
const originalTz = process.env.TZ;
process.env.TZ = "UTC";
onSpy.mockReset(); onSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>; const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
replySpy.mockReset(); replySpy.mockReset();
loadConfig.mockReturnValue({ try {
channels: { loadConfig.mockReturnValue({
telegram: { channels: {
groupPolicy: "open", telegram: {
groups: { "*": { requireMention: false } }, groupPolicy: "open",
groups: { "*": { requireMention: false } },
},
}, },
}, });
});
createTelegramBot({ token: "tok" }); createTelegramBot({ token: "tok" });
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>; const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
await handler({ await handler({
message: { message: {
chat: { id: 42, type: "group", title: "Ops" }, chat: { id: 42, type: "group", title: "Ops" },
text: "hello", text: "hello",
date: 1736380800, date: 1736380800,
message_id: 2, message_id: 2,
from: { from: {
id: 99, id: 99,
first_name: "Ada", first_name: "Ada",
last_name: "Lovelace", last_name: "Lovelace",
username: "ada", username: "ada",
},
}, },
}, me: { username: "clawdbot_bot" },
me: { username: "clawdbot_bot" }, getFile: async () => ({ download: async () => new Uint8Array() }),
getFile: async () => ({ download: async () => new Uint8Array() }), });
});
expect(replySpy).toHaveBeenCalledTimes(1); expect(replySpy).toHaveBeenCalledTimes(1);
const payload = replySpy.mock.calls[0][0]; const payload = replySpy.mock.calls[0][0];
expectInboundContextContract(payload); expectInboundContextContract(payload);
expect(payload.Body).toMatch(/^\[Telegram Ops id:42 (\+\d+[smhd] )?2025-01-09T00:00Z\]/); expect(payload.Body).toMatch(
expect(payload.SenderName).toBe("Ada Lovelace"); /^\[Telegram Ops id:42 (\+\d+[smhd] )?2025-01-09 00:00 [^\]]+\]/,
expect(payload.SenderId).toBe("99"); );
expect(payload.SenderUsername).toBe("ada"); expect(payload.SenderName).toBe("Ada Lovelace");
expect(payload.SenderId).toBe("99");
expect(payload.SenderUsername).toBe("ada");
} finally {
process.env.TZ = originalTz;
}
}); });
it("reacts to mention-gated group messages when ackReaction is enabled", async () => { it("reacts to mention-gated group messages when ackReaction is enabled", async () => {

View File

@@ -329,11 +329,11 @@ describe("web auto-reply", () => {
const firstArgs = resolver.mock.calls[0][0]; const firstArgs = resolver.mock.calls[0][0];
const secondArgs = resolver.mock.calls[1][0]; const secondArgs = resolver.mock.calls[1][0];
expect(firstArgs.Body).toMatch( expect(firstArgs.Body).toMatch(
/\[WhatsApp \+1 (\+\d+[smhd] )?2025-01-01T00:00Z\] \[clawdbot\] first/, /\[WhatsApp \+1 (\+\d+[smhd] )?2025-01-01 01:00 [^\]]+\] \[clawdbot\] first/,
); );
expect(firstArgs.Body).not.toContain("second"); expect(firstArgs.Body).not.toContain("second");
expect(secondArgs.Body).toMatch( expect(secondArgs.Body).toMatch(
/\[WhatsApp \+1 (\+\d+[smhd] )?2025-01-01T01:00Z\] \[clawdbot\] second/, /\[WhatsApp \+1 (\+\d+[smhd] )?2025-01-01 02:00 [^\]]+\] \[clawdbot\] second/,
); );
expect(secondArgs.Body).not.toContain("first"); expect(secondArgs.Body).not.toContain("first");