From 5b9c2f4c468b0ad58885c6c31095966ee841d676 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 28 May 2026 22:08:49 +0500 Subject: [PATCH] asdasd --- client/src/app/providers/SseProvider.tsx | 1 + .../providers/__tests__/SseProvider.test.tsx | 3 + server/prisma/prisma/dev.db | Bin 356352 -> 364544 bytes server/src/routes/__tests__/sse.test.js | 45 +++++++++ .../src/routes/__tests__/user-orders.test.js | 88 ++++++++++++++++++ server/src/routes/sse.js | 14 ++- server/src/routes/user-orders.js | 6 ++ 7 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 server/src/routes/__tests__/user-orders.test.js diff --git a/client/src/app/providers/SseProvider.tsx b/client/src/app/providers/SseProvider.tsx index 0d16a73..5b24b46 100644 --- a/client/src/app/providers/SseProvider.tsx +++ b/client/src/app/providers/SseProvider.tsx @@ -23,6 +23,7 @@ export function SseProvider() { function invalidateOrderQueries(orderId: unknown) { if (!orderId) return + queryClient.invalidateQueries({ queryKey: ['me', 'orders'] }) queryClient.invalidateQueries({ queryKey: ['me', 'orders', orderId] }) queryClient.invalidateQueries({ queryKey: ['admin', 'orders', 'detail', orderId] }) queryClient.invalidateQueries({ queryKey: ['admin', 'orders'] }) diff --git a/client/src/app/providers/__tests__/SseProvider.test.tsx b/client/src/app/providers/__tests__/SseProvider.test.tsx index 3da6488..7bc3ba8 100644 --- a/client/src/app/providers/__tests__/SseProvider.test.tsx +++ b/client/src/app/providers/__tests__/SseProvider.test.tsx @@ -107,6 +107,7 @@ describe('SseProvider', () => { expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'messages', 'unread-count'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'conversations'] }) + expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders', 'o1'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'detail', 'o1'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders'] }) @@ -118,6 +119,7 @@ describe('SseProvider', () => { renderSse() const handler = mockEventHandlers['order:statusChanged'] handler(new MessageEvent('order:statusChanged', { data: JSON.stringify({ orderId: 'o2' }) })) + expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders', 'o2'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'detail', 'o2'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders'] }) @@ -129,6 +131,7 @@ describe('SseProvider', () => { renderSse() const handler = mockEventHandlers['order:updated'] handler(new MessageEvent('order:updated', { data: JSON.stringify({ orderId: 'o3' }) })) + expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['me', 'orders', 'o3'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders', 'detail', 'o3'] }) expect(mockInvalidateQueries).toHaveBeenCalledWith({ queryKey: ['admin', 'orders'] }) diff --git a/server/prisma/prisma/dev.db b/server/prisma/prisma/dev.db index d3ff4da77e4006dbc640c4e36fd2d904e988cc65..86fb7d22ecf87309485bd79bff23e41e17f0f1f1 100644 GIT binary patch delta 9057 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 diff --git a/server/src/routes/__tests__/sse.test.js b/server/src/routes/__tests__/sse.test.js index da79a2e..df2da65 100644 --- a/server/src/routes/__tests__/sse.test.js +++ b/server/src/routes/__tests__/sse.test.js @@ -39,6 +39,19 @@ describe('isAdminUser', () => { expect(isAdminUser(null)).toBe(false) expect(isAdminUser(undefined)).toBe(false) }) + + it('normalizes email before comparing with ADMIN_EMAIL', () => { + const previousAdminEmail = process.env.ADMIN_EMAIL + process.env.ADMIN_EMAIL = 'Admin@Test.com' + + expect(isAdminUser({ email: ' admin@test.com ' })).toBe(true) + + if (previousAdminEmail === undefined) { + delete process.env.ADMIN_EMAIL + } else { + process.env.ADMIN_EMAIL = previousAdminEmail + } + }) }) describe('buildSseListeners', () => { @@ -96,6 +109,20 @@ describe('buildSseListeners', () => { cleanup() }) + it('forwards order:statusChanged to admin', () => { + const cleanup = buildSseListeners('admin-1', true, eventBus, write) + eventBus.emit('order:statusChanged', { + orderId: 'o1', + userId: 'user-1', + oldStatus: 'READY_FOR_PICKUP', + newStatus: 'DONE', + }) + expect(write).toHaveBeenCalledTimes(1) + expect(write.mock.calls[0][0]).toContain('event: order:statusChanged') + expect(write.mock.calls[0][0]).toContain('"orderId":"o1"') + cleanup() + }) + it('forwards payment:statusChanged to matching userId', () => { const cleanup = buildSseListeners('user-1', false, eventBus, write) eventBus.emit('payment:statusChanged', { orderId: 'o1', userId: 'user-1', paymentStatus: 'paid' }) @@ -104,6 +131,15 @@ describe('buildSseListeners', () => { cleanup() }) + it('forwards payment:statusChanged to admin', () => { + const cleanup = buildSseListeners('admin-1', true, eventBus, write) + eventBus.emit('payment:statusChanged', { orderId: 'o1', userId: 'user-1', paymentStatus: 'paid' }) + expect(write).toHaveBeenCalledTimes(1) + expect(write.mock.calls[0][0]).toContain('event: order:statusChanged') + expect(write.mock.calls[0][0]).toContain('"orderId":"o1"') + cleanup() + }) + it('forwards order:deliveryFeeAdjusted to matching userId', () => { const cleanup = buildSseListeners('user-1', false, eventBus, write) eventBus.emit('order:deliveryFeeAdjusted', { orderId: 'o1', userId: 'user-1', totalCents: 50000 }) @@ -112,6 +148,15 @@ describe('buildSseListeners', () => { cleanup() }) + it('forwards order:deliveryFeeAdjusted to admin', () => { + const cleanup = buildSseListeners('admin-1', true, eventBus, write) + eventBus.emit('order:deliveryFeeAdjusted', { orderId: 'o1', userId: 'user-1', totalCents: 50000 }) + expect(write).toHaveBeenCalledTimes(1) + expect(write.mock.calls[0][0]).toContain('event: order:updated') + expect(write.mock.calls[0][0]).toContain('"orderId":"o1"') + cleanup() + }) + it('forwards order:created to admin', () => { const cleanup = buildSseListeners('admin-1', true, eventBus, write) eventBus.emit('order:created', { orderId: 'o1', userId: 'user-1', totalCents: 50000 }) diff --git a/server/src/routes/__tests__/user-orders.test.js b/server/src/routes/__tests__/user-orders.test.js new file mode 100644 index 0000000..c03d62d --- /dev/null +++ b/server/src/routes/__tests__/user-orders.test.js @@ -0,0 +1,88 @@ +import { randomUUID } from 'node:crypto' +import jwt from '@fastify/jwt' +import Fastify from 'fastify' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { NOTIFICATION_EVENTS } from '../../../../shared/constants/notification-events.js' +import { prisma } from '../../lib/prisma.js' +import { registerUserOrderRoutes } from '../user-orders.js' + +const JWT_SECRET = 'test-secret' + +let app +let testUser +let testUserEmail + +async function buildApp() { + const fastify = Fastify({ logger: false }) + await fastify.register(jwt, { secret: JWT_SECRET }) + fastify.decorate('authenticate', async function (request, reply) { + try { + await request.jwtVerify() + } catch { + return reply.code(401).send({ error: 'Unauthorized' }) + } + }) + fastify.decorate('eventBus', { emit: vi.fn() }) + await registerUserOrderRoutes(fastify) + await fastify.ready() + return fastify +} + +async function signToken(user) { + return app.jwt.sign({ sub: user.id, email: user.email }) +} + +async function createOrder(data = {}) { + return prisma.order.create({ + data: { + userId: testUser.id, + status: 'SHIPPED', + deliveryType: 'delivery', + deliveryFeeLocked: true, + paymentMethod: 'online', + itemsSubtotalCents: 10000, + deliveryFeeCents: 50000, + totalCents: 60000, + currency: 'RUB', + ...data, + }, + }) +} + +describe('user order routes', () => { + beforeEach(async () => { + testUserEmail = `user-orders-${randomUUID()}@example.com` + testUser = await prisma.user.create({ data: { email: testUserEmail } }) + app = await buildApp() + }) + + afterEach(async () => { + await app?.close() + if (testUser?.id) { + await prisma.order.deleteMany({ where: { userId: testUser.id } }) + await prisma.user.deleteMany({ where: { id: testUser.id } }) + } else if (testUserEmail) { + await prisma.user.deleteMany({ where: { email: testUserEmail } }) + } + vi.clearAllMocks() + }) + + it('emits order status event when user confirms receiving an order', async () => { + const order = await createOrder() + const token = await signToken(testUser) + + const res = await app.inject({ + method: 'POST', + url: `/api/me/orders/${order.id}/confirm-received`, + headers: { authorization: `Bearer ${token}` }, + }) + + expect(res.statusCode).toBe(200) + expect(app.eventBus.emit).toHaveBeenCalledWith(NOTIFICATION_EVENTS.ORDER_STATUS_CHANGED, { + orderId: order.id, + userId: testUser.id, + oldStatus: 'SHIPPED', + newStatus: 'DONE', + }) + }) +}) diff --git a/server/src/routes/sse.js b/server/src/routes/sse.js index 33eb514..15980fe 100644 --- a/server/src/routes/sse.js +++ b/server/src/routes/sse.js @@ -10,7 +10,13 @@ const { } = NOTIFICATION_EVENTS export function isAdminUser(user) { - return !!(process.env.ADMIN_EMAIL && user?.email === process.env.ADMIN_EMAIL) + const adminEmail = String(process.env.ADMIN_EMAIL || '') + .trim() + .toLowerCase() + const userEmail = String(user?.email || '') + .trim() + .toLowerCase() + return !!(adminEmail && userEmail === adminEmail) } export function formatSSE(event, data) { @@ -53,21 +59,21 @@ export function buildSseListeners(userId, admin, eventBus, write) { on( ORDER_STATUS_CHANGED, - (p) => p.userId === userId, + (p) => admin || p.userId === userId, 'order:statusChanged', (p) => ({ orderId: p.orderId, newStatus: p.newStatus }), ) on( PAYMENT_STATUS_CHANGED, - (p) => p.userId === userId, + (p) => admin || p.userId === userId, 'order:statusChanged', (p) => ({ orderId: p.orderId }), ) on( DELIVERY_FEE_ADJUSTED, - (p) => p.userId === userId, + (p) => admin || p.userId === userId, 'order:updated', (p) => ({ orderId: p.orderId }), ) diff --git a/server/src/routes/user-orders.js b/server/src/routes/user-orders.js index 4d5cc45..d74ba91 100644 --- a/server/src/routes/user-orders.js +++ b/server/src/routes/user-orders.js @@ -267,6 +267,12 @@ export async function registerUserOrderRoutes(fastify) { } await prisma.order.update({ where: { id }, data: { status: 'DONE' } }) + request.server.eventBus.emit(NOTIFICATION_EVENTS.ORDER_STATUS_CHANGED, { + orderId: order.id, + userId: order.userId, + oldStatus: order.status, + newStatus: 'DONE', + }) return { ok: true, status: 'DONE' } }), )