Fork of upstream llblab/[email protected].0 (SHA afe09c5) repackaged as a drop-in extension for can1357/oh-my-pi (omp >= 16.5.2). Changes vs upstream: - Rename package: @llblab/pi-telegram -> @evandrodevbr/oh-my-pi-telegram - Bump version: 0.22.0 -> 0.22.0-evandro.1 - Update repository/homepage/bugs URLs to evandrodevbr - Add omp field in package.json (displayName + compatibleWith) - README: prepend 'Oh My Pi (omp) Compatibility' section with install commands, verified-components table, upstream lineage, and rationale - CHANGELOG: add '0.22.0-evandro.1' entry under Unreleased - docs/: rename @llblab/pi-telegram imports to @evandrodevbr/oh-my-pi-telegram - tests/public-api.test.ts: rename package import paths Gates re-run on the fork: - npm run typecheck -> tsc --noEmit (success) - npm test -> 1229 pass / 0 fail / 1 platform-only skip - npm run audit -> found 0 vulnerabilities - npm run pack:check -> evandrodevbr-oh-my-pi-telegram-0.22.0-evandro.1.tgz Verified: - omp install --link ./pi-telegram -> Linked OK - omp plugin list -> @evandrodevbr/[email protected].0-evandro.1 Upstream lineage: badlogic/pi-telegram -> llblab/pi-telegram (0.22.0) -> evandrodevbr/oh-my-pi-telegram (this fork)
183 lines
4.8 KiB
TypeScript
183 lines
4.8 KiB
TypeScript
/**
|
|
* Regression tests for Telegram message ownership helpers
|
|
* Covers live message-id to target/instance ownership used by multi-instance bus routing
|
|
*/
|
|
|
|
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
createTelegramBusMessageOwnershipRuntime,
|
|
createTelegramMessageOwnershipStore,
|
|
} from "../lib/ownership.ts";
|
|
import { createTelegramThreadTarget } from "../lib/target.ts";
|
|
|
|
test("Bus ownership runtime scopes follower authority to registration generation", () => {
|
|
let followers = [
|
|
{
|
|
instanceId: "follower-a",
|
|
connectedAtMs: 10,
|
|
registrationGeneration: "generation-a",
|
|
},
|
|
];
|
|
const runtime = createTelegramBusMessageOwnershipRuntime({
|
|
instanceId: "leader",
|
|
getProfileKey: () => "default",
|
|
listFollowers: () => followers,
|
|
});
|
|
runtime.recordRouted({
|
|
chatId: 7,
|
|
messageId: 9,
|
|
target: { chatId: 7, threadId: 11 },
|
|
instanceId: "follower-a",
|
|
});
|
|
|
|
assert.equal(
|
|
runtime.isOwnedByFollower({
|
|
chatId: 7,
|
|
messageId: 9,
|
|
follower: followers[0]!,
|
|
}),
|
|
true,
|
|
);
|
|
followers = [
|
|
{
|
|
instanceId: "follower-a",
|
|
connectedAtMs: 20,
|
|
registrationGeneration: "generation-b",
|
|
},
|
|
];
|
|
assert.equal(runtime.store.get(7, 9), undefined);
|
|
assert.equal(
|
|
runtime.isOwnedByFollower({
|
|
chatId: 7,
|
|
messageId: 9,
|
|
follower: followers[0]!,
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("Message ownership records default private targets", () => {
|
|
const store = createTelegramMessageOwnershipStore();
|
|
const record = store.record({
|
|
chatId: 7,
|
|
messageId: 9,
|
|
instanceId: "instance-a",
|
|
now: 100,
|
|
});
|
|
assert.deepEqual(record, {
|
|
chatId: 7,
|
|
messageId: 9,
|
|
target: { chatId: 7 },
|
|
instanceId: "instance-a",
|
|
createdAt: 100,
|
|
updatedAt: 100,
|
|
});
|
|
assert.deepEqual(store.get(7, 9), record);
|
|
});
|
|
|
|
test("Message ownership preserves createdAt when ownership is refreshed", () => {
|
|
const store = createTelegramMessageOwnershipStore();
|
|
store.record({
|
|
chatId: -1007,
|
|
messageId: 11,
|
|
target: createTelegramThreadTarget(-1007, 42),
|
|
instanceId: "instance-a",
|
|
now: 100,
|
|
});
|
|
const refreshed = store.record({
|
|
chatId: -1007,
|
|
messageId: 11,
|
|
target: createTelegramThreadTarget(-1007, 43),
|
|
instanceId: "instance-b",
|
|
now: 150,
|
|
});
|
|
assert.deepEqual(refreshed, {
|
|
chatId: -1007,
|
|
messageId: 11,
|
|
target: createTelegramThreadTarget(-1007, 43),
|
|
instanceId: "instance-b",
|
|
createdAt: 100,
|
|
updatedAt: 150,
|
|
});
|
|
});
|
|
|
|
test("Message ownership can forget a whole target", () => {
|
|
const store = createTelegramMessageOwnershipStore();
|
|
store.record({
|
|
chatId: -1007,
|
|
messageId: 1,
|
|
target: createTelegramThreadTarget(-1007, 42),
|
|
instanceId: "instance-a",
|
|
now: 1,
|
|
});
|
|
store.record({
|
|
chatId: -1007,
|
|
messageId: 2,
|
|
target: createTelegramThreadTarget(-1007, 42),
|
|
instanceId: "instance-a",
|
|
now: 2,
|
|
});
|
|
store.record({
|
|
chatId: -1007,
|
|
messageId: 3,
|
|
target: createTelegramThreadTarget(-1007, 43),
|
|
instanceId: "instance-b",
|
|
now: 3,
|
|
});
|
|
assert.equal(store.forgetTarget(createTelegramThreadTarget(-1007, 42)), 2);
|
|
assert.equal(store.get(-1007, 1), undefined);
|
|
assert.equal(store.get(-1007, 2), undefined);
|
|
assert.equal(store.get(-1007, 3)?.instanceId, "instance-b");
|
|
});
|
|
|
|
test("Message ownership isolates bot profiles and rejects stale follower generations", () => {
|
|
let profileKey = "work";
|
|
let liveGeneration = "follower-a:100";
|
|
const store = createTelegramMessageOwnershipStore({
|
|
getProfileKey: () => profileKey,
|
|
isOwnerGenerationLive: (record) =>
|
|
record.ownerGeneration === liveGeneration,
|
|
});
|
|
store.record({
|
|
chatId: 7,
|
|
messageId: 9,
|
|
instanceId: "follower-a",
|
|
ownerGeneration: "follower-a:100",
|
|
now: 100,
|
|
});
|
|
assert.equal(store.get(7, 9)?.instanceId, "follower-a");
|
|
|
|
profileKey = "personal";
|
|
assert.equal(store.get(7, 9), undefined);
|
|
store.record({
|
|
chatId: 7,
|
|
messageId: 9,
|
|
instanceId: "follower-b",
|
|
ownerGeneration: "follower-b:200",
|
|
now: 200,
|
|
});
|
|
liveGeneration = "follower-b:200";
|
|
assert.equal(store.get(7, 9)?.instanceId, "follower-b");
|
|
|
|
profileKey = "work";
|
|
liveGeneration = "follower-a:101";
|
|
assert.equal(store.get(7, 9), undefined);
|
|
assert.equal(store.entries().length, 2);
|
|
});
|
|
|
|
test("Message ownership prunes by age and record count", () => {
|
|
const store = createTelegramMessageOwnershipStore();
|
|
store.record({ chatId: 7, messageId: 1, instanceId: "a", now: 10 });
|
|
store.record({ chatId: 7, messageId: 2, instanceId: "a", now: 20 });
|
|
store.record({ chatId: 7, messageId: 3, instanceId: "a", now: 30 });
|
|
assert.equal(store.prune({ now: 40, maxAgeMs: 25 }), 1);
|
|
assert.equal(store.get(7, 1), undefined);
|
|
assert.equal(store.prune({ now: 40, maxRecords: 1 }), 1);
|
|
assert.deepEqual(
|
|
store.entries().map((record) => record.messageId),
|
|
[3],
|
|
);
|
|
});
|