/** * AC verification for Passo 10: * Submitting a request (with and without photo) creates the row in the DB. * When a photo is included, the object exists in MinIO. */ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { config as loadEnv } from 'dotenv'; const here = path.dirname(fileURLToPath(import.meta.url)); loadEnv({ path: path.resolve(here, '../.env') }); import { prisma } from '../packages/db/src/index.js'; import { appRouter, createTRPCContext } from '../packages/api/src/index.js'; import { createCallerFactory } from '../packages/api/src/trpc.js'; async function makeCaller(email: string) { const user = await prisma.user.findFirst({ where: { email } }); if (!user) throw new Error(`${email} not found — run pnpm db:seed`); const ctx = await createTRPCContext({ user: { id: user.id, email: user.email, role: user.role as 'OPERATOR', tenantId: user.tenantId }, headers: new Headers(), }); return createCallerFactory(appRouter)(ctx); } async function main() { const op1 = await makeCaller('op1@demo.local'); const workstations = await op1.workstation.list(); const ws = workstations[0]; if (!ws) throw new Error('No workstations — run pnpm db:seed'); // --- Without photo --- console.log('1. Create request WITHOUT photo...'); const noPhoto = await op1.maintenanceRequest.create({ workstationId: ws.id, description: 'Problema no posto — sem foto', clientRequestId: crypto.randomUUID(), }); if (noPhoto.status !== 'OPEN') throw new Error('Expected OPEN status'); const rowNoPhoto = await prisma.maintenanceRequest.findFirst({ where: { id: noPhoto.id } }); if (!rowNoPhoto) throw new Error('Row not found in DB'); if (rowNoPhoto.photoKey !== null) throw new Error('Expected null photoKey'); console.log(` id=${noPhoto.id} photoKey=null ✓`); // --- With photo --- console.log('2. Create request WITH photo...'); const photoContent = 'fake-photo-content-passo10'; const { uploadUrl, photoKey } = await op1.storage.signPhotoUpload({ contentType: 'image/jpeg', byteSize: photoContent.length, }); const putRes = await fetch(uploadUrl, { method: 'PUT', body: photoContent, headers: { 'Content-Type': 'image/jpeg' }, }); if (!putRes.ok) throw new Error(`Photo PUT failed: ${putRes.status}`); const withPhoto = await op1.maintenanceRequest.create({ workstationId: ws.id, description: 'Problema no posto — com foto', photoKey, clientRequestId: crypto.randomUUID(), }); if (withPhoto.status !== 'OPEN') throw new Error('Expected OPEN status'); const rowWithPhoto = await prisma.maintenanceRequest.findFirst({ where: { id: withPhoto.id } }); if (!rowWithPhoto?.photoKey) throw new Error('Expected photoKey in row'); console.log(` id=${withPhoto.id} photoKey=${rowWithPhoto.photoKey} ✓`); // Verify photo is in MinIO via signed GET const { url: getUrl } = await op1.storage.signPhotoDownload({ photoKey }); const getRes = await fetch(getUrl); if (!getRes.ok) throw new Error(`Photo GET failed: ${getRes.status}`); const downloaded = await getRes.text(); if (downloaded !== photoContent) throw new Error('Photo content mismatch'); console.log(' Photo content in MinIO matches ✓'); await prisma.$disconnect(); console.log('\nPasso 10 AC PASSED'); } main().catch(async (err) => { console.error('Passo 10 AC FAILED:', err); await prisma.$disconnect(); process.exit(1); });