ываыв
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,118 @@
|
||||
import jwt from '@fastify/jwt'
|
||||
import Fastify from 'fastify'
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { prisma } from '../../../lib/prisma.js'
|
||||
import { registerAdminOrderRoutes } from '../admin-orders.js'
|
||||
|
||||
const JWT_SECRET = 'test-secret'
|
||||
const ADMIN_EMAIL = `admin-orders-${Date.now()}@example.com`
|
||||
const USER_EMAIL = `admin-orders-user-${Date.now()}@example.com`
|
||||
|
||||
let app
|
||||
let adminUser
|
||||
let buyer
|
||||
|
||||
async function signToken(user) {
|
||||
return app.jwt.sign({ sub: user.id, email: user.email })
|
||||
}
|
||||
|
||||
async function buildApp() {
|
||||
const fastify = Fastify({ logger: false })
|
||||
await fastify.register(jwt, { secret: JWT_SECRET })
|
||||
fastify.decorate('eventBus', { emit: vi.fn() })
|
||||
fastify.decorate('verifyAdmin', async (request, reply) => {
|
||||
try {
|
||||
await request.jwtVerify()
|
||||
} catch {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
if (request.user.email !== ADMIN_EMAIL) {
|
||||
return reply.code(401).send({ error: 'Admin only' })
|
||||
}
|
||||
})
|
||||
await registerAdminOrderRoutes(fastify)
|
||||
await fastify.ready()
|
||||
return fastify
|
||||
}
|
||||
|
||||
async function createOrder(data = {}) {
|
||||
return prisma.order.create({
|
||||
data: {
|
||||
userId: buyer.id,
|
||||
status: 'PENDING_PAYMENT',
|
||||
deliveryType: 'delivery',
|
||||
deliveryFeeLocked: false,
|
||||
paymentMethod: 'online',
|
||||
itemsSubtotalCents: 10000,
|
||||
deliveryFeeCents: 50000,
|
||||
totalCents: 60000,
|
||||
currency: 'RUB',
|
||||
...data,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe('admin order routes', () => {
|
||||
beforeAll(async () => {
|
||||
await prisma.payment.deleteMany()
|
||||
await prisma.order.deleteMany({ where: { user: { email: { in: [ADMIN_EMAIL, USER_EMAIL] } } } })
|
||||
await prisma.user.deleteMany({ where: { email: { in: [ADMIN_EMAIL, USER_EMAIL] } } })
|
||||
|
||||
adminUser = await prisma.user.create({ data: { email: ADMIN_EMAIL } })
|
||||
buyer = await prisma.user.create({ data: { email: USER_EMAIL } })
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await prisma.order.deleteMany({ where: { userId: buyer.id } })
|
||||
app = await buildApp()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.order.deleteMany({ where: { userId: buyer.id } })
|
||||
await prisma.user.deleteMany({ where: { id: { in: [adminUser.id, buyer.id] } } })
|
||||
})
|
||||
|
||||
it('summary counts only delivery orders waiting for price approval', async () => {
|
||||
const token = await signToken(adminUser)
|
||||
const beforeRes = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/admin/orders/summary',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
const baseline = beforeRes.json().attentionCount
|
||||
|
||||
await createOrder({ deliveryFeeLocked: false, deliveryType: 'delivery' })
|
||||
await createOrder({ deliveryFeeLocked: true, deliveryType: 'delivery' })
|
||||
await createOrder({ deliveryFeeLocked: false, deliveryType: 'pickup' })
|
||||
await createOrder({ deliveryFeeLocked: false, deliveryType: 'delivery', status: 'PAID' })
|
||||
|
||||
const res = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/admin/orders/summary',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.json()).toEqual({ attentionCount: baseline + 1 })
|
||||
})
|
||||
|
||||
it('rejects PAID transition while delivery fee is not locked', async () => {
|
||||
const order = await createOrder({ deliveryFeeLocked: false, deliveryType: 'delivery' })
|
||||
const token = await signToken(adminUser)
|
||||
|
||||
const res = await app.inject({
|
||||
method: 'PATCH',
|
||||
url: `/api/admin/orders/${order.id}/status`,
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: { status: 'PAID' },
|
||||
})
|
||||
|
||||
expect(res.statusCode).toBe(409)
|
||||
expect(res.json().error).toContain('стоимость доставки')
|
||||
})
|
||||
})
|
||||
@@ -7,6 +7,8 @@ export async function registerAdminOrderRoutes(fastify) {
|
||||
const attentionCount = await prisma.order.count({
|
||||
where: {
|
||||
status: 'PENDING_PAYMENT',
|
||||
deliveryType: 'delivery',
|
||||
deliveryFeeLocked: false,
|
||||
},
|
||||
})
|
||||
return { attentionCount }
|
||||
@@ -97,6 +99,11 @@ export async function registerAdminOrderRoutes(fastify) {
|
||||
error: `Нельзя сменить статус ${existing.status} → ${next}`,
|
||||
})
|
||||
}
|
||||
if (next === 'PAID' && existing.deliveryType === 'delivery' && existing.deliveryFeeLocked === false) {
|
||||
return reply.code(409).send({
|
||||
error: 'Сначала подтвердите итоговую стоимость доставки',
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await prisma.order.update({
|
||||
where: { id },
|
||||
|
||||
Reference in New Issue
Block a user