Merge branch 'туц_fixes'
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,96 @@
|
||||
import jwt from '@fastify/jwt'
|
||||
import Fastify from 'fastify'
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { prisma } from '../../../lib/prisma.js'
|
||||
import { mapProductForApi, parseMaterialsInput, slugify } from '../_product-helpers.js'
|
||||
import { registerAdminProductRoutes } from '../admin-products.js'
|
||||
|
||||
const JWT_SECRET = 'test-secret'
|
||||
const ADMIN_EMAIL = `admin-products-${Date.now()}@example.com`
|
||||
|
||||
let app
|
||||
let adminUser
|
||||
let category
|
||||
|
||||
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('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' })
|
||||
}
|
||||
})
|
||||
fastify.decorate('slugify', slugify)
|
||||
fastify.decorate('parseMaterialsInput', parseMaterialsInput)
|
||||
fastify.decorate('mapProductForApi', mapProductForApi)
|
||||
await registerAdminProductRoutes(fastify)
|
||||
await fastify.ready()
|
||||
return fastify
|
||||
}
|
||||
|
||||
function productData(overrides = {}) {
|
||||
return {
|
||||
title: 'Медведь',
|
||||
slug: `bear-${Date.now()}`,
|
||||
priceCents: 120000,
|
||||
quantity: 1,
|
||||
categoryId: category.id,
|
||||
published: true,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
describe('admin product routes', () => {
|
||||
beforeAll(async () => {
|
||||
await prisma.product.deleteMany({ where: { category: { slug: { startsWith: 'admin-products-test-' } } } })
|
||||
await prisma.category.deleteMany({ where: { slug: { startsWith: 'admin-products-test-' } } })
|
||||
await prisma.user.deleteMany({ where: { email: ADMIN_EMAIL } })
|
||||
|
||||
adminUser = await prisma.user.create({ data: { email: ADMIN_EMAIL } })
|
||||
category = await prisma.category.create({
|
||||
data: {
|
||||
name: 'Тестовая категория',
|
||||
slug: `admin-products-test-${Date.now()}`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await prisma.product.deleteMany({ where: { categoryId: category.id } })
|
||||
app = await buildApp()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.product.deleteMany({ where: { categoryId: category.id } })
|
||||
await prisma.category.delete({ where: { id: category.id } })
|
||||
await prisma.user.delete({ where: { id: adminUser.id } })
|
||||
})
|
||||
|
||||
it('генерирует уникальный slug при создании товара с повторяющимся названием без ручного slug', async () => {
|
||||
await prisma.product.create({ data: productData({ title: 'Bear', slug: 'bear' }) })
|
||||
const token = await signToken(adminUser)
|
||||
|
||||
const res = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/admin/products',
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: productData({ title: 'Bear', slug: undefined }),
|
||||
})
|
||||
|
||||
expect(res.statusCode).toBe(201)
|
||||
expect(res.json().slug).toBe('bear-2')
|
||||
})
|
||||
})
|
||||
@@ -40,6 +40,19 @@ const PATCH_PRODUCT_SCHEMA = {
|
||||
},
|
||||
}
|
||||
|
||||
async function buildUniqueProductSlug(baseSlug) {
|
||||
const base = String(baseSlug || '').trim()
|
||||
let candidate = base
|
||||
let suffix = 2
|
||||
|
||||
while (await prisma.product.findUnique({ where: { slug: candidate } })) {
|
||||
candidate = `${base}-${suffix}`
|
||||
suffix += 1
|
||||
}
|
||||
|
||||
return candidate
|
||||
}
|
||||
|
||||
export async function registerAdminProductRoutes(fastify) {
|
||||
fastify.get('/api/admin/products', { preHandler: [fastify.verifyAdmin] }, async (request) => {
|
||||
const items = await prisma.product.findMany({
|
||||
@@ -59,7 +72,9 @@ export async function registerAdminProductRoutes(fastify) {
|
||||
reply.code(400).send({ error: 'Укажите название' })
|
||||
return
|
||||
}
|
||||
const slug = String(body.slug ?? '').trim() || request.server.slugify(title) || `item-${Date.now()}`
|
||||
const requestedSlug = String(body.slug ?? '').trim()
|
||||
const slugBase = requestedSlug || request.server.slugify(title) || `item-${Date.now()}`
|
||||
const slug = requestedSlug ? slugBase : await buildUniqueProductSlug(slugBase)
|
||||
const categoryId = String(body.categoryId ?? '').trim()
|
||||
if (!categoryId) {
|
||||
reply.code(400).send({ error: 'Укажите категорию' })
|
||||
@@ -79,7 +94,7 @@ export async function registerAdminProductRoutes(fastify) {
|
||||
reply.code(400).send({ error: 'Цена не может превышать 10 000 ₽' })
|
||||
return
|
||||
}
|
||||
const exists = await prisma.product.findUnique({ where: { slug } })
|
||||
const exists = requestedSlug ? await prisma.product.findUnique({ where: { slug } }) : null
|
||||
if (exists) {
|
||||
reply.code(409).send({ error: 'Такой slug уже занят' })
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user