Skip to content

Commit 14ed8d9

Browse files
test(backend): cover dry-run preview for admin indexer reset/replay
The dryRun=true preview paths in admin.routes.ts were implemented without tests. Add six cases: preview responses for reset and replay, non-true dryRun values falling through to the real mutation, and preview failures returning 500 without mutating state.
1 parent 625155e commit 14ed8d9

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

backend/tests/integration/admin-metrics.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ vi.mock('../../src/services/indexerService.js', () => ({
9090
getIndexerStatus: vi.fn().mockResolvedValue({}),
9191
resetIndexer: vi.fn().mockResolvedValue(undefined),
9292
replayFromLedger: vi.fn().mockResolvedValue(undefined),
93+
previewReset: vi.fn().mockResolvedValue({}),
94+
previewReplay: vi.fn().mockResolvedValue({}),
9395
}));
9496

9597
vi.mock('../../src/workers/soroban-event-worker.js', () => ({
@@ -113,6 +115,8 @@ import {
113115
getIndexerStatus,
114116
resetIndexer,
115117
replayFromLedger,
118+
previewReset,
119+
previewReplay,
116120
} from '../../src/services/indexerService.js';
117121

118122
// ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -520,6 +524,50 @@ describe('POST /v1/admin/indexer/reset', () => {
520524
expect(res.status).toBe(500);
521525
expect(res.body).toEqual({ error: 'Reset failed' });
522526
});
527+
528+
it('returns a dry-run preview without calling resetIndexer when dryRun=true', async () => {
529+
const preview = {
530+
currentLastLedger: 1000,
531+
currentLastCursor: 'cursor_xyz',
532+
targetLastLedger: 500,
533+
};
534+
vi.mocked(previewReset).mockResolvedValueOnce(preview);
535+
536+
const res = await request(app)
537+
.post('/v1/admin/indexer/reset?dryRun=true')
538+
.set('Authorization', `Bearer ${createToken()}`)
539+
.send({ ledger: 500 });
540+
541+
expect(res.status).toBe(200);
542+
expect(res.body).toEqual({ dryRun: true, preview });
543+
expect(previewReset).toHaveBeenCalledWith(500);
544+
expect(resetIndexer).not.toHaveBeenCalled();
545+
});
546+
547+
it('treats non-true dryRun values as a real reset', async () => {
548+
const res = await request(app)
549+
.post('/v1/admin/indexer/reset?dryRun=false')
550+
.set('Authorization', `Bearer ${createToken()}`)
551+
.send({ ledger: 300 });
552+
553+
expect(res.status).toBe(200);
554+
expect(res.body).toEqual({ ok: true, lastLedger: 300 });
555+
expect(resetIndexer).toHaveBeenCalledWith(300);
556+
expect(previewReset).not.toHaveBeenCalled();
557+
});
558+
559+
it('returns 500 when previewReset throws for a dry-run request', async () => {
560+
vi.mocked(previewReset).mockRejectedValueOnce(new Error('Preview failed'));
561+
562+
const res = await request(app)
563+
.post('/v1/admin/indexer/reset?dryRun=true')
564+
.set('Authorization', `Bearer ${createToken()}`)
565+
.send({ ledger: 500 });
566+
567+
expect(res.status).toBe(500);
568+
expect(res.body).toEqual({ error: 'Reset failed' });
569+
expect(resetIndexer).not.toHaveBeenCalled();
570+
});
523571
});
524572

525573
describe('POST /v1/admin/indexer/replay', () => {
@@ -587,5 +635,49 @@ describe('POST /v1/admin/indexer/replay', () => {
587635
expect(res.status).toBe(500);
588636
expect(res.body).toEqual({ error: 'Replay failed' });
589637
});
638+
639+
it('returns a dry-run preview without triggering a replay when dryRun=true', async () => {
640+
const preview = {
641+
fromLedger: 200,
642+
currentLastLedger: 1000,
643+
currentLastCursor: 'cursor_xyz',
644+
eventCount: 42,
645+
minLedgerInReplayRange: 210,
646+
maxLedgerInReplayRange: 999,
647+
};
648+
vi.mocked(previewReplay).mockResolvedValueOnce(preview);
649+
650+
const res = await request(app)
651+
.post('/v1/admin/indexer/replay?from_ledger=200&dryRun=true')
652+
.set('Authorization', `Bearer ${createToken()}`);
653+
654+
expect(res.status).toBe(200);
655+
expect(res.body).toEqual({ dryRun: true, preview });
656+
expect(previewReplay).toHaveBeenCalledWith(200);
657+
expect(replayFromLedger).not.toHaveBeenCalled();
658+
});
659+
660+
it('treats non-true dryRun values as a real replay', async () => {
661+
const res = await request(app)
662+
.post('/v1/admin/indexer/replay?from_ledger=200&dryRun=false')
663+
.set('Authorization', `Bearer ${createToken()}`);
664+
665+
expect(res.status).toBe(202);
666+
expect(res.body).toMatchObject({ ok: true, replayingFrom: 200 });
667+
expect(replayFromLedger).toHaveBeenCalledWith(200);
668+
expect(previewReplay).not.toHaveBeenCalled();
669+
});
670+
671+
it('returns 500 when previewReplay throws for a dry-run request', async () => {
672+
vi.mocked(previewReplay).mockRejectedValueOnce(new Error('Preview failed'));
673+
674+
const res = await request(app)
675+
.post('/v1/admin/indexer/replay?from_ledger=200&dryRun=true')
676+
.set('Authorization', `Bearer ${createToken()}`);
677+
678+
expect(res.status).toBe(500);
679+
expect(res.body).toEqual({ error: 'Replay failed' });
680+
expect(replayFromLedger).not.toHaveBeenCalled();
681+
});
590682
});
591683

0 commit comments

Comments
 (0)