From c0f36d1e0c866eab8fcc3360e4f7b2df863a4a2c Mon Sep 17 00:00:00 2001 From: Securify001 Date: Tue, 30 Jun 2026 06:43:51 +0100 Subject: [PATCH] backend-stream --- .../migration.sql | 8 +++++ backend/prisma/schema.prisma | 2 +- backend/tests/integration/streams.test.ts | 11 +++++++ backend/tests/stream.controller.test.ts | 32 ++++++++++++++++++- backend/tests/user.controller.test.ts | 5 +++ 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 backend/prisma/migrations/20260630120000_add_stream_event_stream_id_timestamp_index/migration.sql diff --git a/backend/prisma/migrations/20260630120000_add_stream_event_stream_id_timestamp_index/migration.sql b/backend/prisma/migrations/20260630120000_add_stream_event_stream_id_timestamp_index/migration.sql new file mode 100644 index 00000000..ec2e12ec --- /dev/null +++ b/backend/prisma/migrations/20260630120000_add_stream_event_stream_id_timestamp_index/migration.sql @@ -0,0 +1,8 @@ +-- Replace the unused (streamId, createdAt) composite with (streamId, timestamp), +-- which matches streamId-scoped event listings that ORDER BY timestamp. + +-- CreateIndex +CREATE INDEX IF NOT EXISTS "StreamEvent_streamId_timestamp_idx" ON "StreamEvent"("streamId", "timestamp"); + +-- DropIndex +DROP INDEX IF EXISTS "StreamEvent_streamId_createdAt_idx"; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index bfece5c2..d7171ca2 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -84,6 +84,6 @@ model StreamEvent { @@index([timestamp]) @@index([transactionHash]) @@index([createdAt]) - @@index([streamId, createdAt]) + @@index([streamId, timestamp]) @@unique([transactionHash, eventType]) } diff --git a/backend/tests/integration/streams.test.ts b/backend/tests/integration/streams.test.ts index 0a8c82f1..c66e1e8b 100644 --- a/backend/tests/integration/streams.test.ts +++ b/backend/tests/integration/streams.test.ts @@ -231,6 +231,17 @@ describe('GET /v1/streams/:id/events — pagination and eventType filter', () => expect(res.body).toHaveProperty('hasMore'); expect(Array.isArray(res.body.data)).toBe(true); expect(res.body.hasMore).toBe(false); + + const callArgs = mockPrisma.streamEvent.findMany.mock.calls[0]![0] as { + where: { streamId: number }; + orderBy: { timestamp: string }; + take: number; + skip: number; + }; + expect(callArgs.where.streamId).toBe(1); + expect(callArgs.orderBy).toEqual({ timestamp: 'desc' }); + expect(callArgs.take).toBe(10); + expect(callArgs.skip).toBe(0); }); it('enforces default limit of 50', async () => { diff --git a/backend/tests/stream.controller.test.ts b/backend/tests/stream.controller.test.ts index ef37fe1b..405ce78b 100644 --- a/backend/tests/stream.controller.test.ts +++ b/backend/tests/stream.controller.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { createStream, listStreams, getStream, getStreamClaimableAmount, pauseStream, resumeStream } from '../src/controllers/stream.controller.js'; +import { createStream, listStreams, getStream, getStreamEvents, getStreamClaimableAmount, pauseStream, resumeStream } from '../src/controllers/stream.controller.js'; import { prisma } from '../src/lib/prisma.js'; import { claimableAmountService } from '../src/services/claimable.service.js'; import * as sorobanService from '../src/services/sorobanService.js'; @@ -16,6 +16,8 @@ vi.mock('../src/lib/prisma.js', () => ({ }, streamEvent: { create: vi.fn(), + findMany: vi.fn(), + count: vi.fn(), }, }, })); @@ -122,6 +124,34 @@ describe('Stream Controller', () => { expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ streamId: 123 })); + expect(prisma.stream.findUnique).toHaveBeenCalledWith( + expect.objectContaining({ + include: expect.objectContaining({ + events: { orderBy: { timestamp: 'desc' } }, + }), + }), + ); + }); + }); + + describe('getStreamEvents', () => { + it('should paginate stream events ordered by timestamp desc', async () => { + req.params = { streamId: '123' }; + req.query = { limit: '10', offset: '0' }; + (prisma.streamEvent.findMany as any).mockResolvedValue([]); + (prisma.streamEvent.count as any).mockResolvedValue(0); + + await getStreamEvents(req as Request, res as Response); + + expect(res.status).toHaveBeenCalledWith(200); + expect(prisma.streamEvent.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + where: { streamId: 123 }, + orderBy: { timestamp: 'desc' }, + take: 10, + skip: 0, + }), + ); }); }); diff --git a/backend/tests/user.controller.test.ts b/backend/tests/user.controller.test.ts index c2c6128b..b3c8e1b6 100644 --- a/backend/tests/user.controller.test.ts +++ b/backend/tests/user.controller.test.ts @@ -130,6 +130,11 @@ describe('User Controller', () => { await getUserEvents(req as Request, res as Response, next); expect(res.status).toHaveBeenCalledWith(200); + expect(prisma.streamEvent.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + orderBy: { timestamp: 'desc' }, + }), + ); expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ data: [], total: 0,