From 0cd3dc82fd771e917e5e0d0ad549de31522d2357 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Sat, 18 Apr 2026 01:17:06 +0700 Subject: [PATCH] feat(listings): allow admin to PATCH /listings/:id (TEC-2746) - UpdateListingCommand accepts userRole; ADMIN bypasses owner/agent check - Controller forwards user.role from JwtPayload - Adds unit test covering admin-authorized edit path Co-Authored-By: Paperclip --- .../__tests__/update-listing.handler.spec.ts | 25 ++++++++++++++++++- .../update-listing/update-listing.command.ts | 1 + .../update-listing/update-listing.handler.ts | 7 +++--- .../controllers/listings.controller.ts | 1 + 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/api/src/modules/listings/application/__tests__/update-listing.handler.spec.ts b/apps/api/src/modules/listings/application/__tests__/update-listing.handler.spec.ts index ec52ecc..0155d18 100644 --- a/apps/api/src/modules/listings/application/__tests__/update-listing.handler.spec.ts +++ b/apps/api/src/modules/listings/application/__tests__/update-listing.handler.spec.ts @@ -137,7 +137,30 @@ describe('UpdateListingHandler', () => { const command = new UpdateListingCommand('listing-1', 'stranger', 'Tiêu đề mới'); - await expect(handler.execute(command)).rejects.toThrow(/người bán|môi giới/); + await expect(handler.execute(command)).rejects.toThrow(/người bán|môi giới|quản trị/); + }); + + it('allows an admin to update any listing', async () => { + const listing = createListing('listing-1', 'seller-1'); + const property = createProperty('prop-1'); + mockListingRepo.findById.mockResolvedValue(listing); + mockPropertyRepo.findById.mockResolvedValue(property); + + const command = new UpdateListingCommand( + 'listing-1', + 'admin-user', + 'Tiêu đề do admin sửa', + undefined, + undefined, + undefined, + undefined, + undefined, + 'ADMIN', + ); + const result = await handler.execute(command); + + expect(result.listingId).toBe('listing-1'); + expect(result.updatedFields).toContain('title'); }); }); diff --git a/apps/api/src/modules/listings/application/commands/update-listing/update-listing.command.ts b/apps/api/src/modules/listings/application/commands/update-listing/update-listing.command.ts index 71eb7d0..85025e9 100644 --- a/apps/api/src/modules/listings/application/commands/update-listing/update-listing.command.ts +++ b/apps/api/src/modules/listings/application/commands/update-listing/update-listing.command.ts @@ -8,5 +8,6 @@ export class UpdateListingCommand { public readonly rentPriceMonthly?: bigint, public readonly amenities?: string[], public readonly mediaOrder?: { mediaId: string; order: number }[], + public readonly userRole?: string, ) {} } diff --git a/apps/api/src/modules/listings/application/commands/update-listing/update-listing.handler.ts b/apps/api/src/modules/listings/application/commands/update-listing/update-listing.handler.ts index d4b6525..b5bc0e2 100644 --- a/apps/api/src/modules/listings/application/commands/update-listing/update-listing.handler.ts +++ b/apps/api/src/modules/listings/application/commands/update-listing/update-listing.handler.ts @@ -38,12 +38,13 @@ export class UpdateListingHandler implements ICommandHandler