Repository navigation
Testing: tests E2E 99% #147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
a21a5e1
testing: OM-409 test E2E created and passed
leoAchu16 fd6b7b8
testing: OM-324 test E2E created and passed
leoAchu16 57222a9
Merge branch 'dev' of https://github.com/CrisNAC/BackendMarketplace i…
leoAchu16 41793b5
testing: OM-326 test E2E created and passed
leoAchu16 09dfb5a
Merge branch 'dev' of https://github.com/CrisNAC/BackendMarketplace i…
leoAchu16 87d257e
testing: OM-487 test E2E created and passed
leoAchu16 81bb777
Merge branch 'dev' of https://github.com/CrisNAC/BackendMarketplace i…
leoAchu16 22eb1d7
testing: OM-492 test E2E created and passed
leoAchu16 ef46cac
Merge branch 'dev' of https://github.com/CrisNAC/BackendMarketplace i…
leoAchu16 15036c8
testing: OM-328 test E2E created and passed
leoAchu16 79aa344
testing: OM-493 test E2E created and passed
leoAchu16 8720c42
testing: OM-485 test E2E created and passed
leoAchu16 6778308
Merge branch 'dev' of https://github.com/CrisNAC/BackendMarketplace i…
leoAchu16 26a72e3
testing: OM-322 test E2E created and passed
leoAchu16 fc00ab5
testing: OM-489 test E2E created and passed
leoAchu16 12c418c
fix: test E2E delivery.test.js
leoAchu16 e3ad27a
testing: OM-490 test E2E created and passed
leoAchu16 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| import { vi, describe, it, expect, beforeEach, beforeAll } from "vitest"; | ||
|
Check warning on line 1 in tests/e2e/assignments-complete.test.js
|
||
| import request from "supertest"; | ||
| import app from "../../src/app.js"; | ||
| import { prisma } from "../../src/lib/prisma.js"; | ||
|
|
||
| const mockTx = { | ||
| deliveryAssignments: { | ||
| update: vi.fn(), | ||
| }, | ||
| orders: { | ||
| update: vi.fn(), | ||
| }, | ||
| }; | ||
|
|
||
| vi.mock("../../src/lib/prisma.js", () => ({ | ||
| prisma: { | ||
| deliveryAssignments: { | ||
| findUnique: vi.fn(), | ||
| }, | ||
| orders: { | ||
| update: vi.fn(), | ||
| }, | ||
| $transaction: vi.fn((callbackOrArray) => { | ||
| if (typeof callbackOrArray === "function") { | ||
| return callbackOrArray(mockTx); | ||
| } | ||
|
|
||
| return Promise.all(callbackOrArray.map((operation) => Promise.resolve(operation))); | ||
| }), | ||
| }, | ||
| })); | ||
|
|
||
| // Mock del middleware de autenticación para controlar req.user según cookie | ||
| vi.mock("../../src/config/jwt.config.js", () => ({ | ||
| default: vi.fn((req, res, next) => { | ||
| const cookie = req.cookies?.userToken; | ||
|
|
||
| if (!cookie) { | ||
| return res.status(401).json({ error: { code: 401, message: "No autenticado" } }); | ||
| } | ||
|
|
||
| // si la cookie es 'other-delivery' simulamos otro usuario | ||
| req.user = cookie === "other-delivery" ? { id_user: 99, role: "DELIVERY" } : { id_user: 5, role: "DELIVERY" }; | ||
| next(); | ||
| }), | ||
| })); | ||
|
|
||
| const authCookie = "userToken=mock-token"; | ||
| const otherDeliveryCookie = "userToken=other-delivery"; | ||
|
|
||
| const mockAcceptedAssignment = { | ||
| id_delivery_assignment: 1, | ||
| fk_order: 100, | ||
| fk_delivery: 5, | ||
| assignment_status: "ACCEPTED", | ||
| status: true, | ||
| delivery: { fk_user: 5 }, | ||
| }; | ||
|
|
||
| const mockDeliveredAssignment = { | ||
| id_delivery_assignment: 1, | ||
| fk_order: 100, | ||
| fk_delivery: 5, | ||
| assignment_status: "DELIVERED", | ||
| status: true, | ||
| }; | ||
|
|
||
| describe("POST /api/assignments/:id/complete", () => { | ||
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
|
|
||
| mockTx.deliveryAssignments.update.mockReset(); | ||
| mockTx.orders.update.mockReset(); | ||
|
|
||
| prisma.deliveryAssignments.findUnique.mockReset(); | ||
| prisma.orders.update.mockReset(); | ||
| prisma.$transaction.mockReset(); | ||
| }); | ||
|
|
||
| it("retrona 200 tras completar asignación con exito", async () => { | ||
| prisma.deliveryAssignments.findUnique.mockResolvedValue(mockAcceptedAssignment); | ||
| mockTx.deliveryAssignments.update.mockResolvedValue(mockDeliveredAssignment); | ||
| mockTx.orders.update.mockResolvedValue({ id_order: 100, order_status: "DELIVERED" }); | ||
|
|
||
| // prisma.$transaction mock invocado arriba devolverá el resultado de callback(mockTx) | ||
| const res = await request(app) | ||
| .post("/api/assignments/1/complete") | ||
| .set("Cookie", authCookie) | ||
| .send(); | ||
|
|
||
| expect(res.status).toBe(200); | ||
| expect(res.body).toMatchObject({ assignment_status: "DELIVERED", id_delivery_assignment: 1 }); | ||
|
|
||
| expect(mockTx.deliveryAssignments.update).toHaveBeenCalledWith( | ||
| expect.objectContaining({ where: { id_delivery_assignment: 1 }, data: { assignment_status: "DELIVERED" } }) | ||
| ); | ||
|
|
||
| expect(mockTx.orders.update).toHaveBeenCalledWith( | ||
| expect.objectContaining({ where: { id_order: 100 }, data: { order_status: "DELIVERED" } }) | ||
| ); | ||
| }); | ||
|
|
||
| it("retorna 403 tras rechazar por usuario no asignado)", async () => { | ||
| prisma.deliveryAssignments.findUnique.mockResolvedValue(mockAcceptedAssignment); | ||
|
|
||
| const res = await request(app) | ||
| .post("/api/assignments/1/complete") | ||
| .set("Cookie", otherDeliveryCookie) | ||
| .send(); | ||
|
|
||
| expect(res.status).toBe(403); | ||
| expect(res.body.error.message).toMatch(/permiso/i); | ||
| // Asegurarse de que no se actualizó nada en la transacción | ||
| expect(mockTx.deliveryAssignments.update).not.toHaveBeenCalled(); | ||
| expect(mockTx.orders.update).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it("retorna 409 tras intentar completar asignación con estado inválido", async () => { | ||
| prisma.deliveryAssignments.findUnique.mockResolvedValue({ ...mockAcceptedAssignment, assignment_status: "PENDING" }); | ||
|
|
||
| const res = await request(app) | ||
| .post("/api/assignments/1/complete") | ||
| .set("Cookie", authCookie) | ||
| .send(); | ||
|
|
||
| expect(res.status).toBe(409); | ||
| expect(res.body.error.message).toMatch(/solo se pueden completar asignaciones aceptadas/i); | ||
| expect(mockTx.deliveryAssignments.update).not.toHaveBeenCalled(); | ||
| expect(mockTx.orders.update).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it("retorna 404 tras intentar completar asignación no encontrada", async () => { | ||
| prisma.deliveryAssignments.findUnique.mockResolvedValue(null); | ||
|
|
||
| const res = await request(app) | ||
| .post("/api/assignments/999/complete") | ||
| .set("Cookie", authCookie) | ||
| .send(); | ||
|
|
||
| expect(res.status).toBe(404); | ||
| expect(res.body.error.message).toMatch(/Asignación no encontrada/i); | ||
| expect(mockTx.deliveryAssignments.update).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| import { vi, describe, it, expect, beforeEach } from "vitest"; | ||
| import request from "supertest"; | ||
| import app from "../../src/app.js"; | ||
| import { prisma } from "../../src/lib/prisma.js"; | ||
|
|
||
|
|
||
| let deliveriesTxMock = { findFirst: vi.fn(), update: vi.fn() }; | ||
|
|
||
| // Mock Prisma | ||
| vi.mock("../../src/lib/prisma.js", () => ({ | ||
| prisma: { | ||
| stores: { findUnique: vi.fn() }, | ||
| deliveries: { findMany: vi.fn() }, | ||
| deliveryReviews: { findMany: vi.fn() }, | ||
| $transaction: vi.fn(async (fn) => fn({ deliveries: deliveriesTxMock })), | ||
| }, | ||
| })); | ||
|
|
||
| // Mock JWT middleware | ||
| vi.mock("../../src/config/jwt.config.js", () => ({ | ||
| default: vi.fn((req, res, next) => { | ||
| const cookie = req.cookies?.userToken; | ||
|
|
||
| if (!cookie) return res.status(401).json({ error: { code: 401, message: "No autenticado" } }); | ||
|
|
||
| if (cookie === "seller-token") req.user = { id_user: 2, email: "seller@test.com", role: "SELLER" }; | ||
| else if (cookie === "delivery-token") req.user = { id_user: 3, email: "delivery@test.com", role: "DELIVERY" }; | ||
| else req.user = { id_user: 1, email: "customer@test.com", role: "CUSTOMER" }; | ||
|
|
||
| next(); | ||
| }), | ||
| })); | ||
|
|
||
| const sellerCookie = "userToken=seller-token"; | ||
|
|
||
| const mockStore = { id_store: 1, fk_user: 2, status: true, user: { id_user: 2, status: true } }; | ||
| const mockStoreOtherOwner = { id_store: 1, fk_user: 99, status: true, user: { id_user: 99, status: true } }; | ||
|
|
||
| // Mocks de deliveries | ||
| const mockDeliveryAvailable = { | ||
| id_delivery: 10, | ||
| delivery_status: "ACTIVE", | ||
| vehicle_type: "MOTORCYCLE", | ||
| user: { id_user: 2, name: "Juan Pérez", email: "juan@delivery.com", phone: "+56912345678" }, | ||
| delivery_assignments: [], | ||
| delivery_reviews: [{ rating: 5 }, { rating: 4 }], | ||
| }; | ||
|
|
||
| const mockDeliveryInDelivery = { | ||
| id_delivery: 11, | ||
| delivery_status: "ACTIVE", | ||
| vehicle_type: "CAR", | ||
| user: { id_user: 3, name: "María González", email: "maria@delivery.com", phone: "+56923456789" }, | ||
| delivery_assignments: [ | ||
| { assignment_status: "ACCEPTED" }, | ||
| { assignment_status: "DELIVERED" }, | ||
| { assignment_status: "DELIVERED" }, | ||
| ], | ||
| delivery_reviews: [{ rating: 5 }, { rating: 5 }, { rating: 4 }], | ||
| }; | ||
|
|
||
| const mockDeliveryUnavailable = { | ||
| id_delivery: 12, | ||
| delivery_status: "INACTIVE", | ||
| vehicle_type: "BICYCLE", | ||
| user: { id_user: 4, name: "Carlos Ramírez", email: "carlos@delivery.com", phone: "+56934567890" }, | ||
| delivery_assignments: [{ assignment_status: "DELIVERED" }, { assignment_status: "REJECTED" }], | ||
| delivery_reviews: [], | ||
| }; | ||
|
|
||
| describe("OM-322: GET /stores/:id/deliveries & DELETE /stores/:id/deliveries/:deliveryId", () => { | ||
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
| // reset tx mocks | ||
| deliveriesTxMock.findFirst = vi.fn(); | ||
| deliveriesTxMock.update = vi.fn(); | ||
| }); | ||
|
|
||
| it("GET - retorna lista y estadísticas exitosamente", async () => { | ||
| prisma.stores.findUnique.mockResolvedValue(mockStore); | ||
| prisma.deliveries.findMany.mockResolvedValue([mockDeliveryAvailable, mockDeliveryInDelivery, mockDeliveryUnavailable]); | ||
|
|
||
| const res = await request(app).get("/api/stores/1/deliveries").set("Cookie", sellerCookie); | ||
|
|
||
| expect(res.status).toBe(200); | ||
| expect(res.body.stats).toMatchObject({ total: 3, available: 1, inDelivery: 1 }); | ||
| // ratings: [5,4] + [5,5,4] = [5,4,5,5,4] => 23/5 = 4.6 | ||
| expect(res.body.stats.avgRating).toBe(4.6); | ||
| expect(Array.isArray(res.body.deliveries)).toBe(true); | ||
| expect(res.body.deliveries[0]).toMatchObject({ user: { id: 2, name: "Juan Pérez" }, vehicleType: "MOTORCYCLE" }); | ||
| }); | ||
|
|
||
| it("GET - 403 si el requester no es dueño del comercio", async () => { | ||
| prisma.stores.findUnique.mockResolvedValue(mockStoreOtherOwner); | ||
|
|
||
| const res = await request(app).get("/api/stores/1/deliveries").set("Cookie", sellerCookie); | ||
| expect(res.status).toBe(403); | ||
| }); | ||
|
|
||
| it("GET - 404 si la tienda no existe", async () => { | ||
| prisma.stores.findUnique.mockResolvedValue(null); | ||
|
|
||
| const res = await request(app).get("/api/stores/1/deliveries").set("Cookie", sellerCookie); | ||
| expect(res.status).toBe(404); | ||
| }); | ||
|
|
||
| it("DELETE - desvincula delivery correctamente", async () => { | ||
| prisma.stores.findUnique.mockResolvedValue(mockStore); | ||
| // en la transacción, findFirst devuelve sin assignments activos | ||
| deliveriesTxMock.findFirst.mockResolvedValue({ id_delivery: 10, delivery_assignments: [] }); | ||
| deliveriesTxMock.update.mockResolvedValue({}); | ||
|
|
||
| const res = await request(app).delete("/api/stores/1/deliveries/10").set("Cookie", sellerCookie); | ||
| expect(res.status).toBe(204); | ||
| expect(deliveriesTxMock.update).toHaveBeenCalledWith({ where: { id_delivery: 10 }, data: { fk_store: null, delivery_status: "INACTIVE" } }); | ||
| }); | ||
|
|
||
| it("DELETE - retorna 400 si tiene assignments activos (PENDING/ACCEPTED)", async () => { | ||
| prisma.stores.findUnique.mockResolvedValue(mockStore); | ||
| deliveriesTxMock.findFirst.mockResolvedValue({ id_delivery: 10, delivery_assignments: [{ id_delivery_assignment: 1 }] }); | ||
|
|
||
| const res = await request(app).delete("/api/stores/1/deliveries/10").set("Cookie", sellerCookie); | ||
| expect(res.status).toBe(400); | ||
| expect(res.body.message).toMatch(/entregas activas/i); | ||
| }); | ||
|
|
||
| it("DELETE - 403 si el requester no es dueño del comercio", async () => { | ||
| prisma.stores.findUnique.mockResolvedValue(mockStoreOtherOwner); | ||
|
|
||
| const res = await request(app).delete("/api/stores/1/deliveries/10").set("Cookie", sellerCookie); | ||
| expect(res.status).toBe(403); | ||
| }); | ||
|
|
||
| it("DELETE - 404 si el delivery no pertenece a la tienda", async () => { | ||
| prisma.stores.findUnique.mockResolvedValue(mockStore); | ||
| deliveriesTxMock.findFirst.mockResolvedValue(null); | ||
|
|
||
| const res = await request(app).delete("/api/stores/1/deliveries/999").set("Cookie", sellerCookie); | ||
| expect(res.status).toBe(404); | ||
| expect(res.body.message).toMatch(/delivery no encontrado/i); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: CrisNAC/BackendMarketplace
Length of output: 15382
🏁 Script executed:
Repository: CrisNAC/BackendMarketplace
Length of output: 3795
🏁 Script executed:
Repository: CrisNAC/BackendMarketplace
Length of output: 108
🏁 Script executed:
Repository: CrisNAC/BackendMarketplace
Length of output: 2420
🏁 Script executed:
# Find the error class definitions cat src/lib/errors.jsRepository: CrisNAC/BackendMarketplace
Length of output: 1421
🏁 Script executed:
# View the complete errorHandler middleware cat src/middlewares/errorHandler.jsRepository: CrisNAC/BackendMarketplace
Length of output: 2213
Las aserciones de error del
DELETEusanres.body.messagepero el middleware devuelveres.body.error.messageEl middleware global
errorHandler.jsformatea todos losAppError(incluyendoValidationErroryNotFoundError) con estructura{ error: { code, message } }. Las aserciones en L124 y L140 acceden directamente ares.body.message, lo que causará que fallen porque la propiedad no existe. Deben cambiar ares.body.error.messagepara coincidir con la respuesta real del servidor.Cambios requeridos
it("DELETE - retorna 400 si tiene assignments activos (PENDING/ACCEPTED)", async () => { prisma.stores.findUnique.mockResolvedValue(mockStore); deliveriesTxMock.findFirst.mockResolvedValue({ id_delivery: 10, delivery_assignments: [{ id_delivery_assignment: 1 }] }); const res = await request(app).delete("/api/stores/1/deliveries/10").set("Cookie", sellerCookie); expect(res.status).toBe(400); - expect(res.body.message).toMatch(/entregas activas/i); + expect(res.body.error.message).toMatch(/entregas activas/i); }); it("DELETE - 404 si el delivery no pertenece a la tienda", async () => { prisma.stores.findUnique.mockResolvedValue(mockStore); deliveriesTxMock.findFirst.mockResolvedValue(null); const res = await request(app).delete("/api/stores/1/deliveries/999").set("Cookie", sellerCookie); expect(res.status).toBe(404); - expect(res.body.message).toMatch(/delivery no encontrado/i); + expect(res.body.error.message).toMatch(/delivery no encontrado/i); });📝 Committable suggestion
🤖 Prompt for AI Agents