Add account browsing simulation

This commit is contained in:
jjbrunton committed 2026-08-11 21:40:31 +01:00
1 parent e2d7dc4b82
commit 27012399ec
31 files changed
+1476 -28

No files matched your search

+35
View File
@@ -8,6 +8,7 @@ import {
} from '../../packages/api-contracts/src/index.js';
import { runMigrations } from '../../src/database/client.js';
import { AccountGrowthRepository } from '../../src/database/growth-repository.js';
import { AccountBrowsingSimulationRepository } from '../../src/database/browsing-simulation-repository.js';
import type { WebContext } from '../../src/web/context.js';
import { createGrowthRouter } from '../../src/web/routes/growth.js';
import type { RedditPost } from '../../src/reddit/api.js';
@@ -63,6 +64,7 @@ function thread(id: string): RedditPost {
describe('growth route', () => {
let db: Database.Database;
let repo: AccountGrowthRepository;
let browsingRepo: AccountBrowsingSimulationRepository;
let app: express.Express;
let pushed: Array<{ level: string; message: string }>;
@@ -77,12 +79,14 @@ describe('growth route', () => {
) VALUES (?, 'grower', ?, 'verified', 1, '[]', '{}', 0, 0)
`).run(ACCOUNT, ACCOUNT);
repo = new AccountGrowthRepository(db);
browsingRepo = new AccountBrowsingSimulationRepository(db);
pushed = [];
const ctx = {
broadcaster: { push: (level: string, message: string) => pushed.push({ level, message }) },
services: {
growth: repo,
browsing: browsingRepo,
redditAccounts: {
list: () => [{ accountId: ACCOUNT, redditUsername: 'grower' }],
},
@@ -246,6 +250,37 @@ describe('growth route', () => {
});
});
describe('PATCH /api/growth/:accountId/browsing', () => {
it('enables simulation within the conservative hard limits', async () => {
const { status, json } = await call(app, 'PATCH', `/api/growth/${ENCODED}/browsing`, {
enabled: true,
dailyTargetMin: 4,
dailyTargetMax: 8,
minimumGapMs: 3_600_000,
});
expect(status).toBe(200);
const parsed = GrowthAccountResponseSchema.parse(json);
expect(parsed.account.browsing).toMatchObject({
settings: { enabled: true, dailyTargetMin: 4, dailyTargetMax: 8 },
noVotesSubmitted: true,
});
expect(pushed).toContainEqual({
level: 'info', message: 'Browsing simulation enabled for u/grower',
});
});
it.each([
{ dailyTargetMax: 13 },
{ minimumGapMs: 44 * 60_000 },
{ postWeight: 0, commentWeight: 0 },
{ nextDueAt: 123 },
])('rejects unsafe or non-operator settings %o', async (body) => {
const { status } = await call(app, 'PATCH', `/api/growth/${ENCODED}/browsing`, body);
expect(status).toBe(400);
});
});
describe('pause and resume', () => {
it('pauses with a default reason and resumes', async () => {
const paused = await call(app, 'POST', `/api/growth/${ENCODED}/pause`, {});