// supabase/functions/ai-structured/ai_structured.test.ts /// /// /// import { assert, assertEquals } from "jsr:@std/assert@1"; import "jsr:@std/dotenv/load"; import { createClient, type SupabaseClient } from "npm:@supabase/supabase-js@2"; const SUPABASE_URL = Deno.env.get("SUPABASE_URL") ?? ""; const SUPABASE_ANON_KEY = Deno.env.get("SUPABASE_ANON_KEY") ?? ""; // (o PUBLISHABLE_KEY si así lo nombraste) const EMAIL = Deno.env.get("TEST_EMAIL") ?? "guillermo.arrieta@lasalle.mx"; const PASSWORD = Deno.env.get("TEST_PASSWORD") ?? "admin"; const options = { auth: { autoRefreshToken: false, persistSession: false, detectSessionInUrl: false, }, }; function mustEnv() { if (!SUPABASE_URL) throw new Error("SUPABASE_URL is required"); if (!SUPABASE_ANON_KEY) throw new Error("SUPABASE_ANON_KEY is required"); } async function getAuthedClient(): Promise< { client: SupabaseClient; accessToken: string } > { mustEnv(); const client = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, options); const { data, error } = await client.auth.signInWithPassword({ email: EMAIL, password: PASSWORD, }); if (error) throw new Error("Sign-in failed: " + error.message); const accessToken = data.session?.access_token; if (!accessToken) { throw new Error("No access_token returned from signInWithPassword"); } return { client, accessToken }; } Deno.test("ai_generate_plan creates plan and returns structured data", async () => { const { client, accessToken } = await getAuthedClient(); const { data: carrera, error: carreraError } = await client .from("carreras") .select("id") .limit(1) .single(); if (carreraError) { throw new Error("Failed to fetch carrera: " + carreraError.message); } if (!carrera) throw new Error("No carrera found"); const datosBasicos = { nombrePlan: "Plan de estudios de Ingeniería en Sistemas Computacionales", carreraId: carrera.id, nivel: "Licenciatura", tipoCiclo: "Semestre", // important: normalizes to "Semestre" numCiclos: 8, }; const { data, error } = await client.functions.invoke("ai_generate_plan", { headers: { Authorization: `Bearer ${accessToken}`, }, body: { datosBasicos }, }); if (error) throw new Error("Invoke failed: " + error.message); assert(data, "Expected data from function"); // Top-level result assertEquals(data.ok, true); // `plan` (DB record) assert(typeof data.plan?.id === "string"); assertEquals(data.plan.nombre, datosBasicos.nombrePlan); assertEquals(data.plan.nivel, "Licenciatura"); assertEquals(data.plan.tipo_ciclo, "Semestre"); assertEquals(data.plan.numero_ciclos, datosBasicos.numCiclos); // `plan_datos` (normalized structured payload) assert(typeof data.plan_datos === "object"); assertEquals(data.plan_datos.nombre, datosBasicos.nombrePlan); assertEquals(data.plan_datos.nivel, "Licenciatura"); assertEquals( data.plan_datos.total_de_ciclos_del_plan_de_estudios, String(datosBasicos.numCiclos), ); assertEquals(data.plan_datos.duracion_del_ciclo_escolar, "16"); // Semestre -> 16 }); Deno.test("ai_generate_plan (multipart with files)", async () => { const { client, accessToken } = await getAuthedClient(); const { data: carrera, error: carreraError } = await client .from("carreras") .select("id") .limit(1) .single(); if (carreraError) { throw new Error("Failed to fetch carrera: " + carreraError.message); } if (!carrera) throw new Error("No carrera found"); const datosBasicos = { nombrePlan: "Plan de estudios generado con referencias", carreraId: carrera.id, nivel: "Maestría", tipoCiclo: "Semestre", numCiclos: 4, }; const iaConfig = { descripcionEnfoque: "Programa de postgrado enfocado en tecnología", notasAdicionales: "Considerar tendencias actuales en IA", }; // Create a simple test file const fileContent = "Sample curriculum reference document"; const file = new File([fileContent], "reference.txt", { type: "text/plain" }); const payload = { datosBasicos, iaConfig, }; const fd = new FormData(); fd.append("payload", JSON.stringify(payload)); fd.append("files", file); const { data, error } = await client.functions.invoke("ai_generate_plan", { headers: { Authorization: `Bearer ${accessToken}`, }, method: "POST", body: fd, }); if (error) throw new Error("Invoke failed: " + error.message); assert(data, "Expected data from function"); assertEquals(data.ok, true); assert(data.plan); assertEquals(data.plan.nombre, datosBasicos.nombrePlan); assertEquals(data.plan.nivel, "Maestría"); assertEquals(data.plan.numero_ciclos, datosBasicos.numCiclos); assert(data.references); assertEquals(data.references.tempFilesCount, 1); });