Show per-account delivery status

This commit is contained in:
jjbrunton committed 2026-08-09 16:12:05 +01:00
1 parent 9b86cee9be
commit 22eb32e5bf
14 files changed
+402 -13

No files matched your search

+42
View File
@@ -13,6 +13,7 @@ import { createDashboardRoutes } from '../../apps/dashboard/src/app/router.js';
interface TestStatsOverrides {
isRunning?: boolean;
recentActivity?: readonly unknown[];
accounts?: readonly unknown[];
}
const server = setupServer();
@@ -30,6 +31,7 @@ function createStats(overrides: TestStatsOverrides = {}) {
subreddits: [{ name: 'casualconversation', type: 'chat' }],
targetGenders: ['female'],
recentActivity: overrides.recentActivity ?? [],
accounts: overrides.accounts ?? [],
invitationCooldown: null,
};
}
@@ -224,11 +226,51 @@ describe('dashboard foundation', () => {
expect(screen.getByText('6 messages remaining')).toBeInTheDocument();
expect(screen.getByText('No recent activity yet.')).toBeInTheDocument();
expect(screen.getByText('Dry run')).toBeInTheDocument();
expect(screen.getByText('No accounts configured.')).toBeInTheDocument();
expect(await screen.findByRole('list', { name: 'Recent sent messages' })).toHaveTextContent('A recent opener');
expect(screen.getByRole('list', { name: 'Recent sent messages' })).toHaveTextContent('Responded');
expect(screen.getByRole('link', { name: 'u/alice' })).toHaveAttribute('href', 'https://www.reddit.com/u/alice');
});
it('shows the live sending status and cooldown for every account', async () => {
const now = Date.now();
installSuccessfulApi({
isRunning: true,
accounts: [
{
accountId: '@t2_ready:reddit.com', redditUsername: 'ready-account', runtimeState: 'running',
authenticated: true, sendingState: 'ready', messagesThisHour: 2, maxMessagesPerHour: 8,
nextMessageAvailableAt: null, hourlyResetAt: now + 3_600_000, messagesRemaining: 6,
actionableReplies: 0, invitationCooldown: null, failure: null,
},
{
accountId: '@t2_limited:reddit.com', redditUsername: 'limited-account', runtimeState: 'running',
authenticated: true, sendingState: 'matrix-cooldown', messagesThisHour: 5, maxMessagesPerHour: 8,
nextMessageAvailableAt: now + 600_000, hourlyResetAt: now + 3_600_000, messagesRemaining: 3,
actionableReplies: 1,
invitationCooldown: {
until: now + 600_000,
reason: 'Matrix rate-limited: M_LIMIT_EXCEEDED',
redditCode: 'rate.invitation_limit_mature_account',
},
failure: null,
},
],
});
renderDashboard();
const panel = await screen.findByRole('region', { name: 'Account status' });
expect(within(panel).getByText('2 configured')).toBeInTheDocument();
expect(within(panel).getByRole('article', { name: 'Status for u/ready-account' })).toHaveTextContent('Ready');
const limited = within(panel).getByRole('article', { name: 'Status for u/limited-account' });
expect(limited).toHaveTextContent('Matrix cooldown');
expect(limited).toHaveTextContent('Matrix rate-limited: M_LIMIT_EXCEEDED');
expect(limited).toHaveTextContent('rate.invitation_limit_mature_account');
expect(limited).toHaveTextContent('5 of 8');
expect(limited).toHaveTextContent('Pending replies1');
});
it('keeps stats visible when timers fail and retries only the failed panel', async () => {
let timerRequests = 0;
server.use(
@@ -132,7 +132,12 @@ describe('OperationsPage', () => {
expect(new URL(request.url).searchParams.get('limit')).toBe('200');
return HttpResponse.json([
{ timestamp: Date.parse('2026-08-08T12:00:01Z'), level: 'info', message: 'Sync complete' },
{ timestamp: Date.parse('2026-08-08T12:00:02Z'), level: 'error', message: 'Matrix request failed' },
{
timestamp: Date.parse('2026-08-08T12:00:02Z'),
level: 'error',
message: 'Matrix request failed: M_LIMIT_EXCEEDED',
data: { accountId: '@t2_owner:reddit.com', redditUsername: 'operator' },
},
]);
}),
http.get('/api/chats/sync', () =>
@@ -171,10 +176,14 @@ describe('OperationsPage', () => {
const consoleRegion = screen.getByRole('region', { name: 'Live console' });
expect(consoleRegion).toHaveTextContent('Sync complete');
expect(consoleRegion).toHaveTextContent('Matrix request failed');
expect(consoleRegion).toHaveTextContent('Matrix request failed: M_LIMIT_EXCEEDED');
expect(within(consoleRegion).getByRole('link', { name: 'u/operator' })).toHaveAttribute(
'href',
'https://www.reddit.com/u/operator',
);
await user.selectOptions(within(consoleRegion).getByLabelText('Log level'), 'error');
expect(consoleRegion).not.toHaveTextContent('Sync complete');
expect(consoleRegion).toHaveTextContent('Matrix request failed');
expect(consoleRegion).toHaveTextContent('Matrix request failed: M_LIMIT_EXCEEDED');
await user.type(within(consoleRegion).getByLabelText('Search logs'), 'no match');
expect(consoleRegion).toHaveTextContent('No logs match the current filters.');
expect(within(consoleRegion).getByRole('button', { name: 'Pause console' })).toHaveAttribute('aria-pressed', 'false');