QA-2: Abgabe-Wizard e2e (ForSale-Seed, sale-ad+contracts Mock, abgabe.spec.ts)

This commit is contained in:
2026-06-06 15:16:38 +02:00
parent 868c5f5cd7
commit 791e9f91cc
3 changed files with 206 additions and 0 deletions

View File

@@ -101,6 +101,7 @@ export async function installMockApi(page: Page): Promise<MockDb> {
'color-varieties': collection(db.colorVarieties as unknown as Row[], 'cv'),
'health-records': collection(db.healthRecords as unknown as Row[], 'hr'),
'weight-records': collection(db.weightRecords as unknown as Row[], 'wr'),
contracts: collection(db.contracts as unknown as Row[], 'contract'),
}
const handler = async (route: Route) => {
@@ -205,6 +206,25 @@ export async function installMockApi(page: Page): Promise<MockDb> {
return json(route, 405)
}
// ABGABE: KI-Inserat-Generator (POST /gerbils/sale-ad) — vor der generischen Route
if (path === '/gerbils/sale-ad' && method === 'POST') {
if (!db.saleAdConfigured) {
return json(route, 503, { code: 'AiKeyMissing', message: 'AI nicht konfiguriert' })
}
return json(route, 200, { text: 'Status: FREI\n\n„Zwei Freunde suchen ein Zuhause" Generierter Inserat-Text für den Mock.' })
}
// ABGABE: Vertrag-Download — /contracts/{id}/file (3 Segmente, nicht vom Generic-Handler bedient)
const cm = path.match(/^\/contracts\/([^/]+)\/file$/)
if (cm && method === 'GET') {
return route.fulfill({
status: 200,
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
headers: { 'Content-Disposition': `attachment; filename="vertrag-${cm[1]}.docx"` },
body: Buffer.from([0x50, 0x4b, 0x05, 0x06, ...new Array(18).fill(0)]),
})
}
// SEARCH-2b: distinct Herkunft (originBreeder) values, sorted — vor der
// generischen /gerbils/:id-Route abfangen.
if (path === '/gerbils/breeders' && method === 'GET') {
@@ -313,6 +333,34 @@ export async function installMockApi(page: Page): Promise<MockDb> {
}
}
// ABGABE: POST /contracts — baut url-Feld und markiert Tiere als Abgegeben
if (path === '/contracts' && method === 'POST') {
const body = request.postDataJSON() as { contactId: string; gerbilIds: string[]; price: number; handoverDate: string; contractDate?: string | null }
const id = newId('contract')
const contract = {
id,
contactId: body.contactId,
gerbilIds: body.gerbilIds ?? [],
price: body.price,
handoverDate: body.handoverDate,
contractDate: body.contractDate ?? body.handoverDate,
fileName: `vertrag-${id}.docx`,
createdAt: new Date().toISOString(),
url: `/contracts/${id}/file`,
}
;(db.contracts as unknown as Row[]).push(contract as unknown as Row)
// Tiere auf GivenAway setzen (wie das echte Backend)
for (const gid of body.gerbilIds ?? []) {
const g = db.gerbils.find((x) => x.id === gid)
if (g) {
g.status = 'GivenAway'
g.receiverContactId = body.contactId
g.goHomeDate = body.handoverDate
}
}
return json(route, 201, contract)
}
// Generische Kollektionen: /<resource> und /<resource>/<id>
m = path.match(/^\/([a-z-]+)(?:\/([^/]+))?$/)
const col = m ? collections[m[1]] : undefined