diff --git a/fixtures/media.fixture.js b/fixtures/media.fixture.js index 2734b12..949c9cd 100644 --- a/fixtures/media.fixture.js +++ b/fixtures/media.fixture.js @@ -3,6 +3,86 @@ const { test } = require("./authRequest.fixture"); // Provee media de muestra existente y media temporal creada para pruebas exports.test = test.extend({ + // Fixture para asegurar que tenemos una media publicada y otra sin publicar + mediaStates: async ({ authRequest }, use) => { + let publishedMedia = null; + let unpublishedMedia = null; + + // 1. Buscar medias existentes con diferentes estados + const res = await authRequest.get("/api/media?all=true&limit=10"); + const body = await res.json(); + const list = Array.isArray(body?.data) ? body.data : []; + + publishedMedia = list.find((m) => m.is_published === true); + unpublishedMedia = list.find((m) => m.is_published === false); + + // 2. Si no encontramos alguna, crear las que falten + if (!unpublishedMedia) { + const unpublishedPayload = { + title: `qa_media_unpublished_${Date.now()}`, + type: "video", + visible: "true", + is_published: "false", + }; + try { + const createRes = await authRequest.post("/api/media", { + form: unpublishedPayload, + }); + const createBody = await createRes.json(); + if (createRes.ok() && createBody?.status === "OK") { + unpublishedMedia = Array.isArray(createBody.data) + ? createBody.data[0] + : createBody.data; + } + } catch (e) { + console.log(`[media.fixture] Error creando media sin publicar:`, e); + } + } + + if (!publishedMedia) { + const publishedPayload = { + title: `qa_media_published_${Date.now()}`, + type: "video", + visible: "true", + is_published: "true", + }; + try { + const createRes = await authRequest.post("/api/media", { + form: publishedPayload, + }); + const createBody = await createRes.json(); + if (createRes.ok() && createBody?.status === "OK") { + publishedMedia = Array.isArray(createBody.data) + ? createBody.data[0] + : createBody.data; + } + } catch (e) { + console.log(`[media.fixture] Error creando media publicada:`, e); + } + } + + // 3. Si tenemos una media sin publicar pero necesitamos una publicada, intentar publicarla + if (!publishedMedia && unpublishedMedia) { + try { + const updateRes = await authRequest.put( + `/api/media/${unpublishedMedia._id}`, + { + form: { ...unpublishedMedia, is_published: "true" }, + } + ); + if (updateRes.ok()) { + const updateBody = await updateRes.json(); + publishedMedia = Array.isArray(updateBody.data) + ? updateBody.data[0] + : updateBody.data; + } + } catch (e) { + console.log(`[media.fixture] Error publicando media:`, e); + } + } + + await use({ published: publishedMedia, unpublished: unpublishedMedia }); + }, mediaSample: async ({ authRequest }, use) => { const res = await authRequest.get("/api/media?limit=1"); const body = await res.json(); diff --git a/tests/media/filter.test.js b/tests/media/filter.test.js new file mode 100644 index 0000000..86bce1c --- /dev/null +++ b/tests/media/filter.test.js @@ -0,0 +1,247 @@ +const { test, expect } = require("../../fixtures"); +const logger = require("../utils/logger"); + +// Helper para crear una media y asegurar su eliminación +async function createMedia(authRequest, attrs = {}) { + const payload = Object.assign( + { + title: `qa_filter_${Date.now()}_${Math.floor(Math.random() * 1000)}`, + type: "video", + visible: "true", + is_published: "false", + }, + attrs + ); + + const res = await authRequest.post("/api/media", { form: payload }); + if (!res.ok()) { + const txt = await res.text().catch(() => ""); + throw new Error(`createMedia failed: ${res.status()} ${txt}`); + } + const body = await res.json(); + const created = Array.isArray(body.data) ? body.data[0] : body.data; + return created; +} + +// Helper para construir query string for filterData +function buildFilterQuery(filters) { + // filters: array of { filter, rule, value } + const params = new URLSearchParams(); + filters.forEach((f, idx) => { + const base = `filterData[${idx}]`; + if (f.filter !== undefined) + params.append(`${base}[filter]`, String(f.filter)); + if (f.rule !== undefined) params.append(`${base}[rule]`, String(f.rule)); + if (f.value !== undefined) params.append(`${base}[value]`, String(f.value)); + }); + return params.toString(); +} + +// Attach small evidence on failure +async function attachShort(info, title, obj) { + try { + await info.attach(title, { + body: JSON.stringify(obj, null, 2).slice(0, 20000), + contentType: "application/json", + }); + } catch (e) { + // ignore + } +} + +test.describe("Media - Filter parameters", () => { + test("FILTER-TITLE-IS - filter by exact title (filterData[0][filter]=title)", async ({ + authRequest, + }) => { + const info = test.info(); + const media = await createMedia(authRequest, { + title: `qa_filter_exact_${Date.now()}`, + }); + try { + const filters = [{ filter: "title", rule: "is", value: media.title }]; + const qs = buildFilterQuery(filters); + const url = `/api/media?${qs}&all=true`; + const res = await authRequest.get(url); + const body = await res.json(); + + if (!res.ok() || !Array.isArray(body.data)) { + await attachShort(info, "FILTER-TITLE-IS - Response", { + url, + status: res.status(), + body, + }); + } + + expect(res.ok()).toBeTruthy(); + expect(body.status).toBe("OK"); + // expect to find created media in results + const ids = (body.data || []).map((m) => m._id ?? m.id ?? m); + expect(ids).toContain(media._id); + } finally { + // cleanup + if (media && media._id) + await authRequest.delete(`/api/media/${media._id}`); + } + }); + + test("FILTER-TITLE-CONTAINS - filter by substring", async ({ + authRequest, + }) => { + const info = test.info(); + const unique = `qa_filter_contain_${Date.now()}`; + const media = await createMedia(authRequest, { + title: `prefix_${unique}_suffix`, + }); + try { + const filters = [{ filter: "title", rule: "contains", value: unique }]; + const qs = buildFilterQuery(filters); + const url = `/api/media?${qs}&all=true`; + const res = await authRequest.get(url); + const body = await res.json(); + + if (!res.ok() || !Array.isArray(body.data)) { + await attachShort(info, "FILTER-TITLE-CONTAINS - Response", { + url, + status: res.status(), + body, + }); + } + + expect(res.ok()).toBeTruthy(); + expect(body.status).toBe("OK"); + const ids = (body.data || []).map((m) => m._id ?? m.id ?? m); + expect(ids).toContain(media._id); + } finally { + if (media && media._id) + await authRequest.delete(`/api/media/${media._id}`); + } + }); + + test("FILTER-TITLE-STARTS_WITH - filter by prefix", async ({ + authRequest, + }) => { + const info = test.info(); + const prefix = `qa_prefix_${Date.now()}`; + const media = await createMedia(authRequest, { title: `${prefix}_rest` }); + try { + const filters = [{ filter: "title", rule: "starts_with", value: prefix }]; + const qs = buildFilterQuery(filters); + const url = `/api/media?${qs}&all=true`; + const res = await authRequest.get(url); + const body = await res.json(); + + if (!res.ok() || !Array.isArray(body.data)) { + await attachShort(info, "FILTER-TITLE-STARTS_WITH - Response", { + url, + status: res.status(), + body, + }); + } + + expect(res.ok()).toBeTruthy(); + expect(body.status).toBe("OK"); + const ids = (body.data || []).map((m) => m._id ?? m.id ?? m); + expect(ids).toContain(media._id); + } finally { + if (media && media._id) + await authRequest.delete(`/api/media/${media._id}`); + } + }); + + test("FILTER-TITLE-ENDS_WITH - filter by suffix", async ({ authRequest }) => { + const info = test.info(); + const suffix = `qa_suffix_${Date.now()}`; + const media = await createMedia(authRequest, { title: `start_${suffix}` }); + try { + const filters = [{ filter: "title", rule: "ends_with", value: suffix }]; + const qs = buildFilterQuery(filters); + const url = `/api/media?${qs}&all=true`; + const res = await authRequest.get(url); + const body = await res.json(); + + if (!res.ok() || !Array.isArray(body.data)) { + await attachShort(info, "FILTER-TITLE-ENDS_WITH - Response", { + url, + status: res.status(), + body, + }); + } + + expect(res.ok()).toBeTruthy(); + expect(body.status).toBe("OK"); + const ids = (body.data || []).map((m) => m._id ?? m.id ?? m); + expect(ids).toContain(media._id); + } finally { + if (media && media._id) + await authRequest.delete(`/api/media/${media._id}`); + } + }); + + test("FILTER-PUBLISHED-TRUE - filter by published true", async ({ + authRequest, + }) => { + const info = test.info(); + + // Primero buscamos una media que ya esté publicada + const searchRes = await authRequest.get("/api/media?all=true&limit=50"); + const searchBody = await searchRes.json(); + expect(searchRes.ok()).toBeTruthy(); + + const publishedMedia = searchBody.data.find((m) => m.is_published === true); + expect( + publishedMedia, + "No se encontró una media publicada para probar" + ).toBeTruthy(); + + // Ahora probamos el filtro de published + const filters = [{ filter: "published", rule: "true" }]; + const qs = buildFilterQuery(filters); + const url = `/api/media?${qs}`; + const res = await authRequest.get(url); + const body = await res.json(); + + if (!res.ok() || !Array.isArray(body.data)) { + await attachShort(info, "FILTER-PUBLISHED-TRUE - Response", { + url, + status: res.status(), + body, + }); + } + + expect(res.ok()).toBeTruthy(); + expect(body.status).toBe("OK"); + const ids = (body.data || []).map((m) => m._id ?? m.id ?? m); + expect(ids).toContain(publishedMedia._id); + }); + + test("FILTER-TYPE-VIDEO - filter by type video", async ({ authRequest }) => { + const info = test.info(); + const media = await createMedia(authRequest, { + title: `qa_type_${Date.now()}`, + type: "video", + }); + try { + const filters = [{ filter: "type", rule: "video" }]; + const qs = buildFilterQuery(filters); + const url = `/api/media?${qs}&all=true`; + const res = await authRequest.get(url); + const body = await res.json(); + + if (!res.ok() || !Array.isArray(body.data)) { + await attachShort(info, "FILTER-TYPE-VIDEO - Response", { + url, + status: res.status(), + body, + }); + } + + expect(res.ok()).toBeTruthy(); + expect(body.status).toBe("OK"); + const ids = (body.data || []).map((m) => m._id ?? m.id ?? m); + expect(ids).toContain(media._id); + } finally { + if (media && media._id) + await authRequest.delete(`/api/media/${media._id}`); + } + }); +}); diff --git a/tests/media/getall.test.js b/tests/media/getall.test.js index 41270c8..196647f 100644 --- a/tests/media/getall.test.js +++ b/tests/media/getall.test.js @@ -1,9 +1,26 @@ const { test, expect } = require("../../fixtures"); const logger = require("../utils/logger"); +// Helper para adjuntar un resumen de la respuesta al reporte (trunca cuerpo largo) +async function attachSummary(info, title, details) { + try { + let payload = JSON.stringify(details, null, 2); + if (payload.length > 20000) + payload = payload.slice(0, 20000) + "\n...truncated"; + await info.attach(title, { + body: payload, + contentType: "application/json", + }); + } catch (e) { + console.error("attachSummary error:", e); + } +} + test.describe("🎬 Media all", () => { - test("GET /api/media all false", async ({ authRequest }) => { - const res = await authRequest.get("/api/media?all=false&limit=1"); + test("TC-MED-GET-001: Lista de medias solo publicadas (all=false)", async ({ + authRequest, + }) => { + const res = await authRequest.get("/api/media?all=false"); expect(res.ok()).toBeTruthy(); const body = await res.json(); @@ -12,13 +29,69 @@ test.describe("🎬 Media all", () => { ); expect(allPublished).toBe(true); + // Adjuntar resumen al reporte + const info = test.info(); + await attachSummary(info, "TC-MED-GET-001 - Response Summary", { + url: "/api/media?all=false", + status: res.status(), + publishedCount: body.data.length, + sampleIds: body.data.slice(0, 5).map((m) => m._id), + }); + logger.info( `📡 GET /api/media all=false - Status: ${res.status()}, Published: ${allPublished}` ); }); - test("GET /api/media all true", async ({ authRequest }) => { - const res = await authRequest.get("/api/media?all=true&limit=1"); + test("TC-MED-GET-001.1: Verificar que media despublicada no aparece con all=false", async ({ + authRequest, + }) => { + // 1️⃣ Primero obtenemos una media despublicada + const resAll = await authRequest.get("/api/media?all=true"); + expect(resAll.ok()).toBeTruthy(); + + const allBody = await resAll.json(); + const unpublishedMedia = allBody.data.find( + (media) => media.is_published === false + ); + + // Verificar que encontramos una media despublicada + expect( + unpublishedMedia, + "No se encontró una media despublicada para probar" + ).toBeTruthy(); + + // 2️⃣ Intentar obtener esa media específica con all=false + const res = await authRequest.get( + `/api/media?all=false&id=${unpublishedMedia._id}` + ); + expect(res.ok()).toBeTruthy(); + + const body = await res.json(); + expect(body.status).toBe("OK"); + expect(Array.isArray(body.data)).toBe(true); + expect(body.data.length).toBe(0); + + // Adjuntar evidencia (id de la media y la respuesta) + const info = test.info(); + await attachSummary(info, "TC-MED-GET-001.1 - Response Evidence", { + requestedUrl: `/api/media?all=false&id=${unpublishedMedia._id}`, + status: res.status(), + mediaId: unpublishedMedia._id, + body: body, + }); + + logger.info( + `📡 GET /api/media?all=false&id=${ + unpublishedMedia._id + } - Status: ${res.status()}, Empty Response: ${body.data.length === 0}` + ); + }); + + test("TC-MED-GET-002: Lista completa de medias (all=true)", async ({ + authRequest, + }) => { + const res = await authRequest.get("/api/media?all=true"); expect(res.ok()).toBeTruthy(); const body = await res.json(); @@ -27,14 +100,83 @@ test.describe("🎬 Media all", () => { ); expect(hasUnpublished).toBe(true); + // Adjuntar resumen al reporte + const info = test.info(); + await attachSummary(info, "TC-MED-GET-002 - Response Summary", { + url: "/api/media?all=true", + status: res.status(), + total: body.data.length, + unpublishedSampleIds: body.data + .filter((m) => m.is_published === false) + .slice(0, 5) + .map((m) => m._id), + }); + logger.info( `📡 GET /api/media all=true - Status: ${res.status()}, Has Unpublished: ${hasUnpublished}` ); }); + + test("TC-MED-GET-002.1: Verificar que media despublicada aparece con all=true", async ({ + authRequest, + }) => { + // 1️⃣ Primero obtenemos una media despublicada específica + const resAll = await authRequest.get("/api/media?all=true"); + expect(resAll.ok()).toBeTruthy(); + + const allBody = await resAll.json(); + const unpublishedMedia = allBody.data.find( + (media) => media.is_published === false + ); + + // Verificar que encontramos una media despublicada + expect( + unpublishedMedia, + "No se encontró una media despublicada para probar" + ).toBeTruthy(); + + // 2️⃣ Intentar obtener esa media específica con all=true + const res = await authRequest.get( + `/api/media?all=true&id=${unpublishedMedia._id}` + ); + expect(res.ok()).toBeTruthy(); + + const body = await res.json(); + expect(body.status).toBe("OK"); + expect(Array.isArray(body.data)).toBe(true); + expect(body.data.length).toBe(1); + + // 3️⃣ Verificar que la media retornada es la correcta y mantiene su estado + const returnedMedia = body.data[0]; + expect(returnedMedia._id).toBe(unpublishedMedia._id); + expect(returnedMedia.is_published).toBe(false); + + // Adjuntar evidencia completa (id y respuesta truncada) + const info = test.info(); + await attachSummary(info, "TC-MED-GET-002.1 - Response Evidence", { + requestedUrl: `/api/media?all=true&id=${unpublishedMedia._id}`, + status: res.status(), + expectedId: unpublishedMedia._id, + returned: { + id: returnedMedia._id, + is_published: returnedMedia.is_published, + title: returnedMedia.title, + }, + fullBody: body, + }); + + logger.info( + `📡 GET /api/media?all=true&id=${ + unpublishedMedia._id + } - Status: ${res.status()}, Media Found: ${returnedMedia._id}` + ); + }); }); test.describe("🎬 Filtro Media without_category", () => { - test("without_category=true - sin categorías", async ({ authRequest }) => { + test("TC-MED-GET-003: Filtrado de medias sin categorías", async ({ + authRequest, + }) => { const res = await authRequest.get("/api/media?without_category=true"); expect(res.ok()).toBeTruthy(); @@ -52,7 +194,7 @@ test.describe("🎬 Filtro Media without_category", () => { expect(allWithoutCategory).toBe(true); }); - test("without_category=false - algunas medias con categorías", async ({ + test("TC-MED-GET-004: Filtrado de medias con categorías", async ({ authRequest, }) => { const res = await authRequest.get("/api/media?without_category=false"); @@ -72,8 +214,8 @@ test.describe("🎬 Filtro Media without_category", () => { }); }); -test.describe("🎬 Get filtro logico sin categoria", () => { - test("debe retornar vacio por que el id tiene categoria y pasamos without_category=true", async ({ +test.describe("🎬 Get filtro lógico sin categoria", () => { + test("TC-MED-GET-005: Validación de filtro without_category con ID específico", async ({ authRequest, }) => { // 1️⃣ Primero obtenemos todas las medias (sin filtros) diff --git a/tests/media/update.test.js b/tests/media/update.test.js index 41039d3..b33b4b1 100644 --- a/tests/media/update.test.js +++ b/tests/media/update.test.js @@ -59,3 +59,70 @@ test.describe("Media - Actualización (POST /api/media/{id})", () => { expect(["OK", "ERROR"]).toContain(updBody.status); }); }); + +// Bloque de pruebas para metas: GET / DELETE / TRANSCODE +test.describe("Media - Metas (GET/DELETE/TRANSCODE)", () => { + test("GET-META-OK-01 - Obtener metas correctamente", async ({ + authRequest, + }) => { + // Buscar una media que ya tenga metas en el sistema + const resAll = await authRequest.get("/api/media?all=true&limit=50"); + expect(resAll.ok()).toBeTruthy(); + const allBody = await resAll.json(); + + const mediaWithMeta = allBody.data.find( + (m) => Array.isArray(m.meta) && m.meta.length > 0 + ); + expect( + mediaWithMeta, + "No se encontró media con metas para probar" + ).toBeTruthy(); + + const media_id = mediaWithMeta._id; + const res = await authRequest.get(`/api/media/${media_id}/meta`); + expect(res.status()).toBe(200); + + const body = await res.json(); + expect(body.status).toBe("OK"); + // La API puede devolver { data: { meta: [...] } } o { data: [...] } + const metas = body.data?.meta ?? body.data; + expect(Array.isArray(metas)).toBe(true); + expect(metas.length).toBeGreaterThan(0); + }); + + test("GET-META-ERR-01 - Media no existente", async ({ authRequest }) => { + const res = await authRequest.get( + "/api/media/666666666666666666666666/meta" + ); + const body = await res.json(); + expect(res.status()).toBe(200); + expect(body.status).toBe("ERROR"); + expect(body.data).toBe("NOT_FOUND"); + }); + + test("DEL-META-OK-01 - Eliminar meta NO original", async ({ + authRequest, + }) => { + // Buscar una media que tenga al menos una meta no original + const resAll = await authRequest.get("/api/media?all=true&limit=50"); + expect(resAll.ok()).toBeTruthy(); + const allBody = await resAll.json(); + + const target = allBody.data.find( + (m) => Array.isArray(m.meta) && m.meta.some((mm) => !mm.is_original) + ); + expect( + target, + "No se encontró media con meta no original para eliminar" + ).toBeTruthy(); + + const media_id = target._id; + const metaObj = target.meta.find((mm) => !mm.is_original); + const meta_no_original_id = metaObj._id ?? metaObj.id ?? metaObj; + + const delRes = await authRequest.delete( + `/api/media/${media_id}/meta/${meta_no_original_id}` + ); + expect(delRes.status()).toBe(200); + }); +});