feat(F-048): completed feature

This commit is contained in:
chattie
2026-08-19 07:17:14 +02:00
parent 8ee1938af9
commit 835ab66eda
187 changed files with 12361 additions and 1065 deletions

View File

@@ -97,7 +97,7 @@ describe.skipIf(!hasDb)('categories flows (real PostgreSQL)', () => {
url: '/categories',
headers: { 'content-type': 'application/json' },
cookies: { [SESSION_COOKIE_NAME]: adminCookie },
payload: { name: 'Alimentación', slug: 'alimentacion' },
payload: { name: 'Alimentación', slug: 'alimentacion', isParent: true },
});
expect(root.statusCode).toBe(201);
const rootBody = root.json() as { id: string };

View File

@@ -7,9 +7,11 @@ import {
recreateDatabase,
runMigrations,
} from '../../infrastructure/db/tests/db-test-support.js';
import { InventoryService } from '../../modules/inventory/index.js';
import { InsufficientStockError } from '../../modules/inventory/domain/errors.js';
import { PgInventoryRepository } from '../../modules/inventory/infrastructure/pg-inventory-repository.js';
import {
createInventoryService,
InsufficientStockError,
type InventoryService,
} from '../../modules/inventory/index.js';
const hasDb = Boolean(process.env.TEST_DATABASE_URL);
@@ -22,7 +24,7 @@ describe.skipIf(!hasDb)('inventory flows (real PostgreSQL)', () => {
await recreateDatabase(url);
await runMigrations(url, 'up');
pool = createPool(url);
inventory = new InventoryService(new PgInventoryRepository(pool));
inventory = createInventoryService(pool);
});
afterAll(async () => {

View File

@@ -98,13 +98,14 @@ describe.skipIf(!hasDb)('users + RBAC flows (real PostgreSQL)', () => {
expect(cross.statusCode).toBe(403);
expect(cross.json().error.code).toBe('FORBIDDEN');
// Own profile does not exist yet -> 404 (authz passed, resource missing).
const ownMissing = await app.inject({
// Customer detail exists even before optional profile fields are populated.
const ownCustomer = await app.inject({
method: 'GET',
url: `/users/${anaId}`,
cookies: { [SESSION_COOKIE_NAME]: anaCookie },
});
expect(ownMissing.statusCode).toBe(404);
expect(ownCustomer.statusCode).toBe(200);
expect(ownCustomer.json()).toMatchObject({ id: anaId, email: 'ana@example.com' });
});
it('PATCH own profile upserts; PATCH is validated', async () => {
@@ -157,8 +158,8 @@ describe.skipIf(!hasDb)('users + RBAC flows (real PostgreSQL)', () => {
cookies: { [SESSION_COOKIE_NAME]: anaCookie },
});
expect(asAdmin.statusCode).toBe(200);
const body = asAdmin.json() as { items: Array<{ userId: string }> };
expect(body.items.some((item) => item.userId === anaId)).toBe(true);
const body = asAdmin.json() as { items: Array<{ id: string }> };
expect(body.items.some((item) => item.id === anaId)).toBe(true);
// Admin can also read another user's profile (owner-or-admin).
const adminReadsBen = await app.inject({
@@ -166,8 +167,9 @@ describe.skipIf(!hasDb)('users + RBAC flows (real PostgreSQL)', () => {
url: `/users/${benId}`,
cookies: { [SESSION_COOKIE_NAME]: anaCookie },
});
// Ben has no profile yet: 404, not 403 (authz passed).
expect(adminReadsBen.statusCode).toBe(404);
// Customer detail remains available when optional profile fields are absent.
expect(adminReadsBen.statusCode).toBe(200);
expect(adminReadsBen.json()).toMatchObject({ id: benId, email: 'ben@example.com' });
});
it('address CRUD works end to end for own addresses (AC4)', async () => {