@yaebal/test 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +333 -160
  2. package/lib/api.d.ts +83 -0
  3. package/lib/api.d.ts.map +1 -0
  4. package/lib/api.js +186 -0
  5. package/lib/api.js.map +1 -0
  6. package/lib/api.test.d.ts +2 -0
  7. package/lib/api.test.d.ts.map +1 -0
  8. package/lib/api.test.js +131 -0
  9. package/lib/api.test.js.map +1 -0
  10. package/lib/bot-messages.d.ts +42 -0
  11. package/lib/bot-messages.d.ts.map +1 -0
  12. package/lib/bot-messages.js +72 -0
  13. package/lib/bot-messages.js.map +1 -0
  14. package/lib/chat-actor.d.ts +45 -0
  15. package/lib/chat-actor.d.ts.map +1 -0
  16. package/lib/chat-actor.js +72 -0
  17. package/lib/chat-actor.js.map +1 -0
  18. package/lib/clock.d.ts +22 -0
  19. package/lib/clock.d.ts.map +1 -0
  20. package/lib/clock.js +72 -0
  21. package/lib/clock.js.map +1 -0
  22. package/lib/clock.test.d.ts +2 -0
  23. package/lib/clock.test.d.ts.map +1 -0
  24. package/lib/clock.test.js +69 -0
  25. package/lib/clock.test.js.map +1 -0
  26. package/lib/env.d.ts +100 -0
  27. package/lib/env.d.ts.map +1 -0
  28. package/lib/env.js +164 -0
  29. package/lib/env.js.map +1 -0
  30. package/lib/env.test.d.ts +2 -0
  31. package/lib/env.test.d.ts.map +1 -0
  32. package/lib/env.test.js +302 -0
  33. package/lib/env.test.js.map +1 -0
  34. package/lib/fetch.d.ts +3 -0
  35. package/lib/fetch.d.ts.map +1 -0
  36. package/lib/fetch.js +12 -0
  37. package/lib/fetch.js.map +1 -0
  38. package/lib/index.d.ts +19 -205
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +19 -391
  41. package/lib/index.js.map +1 -1
  42. package/lib/internal.d.ts +25 -0
  43. package/lib/internal.d.ts.map +1 -0
  44. package/lib/internal.js +19 -0
  45. package/lib/internal.js.map +1 -0
  46. package/lib/keyboard.d.ts +15 -0
  47. package/lib/keyboard.d.ts.map +1 -0
  48. package/lib/keyboard.js +25 -0
  49. package/lib/keyboard.js.map +1 -0
  50. package/lib/keyboard.test.d.ts +2 -0
  51. package/lib/keyboard.test.d.ts.map +1 -0
  52. package/lib/keyboard.test.js +31 -0
  53. package/lib/keyboard.test.js.map +1 -0
  54. package/lib/normalize.d.ts +10 -0
  55. package/lib/normalize.d.ts.map +1 -0
  56. package/lib/normalize.js +27 -0
  57. package/lib/normalize.js.map +1 -0
  58. package/lib/reactions.d.ts +3 -0
  59. package/lib/reactions.d.ts.map +1 -0
  60. package/lib/reactions.js +17 -0
  61. package/lib/reactions.js.map +1 -0
  62. package/lib/updates.d.ts +126 -0
  63. package/lib/updates.d.ts.map +1 -0
  64. package/lib/updates.js +200 -0
  65. package/lib/updates.js.map +1 -0
  66. package/lib/updates.test.d.ts +2 -0
  67. package/lib/updates.test.d.ts.map +1 -0
  68. package/lib/updates.test.js +72 -0
  69. package/lib/updates.test.js.map +1 -0
  70. package/lib/user-actor.d.ts +188 -0
  71. package/lib/user-actor.d.ts.map +1 -0
  72. package/lib/user-actor.js +465 -0
  73. package/lib/user-actor.js.map +1 -0
  74. package/lib/webhook.d.ts +18 -0
  75. package/lib/webhook.d.ts.map +1 -0
  76. package/lib/webhook.js +25 -0
  77. package/lib/webhook.js.map +1 -0
  78. package/lib/webhook.test.d.ts +2 -0
  79. package/lib/webhook.test.d.ts.map +1 -0
  80. package/lib/webhook.test.js +36 -0
  81. package/lib/webhook.test.js.map +1 -0
  82. package/package.json +7 -5
  83. package/src/api.test.ts +180 -0
  84. package/src/api.ts +289 -0
  85. package/src/bot-messages.ts +117 -0
  86. package/src/chat-actor.ts +101 -0
  87. package/src/clock.test.ts +80 -0
  88. package/src/clock.ts +118 -0
  89. package/src/env.test.ts +370 -0
  90. package/src/env.ts +235 -0
  91. package/src/fetch.ts +11 -0
  92. package/src/index.ts +79 -630
  93. package/src/internal.ts +43 -0
  94. package/src/keyboard.test.ts +35 -0
  95. package/src/keyboard.ts +38 -0
  96. package/src/normalize.ts +34 -0
  97. package/src/reactions.ts +18 -0
  98. package/src/updates.test.ts +107 -0
  99. package/src/updates.ts +354 -0
  100. package/src/user-actor.ts +702 -0
  101. package/src/webhook.test.ts +54 -0
  102. package/src/webhook.ts +48 -0
  103. package/lib/index.test.d.ts +0 -2
  104. package/lib/index.test.d.ts.map +0 -1
  105. package/lib/index.test.js +0 -213
  106. package/lib/index.test.js.map +0 -1
  107. package/src/index.test.ts +0 -320
package/lib/api.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { type AfterHook, type Api, type BeforeHook, type ErrorHook, TelegramError } from "@yaebal/core";
2
+ /** a single recorded api call: the method, the params it was given, and how it resolved. */
3
+ export interface RecordedCall {
4
+ method: string;
5
+ params: Record<string, unknown> | undefined;
6
+ /** the resolved result, if the call succeeded. */
7
+ result?: unknown;
8
+ /** the thrown error, if the call failed (and no `onError` hook rescued it). */
9
+ error?: unknown;
10
+ /** ms since epoch when the call was made — honors an installed {@link TestClock}. */
11
+ at: number;
12
+ }
13
+ /** a canned result for one method: a static value, an {@link ApiErrorSentinel}/`Error`, or a function of `(params, attempt)`. */
14
+ export type MockResult = unknown | Error | ApiErrorSentinel | ((params: Record<string, unknown> | undefined, attempt: number) => unknown);
15
+ /** the shape `apiError(...)` returns — a sentinel telling the mock to throw a {@link TestApiError}. */
16
+ export interface ApiErrorSentinel {
17
+ readonly __yaebalApiError: true;
18
+ code: number;
19
+ description: string;
20
+ parameters?: Record<string, unknown>;
21
+ }
22
+ /**
23
+ * simulate a real Telegram Bot API error response — the bot sees exactly what it would see
24
+ * from the real api: a thrown {@link TestApiError} (a {@link TelegramError} subclass).
25
+ *
26
+ * @example
27
+ * env.onApi("sendMessage", apiError(403, "Forbidden: bot was blocked by the user"));
28
+ * env.onApi("sendMessage", apiError(429, "Too Many Requests", { retry_after: 30 }));
29
+ */
30
+ export declare function apiError(code: number, description: string, parameters?: Record<string, unknown>): ApiErrorSentinel;
31
+ /** typeguard for a stored `apiError(...)` sentinel. */
32
+ export declare function isApiErrorSentinel(value: unknown): value is ApiErrorSentinel;
33
+ /** a {@link TelegramError} carrying the optional `parameters` bag (e.g. `retry_after`) real errors ship. */
34
+ export declare class TestApiError extends TelegramError {
35
+ readonly parameters?: Record<string, unknown>;
36
+ constructor(method: string, code: number, description: string, parameters?: Record<string, unknown>);
37
+ }
38
+ /** options accepted by `{ times }` overrides: one-shot (or N-shot) replies. */
39
+ export interface OnApiOptions {
40
+ /** consume this override for exactly `times` calls, then fall back to the next queued/permanent reply. default: permanent (never expires). */
41
+ times?: number;
42
+ }
43
+ export interface MockApiOptions {
44
+ /** per-method canned results/errors, keyed by method name — seeds the permanent override, same as calling `onApi(method, result)` for each entry. */
45
+ results?: Record<string, MockResult>;
46
+ /** throw instead of falling back to `{}` when a method has no builtin default and no override registered. catches "forgot to stub this" in tests that care. default `false`. */
47
+ strictApi?: boolean;
48
+ /** clock to timestamp recorded calls with; defaults to the real `Date.now`. */
49
+ now?: () => number;
50
+ }
51
+ /** result of {@link mockApi}: the fake `api`, its recorded calls, and inspection/stubbing helpers. */
52
+ export interface MockApi {
53
+ api: Api;
54
+ calls: RecordedCall[];
55
+ /** hooks registered on `api` via `before`/`after`/`onError` — inspect them or invoke them yourself. */
56
+ hooks: {
57
+ before: BeforeHook[];
58
+ after: AfterHook[];
59
+ onError: ErrorHook[];
60
+ };
61
+ /** the most recent recorded call, optionally filtered to a method. */
62
+ lastCall(method?: string): RecordedCall | undefined;
63
+ /** every recorded call to a given method, in call order. */
64
+ callsTo(method: string): RecordedCall[];
65
+ /** override a method's reply. permanent unless `opts.times` is given (then it's consumed after `times` calls and the previous permanent, if any, resumes). */
66
+ onApi(method: string, reply: MockResult, opts?: OnApiOptions): void;
67
+ /** drop a method's overrides (or every method's, if none given) — reverts to the builtin default. */
68
+ offApi(method?: string): void;
69
+ /** clear recorded calls and per-method attempt counters. keeps hooks and overrides. */
70
+ reset(): void;
71
+ }
72
+ /**
73
+ * a fake {@link Api} whose every method records `{ method, params, result | error, at }` into
74
+ * `calls` and resolves to a sensible default (auto-incrementing `message_id` for `send*`, `true`
75
+ * for `answerCallbackQuery`, `{}` otherwise) — or to whatever `onApi`/`options.results` says.
76
+ * `before`/`after`/`onError` are real hook registrars (not no-ops): register a hook the same way
77
+ * you would on the production `Api` and it actually runs, including retries requested by an
78
+ * `onError` hook. the mock never actually waits on a requested `delayMs` — retries settle
79
+ * instantly, so tests stay fast (pair with {@link installTestClock} if the code under test
80
+ * schedules the retry itself via `setTimeout`).
81
+ */
82
+ export declare function mockApi(options?: MockApiOptions): MockApi;
83
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/lib/api.js ADDED
@@ -0,0 +1,186 @@
1
+ import { TelegramError, } from "@yaebal/core";
2
+ import { normalizeParams } from "./normalize.js";
3
+ /**
4
+ * simulate a real Telegram Bot API error response — the bot sees exactly what it would see
5
+ * from the real api: a thrown {@link TestApiError} (a {@link TelegramError} subclass).
6
+ *
7
+ * @example
8
+ * env.onApi("sendMessage", apiError(403, "Forbidden: bot was blocked by the user"));
9
+ * env.onApi("sendMessage", apiError(429, "Too Many Requests", { retry_after: 30 }));
10
+ */
11
+ export function apiError(code, description, parameters) {
12
+ return { __yaebalApiError: true, code, description, parameters };
13
+ }
14
+ /** typeguard for a stored `apiError(...)` sentinel. */
15
+ export function isApiErrorSentinel(value) {
16
+ return typeof value === "object" && value !== null && "__yaebalApiError" in value;
17
+ }
18
+ /** a {@link TelegramError} carrying the optional `parameters` bag (e.g. `retry_after`) real errors ship. */
19
+ export class TestApiError extends TelegramError {
20
+ parameters;
21
+ constructor(method, code, description, parameters) {
22
+ super(method, code, description);
23
+ this.parameters = parameters;
24
+ }
25
+ }
26
+ /** default results for known methods; everything else resolves to `{}` (or throws, with `strictApi`). */
27
+ const STUBBED_METHODS = new Set(["getMe", "answerCallbackQuery"]);
28
+ function builtinResult(method, nextMessageId) {
29
+ if (method.startsWith("send") || method === "copyMessage" || method === "forwardMessage") {
30
+ return { message_id: nextMessageId() };
31
+ }
32
+ if (method === "answerCallbackQuery")
33
+ return true;
34
+ if (method === "getMe")
35
+ return { id: 1, is_bot: true, first_name: "bot", username: "bot" };
36
+ return {};
37
+ }
38
+ function hasBuiltin(method) {
39
+ return (method.startsWith("send") ||
40
+ method === "copyMessage" ||
41
+ method === "forwardMessage" ||
42
+ STUBBED_METHODS.has(method));
43
+ }
44
+ /**
45
+ * a fake {@link Api} whose every method records `{ method, params, result | error, at }` into
46
+ * `calls` and resolves to a sensible default (auto-incrementing `message_id` for `send*`, `true`
47
+ * for `answerCallbackQuery`, `{}` otherwise) — or to whatever `onApi`/`options.results` says.
48
+ * `before`/`after`/`onError` are real hook registrars (not no-ops): register a hook the same way
49
+ * you would on the production `Api` and it actually runs, including retries requested by an
50
+ * `onError` hook. the mock never actually waits on a requested `delayMs` — retries settle
51
+ * instantly, so tests stay fast (pair with {@link installTestClock} if the code under test
52
+ * schedules the retry itself via `setTimeout`).
53
+ */
54
+ export function mockApi(options = {}) {
55
+ const calls = [];
56
+ const overrides = new Map();
57
+ const attempts = new Map();
58
+ const now = options.now ?? (() => Date.now());
59
+ let nextMessageId = 1;
60
+ for (const [method, reply] of Object.entries(options.results ?? {})) {
61
+ overrides.set(method, { queue: [], permanent: reply });
62
+ }
63
+ const hooks = {
64
+ before: [],
65
+ after: [],
66
+ onError: [],
67
+ };
68
+ function resolveReply(method, params) {
69
+ const attempt = (attempts.get(method) ?? 0) + 1;
70
+ attempts.set(method, attempt);
71
+ const override = overrides.get(method);
72
+ let reply;
73
+ if (override?.queue.length) {
74
+ const head = override.queue[0];
75
+ head.remaining--;
76
+ reply = head.reply;
77
+ if (head.remaining <= 0)
78
+ override.queue.shift();
79
+ }
80
+ else if (override && "permanent" in override) {
81
+ reply = override.permanent;
82
+ }
83
+ else if (options.strictApi && !hasBuiltin(method)) {
84
+ throw new Error(`mockApi: no stub for "${method}" — register one with onApi("${method}", ...), or disable strictApi`);
85
+ }
86
+ else {
87
+ return builtinResult(method, () => nextMessageId++);
88
+ }
89
+ if (typeof reply === "function") {
90
+ return reply(params, attempt);
91
+ }
92
+ if (isApiErrorSentinel(reply)) {
93
+ return new TestApiError(method, reply.code, reply.description, reply.parameters);
94
+ }
95
+ return reply;
96
+ }
97
+ const call = async (method, rawParams) => {
98
+ let p = normalizeParams(rawParams);
99
+ for (const hook of hooks.before) {
100
+ const next = await hook(method, p);
101
+ if (next !== undefined)
102
+ p = next;
103
+ }
104
+ for (let attempt = 1;; attempt++) {
105
+ let result;
106
+ try {
107
+ result = resolveReply(method, p);
108
+ if (result instanceof Error)
109
+ throw result;
110
+ }
111
+ catch (error) {
112
+ let retry;
113
+ for (const hook of hooks.onError) {
114
+ const action = await hook(method, error, attempt);
115
+ if (action?.retry) {
116
+ retry = action;
117
+ break;
118
+ }
119
+ }
120
+ calls.push({ method, params: p, error, at: now() });
121
+ if (!retry)
122
+ throw error;
123
+ continue; // the mock never actually waits on retry.delayMs
124
+ }
125
+ for (const hook of hooks.after) {
126
+ const next = await hook(method, p, result);
127
+ if (next !== undefined)
128
+ result = next;
129
+ }
130
+ calls.push({ method, params: p, result, at: now() });
131
+ return result;
132
+ }
133
+ };
134
+ const registrar = {
135
+ call: (method, params) => call(method, params),
136
+ fileUrl: (filePath) => `https://example.invalid/file/${filePath}`,
137
+ before(hook) {
138
+ hooks.before.push(hook);
139
+ return api;
140
+ },
141
+ after(hook) {
142
+ hooks.after.push(hook);
143
+ return api;
144
+ },
145
+ onError(hook) {
146
+ hooks.onError.push(hook);
147
+ return api;
148
+ },
149
+ };
150
+ const api = new Proxy(registrar, {
151
+ get(obj, prop) {
152
+ if (prop in obj)
153
+ return obj[prop];
154
+ const method = (params) => call(prop, params);
155
+ obj[prop] = method;
156
+ return method;
157
+ },
158
+ });
159
+ return {
160
+ api,
161
+ calls,
162
+ hooks,
163
+ lastCall: (method) => method ? [...calls].reverse().find((c) => c.method === method) : calls.at(-1),
164
+ callsTo: (method) => calls.filter((c) => c.method === method),
165
+ onApi: (method, reply, opts) => {
166
+ const entry = overrides.get(method) ?? { queue: [] };
167
+ if (opts?.times !== undefined)
168
+ entry.queue.push({ reply, remaining: opts.times });
169
+ else
170
+ entry.permanent = reply;
171
+ overrides.set(method, entry);
172
+ },
173
+ offApi: (method) => {
174
+ if (method)
175
+ overrides.delete(method);
176
+ else
177
+ overrides.clear();
178
+ },
179
+ reset: () => {
180
+ calls.length = 0;
181
+ attempts.clear();
182
+ nextMessageId = 1;
183
+ },
184
+ };
185
+ }
186
+ //# sourceMappingURL=api.js.map
package/lib/api.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=api.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.test.d.ts","sourceRoot":"","sources":["../src/api.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,131 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { TelegramError } from "@yaebal/core";
4
+ import { apiError, isApiErrorSentinel, mockApi, TestApiError } from "./api.js";
5
+ test("mockApi records calls and resolves sensible defaults", async () => {
6
+ const { api, calls } = mockApi();
7
+ const sent = await api.sendMessage({ chat_id: 1, text: "hi" });
8
+ assert.deepEqual(sent, { message_id: 1 });
9
+ const answered = await api.answerCallbackQuery({ callback_query_id: "1" });
10
+ assert.equal(answered, true);
11
+ const viaCall = await api.call("setMyCommands", { commands: [] });
12
+ assert.deepEqual(viaCall, {});
13
+ assert.equal(calls.length, 3);
14
+ assert.equal(calls[0]?.method, "sendMessage");
15
+ assert.deepEqual(calls[0]?.params, { chat_id: 1, text: "hi" });
16
+ assert.deepEqual(calls[0]?.result, { message_id: 1 });
17
+ assert.equal(typeof calls[0]?.at, "number");
18
+ });
19
+ test("mockApi hook registrars are chainable", () => {
20
+ const { api } = mockApi();
21
+ assert.equal(api.before(() => undefined), api);
22
+ assert.equal(api.after((_m, _p, r) => r), api);
23
+ });
24
+ test("mockApi auto-increments message_id across send* calls", async () => {
25
+ const { api } = mockApi();
26
+ const first = await api.sendMessage({ chat_id: 1, text: "a" });
27
+ const second = await api.sendMessage({ chat_id: 1, text: "b" });
28
+ assert.deepEqual(first, { message_id: 1 });
29
+ assert.deepEqual(second, { message_id: 2 });
30
+ });
31
+ test("mockApi results: static override replaces the built-in default", async () => {
32
+ const { api } = mockApi({ results: { sendMessage: { message_id: 99 } } });
33
+ assert.deepEqual(await api.sendMessage({ chat_id: 1 }), { message_id: 99 });
34
+ });
35
+ test("mockApi results: an Error value makes the call throw", async () => {
36
+ const { api } = mockApi({
37
+ results: { sendMessage: new TelegramError("sendMessage", 400, "Bad Request") },
38
+ });
39
+ await assert.rejects(api.sendMessage({ chat_id: 1 }), TelegramError);
40
+ });
41
+ test("mockApi results: a function sees params and a running attempt count", async () => {
42
+ const { api } = mockApi({
43
+ results: {
44
+ sendMessage: (params, attempt) => attempt <= 2
45
+ ? new TelegramError("sendMessage", 429, "retry after 0")
46
+ : { message_id: 1, echo: params?.text },
47
+ },
48
+ });
49
+ api.onError((_m, _e, attempt) => (attempt <= 2 ? { retry: true } : undefined));
50
+ const result = await api.sendMessage({ chat_id: 1, text: "hi" });
51
+ assert.deepEqual(result, { message_id: 1, echo: "hi" });
52
+ });
53
+ test("onApi sets a permanent override; reset() clears calls and counters but keeps it", async () => {
54
+ const { api, calls, onApi, reset } = mockApi();
55
+ await api.sendMessage({ chat_id: 1 });
56
+ onApi("sendMessage", { message_id: 42 });
57
+ assert.deepEqual(await api.sendMessage({ chat_id: 1 }), { message_id: 42 });
58
+ assert.equal(calls.length, 2);
59
+ reset();
60
+ assert.equal(calls.length, 0);
61
+ assert.deepEqual(await api.sendMessage({ chat_id: 1 }), { message_id: 42 });
62
+ });
63
+ test("onApi with { times } is one-shot, then falls back to the previous permanent reply", async () => {
64
+ const { api, onApi } = mockApi({ results: { sendMessage: { message_id: 1 } } });
65
+ onApi("sendMessage", apiError(429, "Too Many Requests"), { times: 1 });
66
+ await assert.rejects(api.sendMessage({ chat_id: 1 }), TestApiError);
67
+ assert.deepEqual(await api.sendMessage({ chat_id: 1 }), { message_id: 1 });
68
+ });
69
+ test("onApi { times: 2 } is consumed over exactly two calls", async () => {
70
+ const { api, onApi } = mockApi();
71
+ onApi("getChat", { id: 1, type: "private", stale: true }, { times: 2 });
72
+ onApi("getChat", { id: 1, type: "private", stale: false });
73
+ assert.equal((await api.call("getChat")).stale, true);
74
+ assert.equal((await api.call("getChat")).stale, true);
75
+ assert.equal((await api.call("getChat")).stale, false);
76
+ });
77
+ test("offApi drops a method's override, or every override with no argument", async () => {
78
+ const { api, onApi, offApi } = mockApi();
79
+ onApi("sendMessage", { message_id: 99 });
80
+ offApi("sendMessage");
81
+ assert.deepEqual(await api.sendMessage({ chat_id: 1 }), { message_id: 1 });
82
+ onApi("getMe", { id: 7, is_bot: true, first_name: "x" });
83
+ offApi();
84
+ assert.deepEqual(await api.getMe(), { id: 1, is_bot: true, first_name: "bot", username: "bot" });
85
+ });
86
+ test("apiError() produces a sentinel that throws a TestApiError carrying parameters", async () => {
87
+ const sentinel = apiError(429, "Too Many Requests", { retry_after: 30 });
88
+ assert.ok(isApiErrorSentinel(sentinel));
89
+ const { api } = mockApi({ results: { sendMessage: sentinel } });
90
+ await assert.rejects(api.sendMessage({ chat_id: 1 }), (error) => {
91
+ assert.ok(error instanceof TestApiError);
92
+ assert.ok(error instanceof TelegramError);
93
+ assert.equal(error.code, 429);
94
+ assert.deepEqual(error.parameters, { retry_after: 30 });
95
+ return true;
96
+ });
97
+ });
98
+ test("strictApi throws for methods with no builtin default and no override", async () => {
99
+ const { api } = mockApi({ strictApi: true });
100
+ await assert.rejects(api.call("getChatMember"), /no stub for "getChatMember"/);
101
+ assert.deepEqual(await api.sendMessage({ chat_id: 1 }), { message_id: 1 });
102
+ });
103
+ test("mockApi.lastCall and callsTo filter recorded calls", async () => {
104
+ const { api, lastCall, callsTo } = mockApi();
105
+ await api.sendMessage({ chat_id: 1, text: "a" });
106
+ await api.answerCallbackQuery({ callback_query_id: "1" });
107
+ await api.sendMessage({ chat_id: 1, text: "b" });
108
+ assert.equal(lastCall()?.method, "sendMessage");
109
+ assert.equal(lastCall("answerCallbackQuery")?.method, "answerCallbackQuery");
110
+ assert.equal(lastCall("sendMessage")?.params?.text, "b");
111
+ assert.equal(callsTo("sendMessage").length, 2);
112
+ });
113
+ test("mockApi hooks actually run: before rewrites params, after rewrites result", async () => {
114
+ const { api } = mockApi();
115
+ api.before((_method, params) => ({ ...params, injected: true }));
116
+ api.after((_method, _params, result) => ({ ...result, tagged: true }));
117
+ const result = await api.sendMessage({ chat_id: 1 });
118
+ assert.deepEqual(result, { message_id: 1, tagged: true });
119
+ });
120
+ test("reply_markup builder instances are normalized to plain JSON on recorded calls", async () => {
121
+ const { api, calls } = mockApi();
122
+ await api.sendMessage({
123
+ chat_id: 1,
124
+ text: "hi",
125
+ reply_markup: { toJSON: () => ({ inline_keyboard: [[{ text: "ok", callback_data: "ok" }]] }) },
126
+ });
127
+ assert.deepEqual(calls[0]?.params?.reply_markup, {
128
+ inline_keyboard: [[{ text: "ok", callback_data: "ok" }]],
129
+ });
130
+ });
131
+ //# sourceMappingURL=api.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.test.js","sourceRoot":"","sources":["../src/api.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { Api, Chat, Message } from "@yaebal/core";
2
+ /** `Message`'s own `reply_markup` union — reused so a `BotMessage` structurally satisfies `Message`. */
3
+ type ReplyMarkup = NonNullable<Message["reply_markup"]>;
4
+ /**
5
+ * a live mirror of one message the bot sent — built from the outgoing `send*` params (so it's
6
+ * populated even against the builtin `{ message_id }` stub, no `onApi` override needed) and kept
7
+ * in sync in place as `editMessageText`/`editMessageCaption`/`editMessageReplyMarkup` calls land,
8
+ * so a reference captured before an edit still reflects it after.
9
+ */
10
+ export interface BotMessage {
11
+ message_id: number;
12
+ chat: {
13
+ id: number;
14
+ type: Chat["type"];
15
+ };
16
+ text?: string;
17
+ caption?: string;
18
+ reply_markup?: ReplyMarkup;
19
+ date: number;
20
+ }
21
+ export interface LastBotMessageQuery {
22
+ /** scope to a specific chat (an `{ id }`-shaped value — a `ChatActor` or `pmChat` both qualify). */
23
+ chat?: {
24
+ id: number;
25
+ };
26
+ /** only consider messages currently carrying a non-empty `inline_keyboard`. */
27
+ withReplyMarkup?: boolean;
28
+ /** arbitrary predicate over the call that produced (or last touched) the message. */
29
+ where?: (call: {
30
+ method: string;
31
+ params: Record<string, unknown> | undefined;
32
+ }) => boolean;
33
+ }
34
+ export interface BotMessageTracker {
35
+ lastBotMessage(query?: LastBotMessageQuery): BotMessage | undefined;
36
+ botMessage(chatId: number, messageId: number): BotMessage | undefined;
37
+ clear(): void;
38
+ }
39
+ /** install bot-message tracking on a mock `api` via its `after` hook. */
40
+ export declare function attachBotMessageTracking(api: Api, resolveChatType: (chatId: number) => Chat["type"]): BotMessageTracker;
41
+ export {};
42
+ //# sourceMappingURL=bot-messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-messages.d.ts","sourceRoot":"","sources":["../src/bot-messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvD,wGAAwG;AACxG,KAAK,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,mBAAmB;IACnC,oGAAoG;IACpG,IAAI,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACtB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qFAAqF;IACrF,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;KAAE,KAAK,OAAO,CAAC;CAC3F;AAED,MAAM,WAAW,iBAAiB;IACjC,cAAc,CAAC,KAAK,CAAC,EAAE,mBAAmB,GAAG,UAAU,GAAG,SAAS,CAAC;IACpE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IACtE,KAAK,IAAI,IAAI,CAAC;CACd;AAWD,yEAAyE;AACzE,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,GAAG,EACR,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,GAC/C,iBAAiB,CAoEnB"}
@@ -0,0 +1,72 @@
1
+ const SEND_LIKE = (method) => method.startsWith("send") || method === "forwardMessage" || method === "copyMessage";
2
+ function hasInlineKeyboard(markup) {
3
+ const withRows = markup;
4
+ const rows = withRows?.inline_keyboard;
5
+ return Array.isArray(rows) && rows.length > 0;
6
+ }
7
+ /** install bot-message tracking on a mock `api` via its `after` hook. */
8
+ export function attachBotMessageTracking(api, resolveChatType) {
9
+ const byKey = new Map();
10
+ const order = [];
11
+ const key = (chatId, messageId) => `${chatId}:${messageId}`;
12
+ api.after((method, params, result) => {
13
+ const p = params ?? {};
14
+ if (method.startsWith("edit")) {
15
+ const chatId = p.chat_id;
16
+ const messageId = p.message_id;
17
+ if (chatId === undefined || messageId === undefined)
18
+ return;
19
+ const entry = byKey.get(key(chatId, messageId));
20
+ if (!entry)
21
+ return;
22
+ if (method === "editMessageText" && typeof p.text === "string")
23
+ entry.text = p.text;
24
+ if (method === "editMessageCaption")
25
+ entry.caption = p.caption;
26
+ if ("reply_markup" in p)
27
+ entry.reply_markup = p.reply_markup;
28
+ entry.lastCall = { method, params: p };
29
+ return;
30
+ }
31
+ if (!SEND_LIKE(method))
32
+ return;
33
+ if (!result || typeof result !== "object" || !("message_id" in result))
34
+ return;
35
+ const chatId = p.chat_id;
36
+ if (chatId === undefined)
37
+ return;
38
+ const messageId = result.message_id;
39
+ const entry = {
40
+ message_id: messageId,
41
+ chat: { id: chatId, type: resolveChatType(chatId) },
42
+ text: p.text,
43
+ caption: p.caption,
44
+ reply_markup: p.reply_markup,
45
+ date: result.date ?? Date.now(),
46
+ lastCall: { method, params: p },
47
+ };
48
+ byKey.set(key(chatId, messageId), entry);
49
+ order.push(entry);
50
+ });
51
+ return {
52
+ lastBotMessage(query = {}) {
53
+ for (let i = order.length - 1; i >= 0; i--) {
54
+ const entry = order[i];
55
+ if (query.chat && entry.chat.id !== query.chat.id)
56
+ continue;
57
+ if (query.withReplyMarkup && !hasInlineKeyboard(entry.reply_markup))
58
+ continue;
59
+ if (query.where && !query.where(entry.lastCall))
60
+ continue;
61
+ return entry;
62
+ }
63
+ return undefined;
64
+ },
65
+ botMessage: (chatId, messageId) => byKey.get(key(chatId, messageId)),
66
+ clear() {
67
+ byKey.clear();
68
+ order.length = 0;
69
+ },
70
+ };
71
+ }
72
+ //# sourceMappingURL=bot-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-messages.js","sourceRoot":"","sources":["../src/bot-messages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { Chat, Message } from "@yaebal/core";
2
+ import type { ActorHost, SendText } from "./internal.js";
3
+ import type { UserActor } from "./user-actor.js";
4
+ export type ChatType = "private" | "group" | "supergroup" | "channel";
5
+ export interface CreateChatOptions {
6
+ type: ChatType;
7
+ id?: number;
8
+ title?: string;
9
+ username?: string;
10
+ }
11
+ /** a chat member's tracked status — set via {@link ChatActor.setMembership}. */
12
+ export interface ChatMembership {
13
+ status: string;
14
+ since?: number;
15
+ }
16
+ /**
17
+ * a passive container actors send into — a group/supergroup/channel/private chat. tracks who's
18
+ * currently a member (best-effort — nothing enforces it unless you opt into `strictMembership`
19
+ * via {@link createTestEnv}) and every message dispatched through it.
20
+ */
21
+ export declare class ChatActor {
22
+ readonly id: number;
23
+ readonly type: ChatType;
24
+ readonly title?: string;
25
+ readonly username?: string;
26
+ readonly members: Set<UserActor>;
27
+ readonly messages: unknown[];
28
+ private readonly host;
29
+ private readonly membership;
30
+ constructor(host: ActorHost, options: CreateChatOptions);
31
+ /** reconstruct a `ChatActor` from a raw `Chat` payload seen on an update (e.g. to find the chat a message you didn't create arrived in). not registered with any `TestEnv`. */
32
+ static fromChat(host: ActorHost, chat: Chat): ChatActor;
33
+ /** this chat as a plain {@link Chat} payload, ready to embed in an update. */
34
+ toChat(): Chat;
35
+ /** record (or update) a member's status — used by `strictMembership` and `getChatMember`-style assertions. */
36
+ setMembership(userId: number, membership: ChatMembership): void;
37
+ /** the tracked membership for a user, or `undefined` if never set. */
38
+ membershipOf(user: UserActor): ChatMembership | undefined;
39
+ /**
40
+ * an anonymous channel post — `update.channel_post`, with no `from` (matching real Telegram:
41
+ * channel posts aren't attributed to a user). throws on non-channel chats.
42
+ */
43
+ post(text: SendText): Promise<Message>;
44
+ }
45
+ //# sourceMappingURL=chat-actor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-actor.d.ts","sourceRoot":"","sources":["../src/chat-actor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;;;GAIG;AACH,qBAAa,SAAS;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,iBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAM;IAElC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqC;gBAEpD,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB;IAQvD,+KAA+K;IAC/K,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,GAAG,SAAS;IASvD,8EAA8E;IAC9E,MAAM,IAAI,IAAI;IASd,8GAA8G;IAC9G,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,GAAG,IAAI;IAI/D,sEAAsE;IACtE,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,cAAc,GAAG,SAAS;IAIzD;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CAmB5C"}