GESTORA API — Guía del Tester

Esta página está pensada para alguien que nunca probó una API antes. Vas a encontrar, endpoint por endpoint, qué hace, si necesita un token, si hay que mandarle datos (body) y qué cosas conviene probar para encontrar errores. El objetivo es que puedas romper la API para ayudarnos a encontrar fallas.

¿Qué es esto y qué voy a estar probando?

GESTORA es una API para administrar finanzas personales: permite registrar ingresos (los que cobrás cada mes y los eventuales) y gastos (los fijos y los que van saliendo en el mes), agruparlos por categorías y consumos (por ejemplo "Sueldo", "Supermercado", "Netflix"), y sacar reportes: si un mes terminaste con superávit o déficit, cómo fue comparado con otro mes, y cuáles fueron los 10 gastos más grandes de un mes.

Todo se maneja por usuario: cada persona sólo puede ver y modificar sus propios datos. Para identificarse, la API usa un token (una cadena larga) que se obtiene al hacer login y se manda en cada request siguiente.

Links útiles dentro de la API
· Swagger UI interactivo: /docs
· ReDoc (misma info, otro formato): /redoc
· Especificación OpenAPI cruda: /openapi.json

Preparar Insomnia

Seguí estos pasos una sola vez; después sirven para todas las pruebas.

1. Abrí Insomnia y creá un nuevo proyecto (o Workspace). Adentro, creá una Request Collection.
2. Dentro de la colección, creá un Environment (menú superior izquierdo → el nombre del environment). Agregá dos variables:
{
  "base_url": "http://127.0.0.1:8000",
  "token": ""
}
Después de hacer login te vas a copiar el access_token que devuelve la API y lo vas a pegar en la variable token.
3. Cuando un endpoint pida body JSON, en Insomnia hacé: pestaña Body → seleccioná JSON, y pegá el ejemplo de body que aparece en cada endpoint de esta página.
4. Cuando un endpoint pida token, en Insomnia hacé: pestaña Auth → seleccioná Bearer Token → pegá {{ _.token }} (para usar la variable del environment). Una alternativa manual es agregar en la pestaña Headers: Authorization: Bearer <tu-token>.
5. La URL de cada request se arma como {{ _.base_url }}/api/.... Por ejemplo, para login sería {{ _.base_url }}/api/auth/login.

Flujo recomendado de testing

Algunos endpoints dependen de que exista información previa (no podés crear un ingreso fijo sin tener antes un registro mensual y un consumo). Te recomiendo probar en este orden:

  1. Registrar un usuario nuevo.
  2. Login para obtener el token y guardarlo en la variable token del environment.
  3. Crear tu perfil (una vez por usuario).
  4. Crear al menos 2 categorías: una de tipo ingreso (ej. "Sueldo") y una de tipo gasto (ej. "Supermercado").
  5. Crear consumos vinculados a esas categorías (ej. "Salario mensual" en la categoría "Sueldo").
  6. Crear uno o más registros mensuales (año/mes). Sin esto no podés cargar movimientos.
  7. Cargar ingresos y gastos (fijos y/o eventuales) eligiendo el registro mensual y el consumo.
  8. Pedir reportes: balance del mes, comparativa entre dos meses, top 10 gastos.
Importante: cada vez que intentes acceder a un recurso que NO es tuyo (por ejemplo, pedir una categoría de otro usuario con tu token), la API te va a devolver 404 "no encontrado" en lugar de 403. Esto es adrede, para no revelar qué IDs existen en el sistema.

Códigos de respuesta que vas a ver

CódigoSignificadoCuándo aparece
200OKTodo salió bien (GET, PATCH, DELETE).
201CreadoUn POST creó un recurso nuevo.
400Datos inválidosEl body tiene errores de formato o reglas (ej. email mal escrito).
401No autenticadoFalta el token, o está vencido o mal armado.
404No encontradoEl recurso no existe o no te pertenece.
409ConflictoYa existe algo igual (ej. email duplicado, mes/año ya registrado).
422Body mal formadoFaltan campos requeridos o los tipos están mal (ej. mandás texto donde va un número).
500Error internoProblema del servidor. Si ves esto, reportalo con el body y los pasos que hiciste.

Registrar un usuario nuevo

POST /api/users/register BODY SIN TOKEN

Crea una cuenta con email y contraseña. Es el primer paso antes de cualquier cosa.

Body JSON

{
  "email": "tester@gestora.local",
  "password": "Prueba123"
}

Reglas de password

  • Mínimo 8 caracteres.
  • Al menos 1 letra mayúscula.
  • Al menos 1 número.

Respuestas posibles

  • 201 devuelve el usuario creado (sin la contraseña).
  • 400 email mal formado y/o contraseña débil (los errores vienen agrupados).
  • 409 ese email ya existe.

Qué debe hacer y qué probar

Debería crear un usuario si los datos son válidos y rechazar cualquier combinación inválida.

  1. Caso feliz: email bien escrito + contraseña que cumple las reglas → debería devolver 201.
  2. Email sin "@" o sin dominio → debería devolver 400 con errors.email.
  3. Contraseña corta / sin mayúscula / sin número → debería devolver 400 con errors.password.
  4. Email y contraseña inválidos al mismo tiempo → 400 con los dos errores juntos.
  5. Repetir el mismo email → debería devolver 409.
  6. Mandar el body sin el campo password → debería devolver 422.
  7. Probá con la contraseña Abcdefg1. ¿Pasa? Debería (8 caracteres, 1 mayús, 1 número).
  8. Probá con ABCDEFG1 (todo mayúsculas, sin minúsculas). Mirá si la API lo acepta: según las reglas, sí lo acepta porque no exigimos minúsculas. Si creés que debería exigirlas, anotalo como posible mejora.

Iniciar sesión (login)

POST /api/auth/login BODY SIN TOKEN

Verifica las credenciales y devuelve un access_token que vale por 24 horas. Ese token lo vas a pegar en la variable token de Insomnia y se va a usar en todos los endpoints protegidos.

Body JSON

{
  "email": "tester@gestora.local",
  "password": "Prueba123"
}

Respuesta si el login es correcto

{
  "access_token": "eyJhbGciOi... (muy largo)",
  "token_type": "Bearer",
  "user": {
    "id_usuario": 1,
    "email": "tester@gestora.local",
    "activo": true
  }
}

Respuestas posibles

  • 200 login OK, devuelve el token.
  • 401 credenciales inválidas (email no existe, password no coincide o cuenta inactiva).

Qué debe hacer y qué probar

  1. Login correcto → 200 con un access_token. Copialo a la variable token.
  2. Email correcto pero password incorrecta → 401 "Credenciales inválidas".
  3. Email que no existe → 401. El mensaje debe ser exactamente el mismo que el caso anterior (es a propósito: no queremos que se pueda averiguar si un email está registrado midiendo respuestas).
  4. Body vacío o sin email/password422.

Ver mi usuario autenticado

GET /api/users/me SIN BODY TOKEN

Devuelve el usuario que corresponde al token que mandaste. Es el endpoint más simple para validar que tu token está bien configurado.

Respuestas posibles

  • 200 devuelve tu id_usuario, email, activo, fecha_creacion.
  • 401 sin token, token inválido o token vencido.

Qué debe hacer y qué probar

  1. Con token válido → 200 y tu info.
  2. Sin header Authorization401.
  3. Con Authorization: Bearer cualquier-cosa inventada → 401.
  4. Con un token válido pero muy viejo (más de 24 horas) → 401.

Crear mi perfil

POST /api/profiles BODY TOKEN

Cada usuario tiene un solo perfil. Si ya existe, este endpoint falla.

Body JSON

{
  "nombre_completo": "Ferminito de Prueba",
  "ocupacion": "QA Engineer",
  "avatar_url": "https://ejemplo.com/avatar.png"
}

ocupacion y avatar_url son opcionales. Si mandás avatar_url, tiene que empezar con http:// o https://.

Respuestas posibles

  • 201 perfil creado.
  • 400 nombre_completo vacío o > 100 chars, o avatar_url no parece URL, o ocupacion > 100 chars.
  • 409 ya existe un perfil para este usuario.

Qué probar

  1. Crear con sólo nombre_completo201.
  2. Crear con todos los campos → 201.
  3. Crear una segunda vez con el mismo usuario → 409.
  4. Mandar nombre_completo: ""400.
  5. Mandar avatar_url: "no-es-url"400.

Ver mi perfil

GET /api/profiles/me SIN BODY TOKEN

Respuestas posibles

  • 200 devuelve el perfil.
  • 404 todavía no creaste tu perfil.
  • 401 sin token.

Qué probar

  1. Pedirlo antes de crearlo → 404.
  2. Pedirlo después de crearlo → 200.

Editar mi perfil

PATCH /api/profiles/me BODY TOKEN

Podés mandar uno, dos o los tres campos. Los que no mandes se quedan como estaban.

Body JSON (ejemplo)

{
  "ocupacion": "Data Scientist"
}

Respuestas posibles

  • 200 devuelve el perfil actualizado.
  • 400 mandaste el body vacío {}, o un campo no cumple las reglas.
  • 404 no tenés perfil creado.

Qué probar

  1. Cambiar solo ocupacion y verificar que nombre_completo y avatar_url no cambien.
  2. Mandar {} (nada para actualizar) → 400.
  3. Mandar avatar_url: "no-es-url"400.
  4. Patch con token válido pero sin haber creado el perfil → 404.

Borrar mi perfil

DELETE /api/profiles/me SIN BODY TOKEN

Respuestas posibles

  • 200 {"deleted": true}.
  • 404 no tenías perfil.

Qué probar

  1. Borrar después de crear → 200. Después GET /api/profiles/me debería dar 404.
  2. Borrar dos veces seguidas → primera 200, segunda 404.

Crear categoría

POST /api/categories BODY TOKEN

Las categorías agrupan tus ingresos o gastos. El tipo define si esa categoría sirve para ingresos o para gastos.

Body JSON

{
  "nombre": "Sueldo",
  "tipo": "ingreso",
  "color": "#22c55e"
}

Reglas

  • nombre: entre 2 y 100 caracteres.
  • tipo: exactamente "ingreso" o "gasto".
  • color: opcional, formato hex (#RGB o #RRGGBB).

Qué probar

  1. Crear una categoría "Sueldo" tipo ingreso → 201.
  2. Crear una categoría "Supermercado" tipo gasto → 201.
  3. Mandar tipo: "otro"400.
  4. Mandar color: "verde"400.
  5. Mandar nombre: "X" (1 char) → 400.

Listar mis categorías

GET /api/categories SIN BODY TOKEN

Qué probar

  1. Sin categorías creadas → 200 con array vacío [].
  2. Con 2 categorías creadas → 200 con array de 2 elementos.
  3. Con un segundo usuario: sus categorías no deben aparecer acá.

Ver una categoría

GET /api/categories/{id_categoria} SIN BODY TOKEN

Reemplazá {id_categoria} por el número del ID que te devolvió al crearla (ej. /api/categories/1).

Qué probar

  1. ID de una categoría tuya → 200.
  2. ID inexistente (ej. 999999) → 404.
  3. ID de una categoría de otro usuario → 404 (no 403, a propósito).

Editar categoría

PATCH /api/categories/{id_categoria} BODY TOKEN

Body JSON (ejemplo, campos opcionales)

{
  "nombre": "Sueldo bruto",
  "color": "#0ea5e9"
}

Qué probar

  1. Cambiar sólo color200.
  2. Cambiar tipo de "ingreso" a "gasto" (la API lo permite, pero revisá si tiene sentido cuando la categoría ya tiene consumos asociados — posible mejora a reportar).
  3. Body vacío {}400.
  4. ID inexistente → 404.

Borrar categoría

DELETE /api/categories/{id_categoria} SIN BODY TOKEN

Qué probar

  1. Borrar una categoría que no tenés consumos asociados → 200.
  2. Intentar borrar una categoría que tiene consumos encima → puede fallar con 500 por restricción de clave foránea. Si eso pasa, reportalo; lo ideal es devolver 409 o borrar en cascada.
  3. Borrar categoría de otro usuario → 404.

Crear consumo

POST /api/consumos BODY TOKEN

Un consumo es un "ítem" con nombre que viveá dentro de una categoría. Ejemplo: en la categoría "Supermercado" podés tener los consumos "Coto centro" y "Día del barrio". Después, cuando cargues un gasto de supermercado, elegís uno de esos consumos.

Body JSON

{
  "id_categoria": 1,
  "nombre": "Salario mensual",
  "descripcion": "Sueldo de mi trabajo principal"
}

Reglas

  • nombre: entre 2 y 150 caracteres.
  • descripcion: opcional, texto libre.
  • id_categoria: debe ser una categoría que te pertenece.

Qué probar

  1. Crear un consumo con categoría propia → 201.
  2. Usar un id_categoria inexistente → 404.
  3. Usar un id_categoria de otro usuario → 404 (seguridad).
  4. Nombre de 1 sólo carácter → 400.

Listar mis consumos

GET /api/consumos SIN BODY TOKEN

Qué probar

  1. Sin consumos → 200 y [].
  2. Después de crear 2 o 3 consumos → 200 con la lista correcta.
  3. Otro usuario no debe ver tus consumos.

Ver un consumo

GET /api/consumos/{id_consumo} SIN BODY TOKEN

Qué probar

  1. ID tuyo → 200.
  2. ID inexistente o de otro usuario → 404.

Editar consumo

PATCH /api/consumos/{id_consumo} BODY TOKEN

Body JSON (campos opcionales)

{
  "nombre": "Salario nuevo",
  "descripcion": "Con aumento",
  "id_categoria": 2
}

Qué probar

  1. Cambiar sólo descripcion200.
  2. Intentar mover el consumo a una categoría de otro usuario → 404.
  3. Body vacío {}400.

Borrar consumo

DELETE /api/consumos/{id_consumo} SIN BODY TOKEN

Qué probar

  1. Borrar consumo sin movimientos asociados → 200.
  2. Borrar consumo que ya se usó en un ingreso/gasto → puede fallar con 500 por FK; si pasa, reportalo.
  3. Borrar consumo de otro usuario → 404.

Crear registro mensual

POST /api/monthly-records BODY TOKEN

Un registro mensual representa un mes concreto (año + mes) en el que vas a cargar ingresos y gastos. Por ejemplo, "marzo de 2026" es un registro. Sin esto no podés crear ningún movimiento.

Body JSON

{
  "anio": 2026,
  "mes": 3
}

Reglas

  • anio: entre 2000 y 2100.
  • mes: entre 1 y 12.
  • No puede haber dos registros con el mismo anio y mes para el mismo usuario.

Qué probar

  1. Crear marzo 2026 → 201. Anotá el id_registro_mensual que devuelve.
  2. Intentar crear otra vez marzo 2026 (mismo usuario) → 409.
  3. mes: 13 o mes: 0400.
  4. anio: 1999 o anio: 2200400.
  5. Mandar mes como texto ("marzo") → 422.

Listar mis registros mensuales

GET /api/monthly-records SIN BODY TOKEN

Los devuelve ordenados del más reciente al más viejo.

Qué probar

  1. Crear marzo, abril y enero de 2026. El orden debería ser: abril, marzo, enero.

Ver un registro mensual

GET /api/monthly-records/{id_registro} SIN BODY TOKEN

Borrar registro mensual

DELETE /api/monthly-records/{id_registro} SIN BODY TOKEN

Si tenía movimientos asociados, es probable que el borrado falle con 500 por FK. Reportalo.


Ingresos y gastos (movimientos)

Hay 4 tipos de movimientos. Todos se crean de forma muy parecida:

En todos, vas a necesitar tener antes:

  1. Un registro mensual del mes que corresponda.
  2. Un consumo cuya categoría sea del tipo correcto:
    • Para ingresos, el consumo tiene que estar en una categoría tipo ingreso.
    • Para gastos, en una categoría tipo gasto.
Test clave (seguridad/integridad): si intentás crear un ingreso usando un consumo de categoría tipo gasto, la API debe rechazarlo con 404. Lo mismo al revés. Probalo.

Campos comunes de todos los movimientos

Ingresos fijos

POST /api/incomes/fixed BODY TOKEN
{
  "id_registro_mensual": 1,
  "id_consumo": 1,
  "monto": 500000,
  "observaciones": "Sueldo base"
}

Qué probar

  1. Caso feliz con datos válidos → 201.
  2. monto: 0 o negativo → 400.
  3. id_consumo de categoría gasto404 "Consumo no encontrado o no es de tipo ingreso".
  4. id_registro_mensual inexistente o de otro usuario → 404.
GET /api/incomes/fixed SIN BODY TOKEN

Lista todos tus ingresos fijos de todos los meses.

GET /api/incomes/fixed/{id_ingreso_fijo} SIN BODY TOKEN
PATCH /api/incomes/fixed/{id_ingreso_fijo} BODY TOKEN
{
  "monto": 550000
}

Se puede cambiar id_consumo, monto y observaciones. Todos opcionales.

Qué probar

  1. Cambiar sólo el monto → 200.
  2. Cambiar el consumo por uno de categoría gasto → 404.
  3. Body vacío → 400.
DELETE /api/incomes/fixed/{id_ingreso_fijo} SIN BODY TOKEN

Ingresos eventuales

Misma idea que los fijos, pero llevan fecha.

POST /api/incomes/occasional BODY TOKEN
{
  "id_registro_mensual": 1,
  "id_consumo": 1,
  "monto": 50000,
  "fecha": "2026-03-15T12:00:00",
  "observaciones": "Bono trimestral"
}

Qué probar

  1. Fecha con formato ISO válido → 201.
  2. Fecha en formato raro (ej. "15/03/2026") → 422.
  3. Fecha fuera del mes del registro (ej. "2026-07-01" si el registro es de marzo) → actualmente la API lo acepta. Anotalo como posible mejora: idealmente debería validar que la fecha caiga dentro del mes.
  4. Monto 0 → 400.
  5. Consumo de categoría gasto → 404.
GET /api/incomes/occasional SIN BODY TOKEN
GET /api/incomes/occasional/{id_ingreso_eventual} SIN BODY TOKEN
PATCH /api/incomes/occasional/{id_ingreso_eventual} BODY TOKEN
{
  "monto": 75000,
  "fecha": "2026-03-20T09:30:00"
}
DELETE /api/incomes/occasional/{id_ingreso_eventual} SIN BODY TOKEN

Gastos fijos

Idéntico a ingresos fijos pero con categoría gasto.

POST /api/expenses/fixed BODY TOKEN
{
  "id_registro_mensual": 1,
  "id_consumo": 2,
  "monto": 200000,
  "observaciones": "Alquiler departamento"
}

Qué probar

  1. Consumo de categoría ingreso404.
  2. Monto negativo → 400.
GET /api/expenses/fixed SIN BODY TOKEN
GET /api/expenses/fixed/{id_gasto_fijo} SIN BODY TOKEN
PATCH /api/expenses/fixed/{id_gasto_fijo} BODY TOKEN
DELETE /api/expenses/fixed/{id_gasto_fijo} SIN BODY TOKEN

Gastos eventuales

Los gastos del día a día, con fecha.

POST /api/expenses/occasional BODY TOKEN
{
  "id_registro_mensual": 1,
  "id_consumo": 3,
  "monto": 8500,
  "fecha": "2026-03-21T20:00:00",
  "observaciones": "cena con amigos"
}

Qué probar

  1. Cargar 15 gastos eventuales con montos distintos y después pedir el top 10 (ver sección Reportes). Deberían aparecer los 10 más altos, ordenados de mayor a menor.
  2. Dos gastos exactamente del mismo monto → ambos deben aparecer si están en el top; no debe "saltear" uno.
GET /api/expenses/occasional SIN BODY TOKEN
GET /api/expenses/occasional/{id_gasto_eventual} SIN BODY TOKEN
PATCH /api/expenses/occasional/{id_gasto_eventual} BODY TOKEN
DELETE /api/expenses/occasional/{id_gasto_eventual} SIN BODY TOKEN

Reporte: balance mensual

GET /api/reports/balance/{anio}/{mes} SIN BODY TOKEN

Calcula el balance del mes indicado: suma todos los ingresos (fijos + eventuales), todos los gastos (fijos + eventuales) y te dice si tuviste superávit, déficit o equilibrio.

Ejemplo

GET /api/reports/balance/2026/3

{
  "anio": 2026,
  "mes": 3,
  "total_ingresos_fijos": 550000,
  "total_ingresos_eventuales": 50000,
  "total_ingresos": 600000,
  "total_gastos_fijos": 200000,
  "total_gastos_eventuales": 58900,
  "total_gastos": 258900,
  "balance": 341100,
  "estado": "superavit"
}

Qué probar

  1. Mes con más ingresos que gastos → "estado": "superavit".
  2. Mes con más gastos que ingresos → "estado": "deficit".
  3. Mes con ingresos y gastos iguales → "estado": "equilibrio".
  4. Verificá a mano que total_ingresos = ingresos_fijos + ingresos_eventuales. Lo mismo con gastos. Y balance = total_ingresos - total_gastos.
  5. Mes que no tiene registro mensual creado → 404.
  6. Mes con registro pero sin movimientos → todos los totales en 0 y "estado": "equilibrio".

Reporte: comparativa entre dos meses

GET /api/reports/compare/{anio_base}/{mes_base}/vs/{anio_cmp}/{mes_cmp} SIN BODY TOKEN

Compara dos meses: el primero es la base, el segundo es el comparado. Devuelve el balance de cada uno y la variación porcentual del segundo respecto al primero.

Ejemplo

GET /api/reports/compare/2026/3/vs/2026/4

Si en marzo gastaste $100.000 y en abril $80.000, gastos_pct debe dar -20 (bajaron 20%).

Qué probar

  1. Mes base con total_ingresos = 0ingresos_pct debe ser null (no se puede dividir por 0).
  2. Comparar dos meses iguales → todas las variaciones deben ser 0 o muy cerca.
  3. Alguno de los dos meses sin registro → 404 con detalle claro de cuál falta.
  4. Cruzar años (/2025/12/vs/2026/1) → debería funcionar normal.

Reporte: top 10 gastos eventuales

GET /api/reports/top-expenses/{anio}/{mes} SIN BODY TOKEN

Devuelve los 10 gastos eventuales más grandes del mes indicado, ordenados de mayor a menor. Si hay menos de 10, devuelve todos. No incluye gastos fijos.

Qué probar

  1. Mes con 5 gastos eventuales → devuelve los 5, ordenados del más caro al más barato.
  2. Mes con 15 gastos eventuales → devuelve los 10 más grandes.
  3. Mes sin gastos eventuales → devuelve [].
  4. Mes sin registro mensual → 404.
  5. Dos gastos con el mismo monto → ambos deben estar si entran en el top, no debe saltarse uno.
  6. Confirmá que no aparezcan gastos fijos (son otro recurso; estos endpoints son sólo "ocasionales").