Daily Bluesky bot for AT Mot. Invites players and congratulates yesterday's solvers.
1import { describe, it, expect } from 'vitest';
2import { alreadyPosted, type BotSession } from '../src/post.js';
3import { postMarker } from '../src/compose.js';
4
5/** A fake session whose listRecords returns `texts` (or fails when `ok` is false). */
6function fakeSession(texts: string[], ok = true): BotSession {
7 return {
8 did: 'did:plc:bot',
9 client: {
10 get: async () => ({
11 ok,
12 data: { records: texts.map((text) => ({ value: { text } })) },
13 }),
14 },
15 } as unknown as BotSession;
16}
17
18describe('alreadyPosted', () => {
19 it('detects today’s marker in a recent post', async () => {
20 const marker = postMarker('en', 5); // "AT Mot #5"
21 const session = fakeSession([`🟩 ${marker} is live! ...`]);
22 expect(await alreadyPosted(session, marker)).toBe(true);
23 });
24
25 it('returns false when no recent post carries the marker', async () => {
26 const session = fakeSession(['🟩 AT Mot #4 is live! ...', 'unrelated post']);
27 expect(await alreadyPosted(session, postMarker('en', 5))).toBe(false);
28 });
29
30 it('does not cross-match EN and FR markers for the same puzzle', async () => {
31 const enSession = fakeSession([`🟩 ${postMarker('fr', 5)} est en ligne ! ...`]);
32 expect(await alreadyPosted(enSession, postMarker('en', 5))).toBe(false);
33
34 const frSession = fakeSession([`🟩 ${postMarker('en', 5)} is live! ...`]);
35 expect(await alreadyPosted(frSession, postMarker('fr', 5))).toBe(false);
36 });
37
38 it('allows the post (returns false) when the listRecords read fails', async () => {
39 const session = fakeSession([`🟩 ${postMarker('en', 5)} is live! ...`], false);
40 expect(await alreadyPosted(session, postMarker('en', 5))).toBe(false);
41 });
42});