Files
kakebo/app/api/tests.py
Andros Fenollosa 19f4e84a30 Remove user FK from all models, add goals and promises
- Remove user ForeignKey from all 7 models (single-user app)
- Update all views, handlers, forms, admin, API, seed, and tests
- Add MonthlyGoal model with goals and promises sections
- Goals/promises: add, toggle (strikethrough), delete via LiveView
2026-03-18 15:18:50 +01:00

163 lines
4.5 KiB
Python

import os
from django.test import TestCase, override_settings
from rest_framework.test import APIClient
from app.expenses.models import Category, Subcategory
TEST_TOKEN = "test-token-123"
@override_settings()
class APITestCase(TestCase):
def setUp(self):
os.environ["API_TOKEN"] = TEST_TOKEN
self.client = APIClient()
self.category = Category.objects.create(name="Survival", order=1)
self.subcategory = Subcategory.objects.create(
name="Food", order=1, category=self.category
)
self.other_sub = Subcategory.objects.create(
name="Other", order=99, category=self.category
)
def tearDown(self):
if "API_TOKEN" in os.environ:
del os.environ["API_TOKEN"]
def auth_headers(self):
return {"HTTP_AUTHORIZATION": f"Bearer {TEST_TOKEN}"}
# --- Authentication ---
def test_no_token_returns_403(self):
response = self.client.get("/api/categories/")
self.assertEqual(response.status_code, 403)
def test_invalid_token_returns_403(self):
response = self.client.get(
"/api/categories/", HTTP_AUTHORIZATION="Bearer wrong-token"
)
self.assertEqual(response.status_code, 403)
def test_valid_token_returns_200(self):
response = self.client.get("/api/categories/", **self.auth_headers())
self.assertEqual(response.status_code, 200)
# --- Root ---
def test_root_returns_links(self):
response = self.client.get("/api/", **self.auth_headers())
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data["type"], "Success")
self.assertIn("categories", data["_links"])
self.assertIn("expenses", data["_links"])
# --- Categories ---
def test_get_categories(self):
response = self.client.get("/api/categories/", **self.auth_headers())
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data["type"], "Success")
self.assertEqual(len(data["data"]), 1)
self.assertEqual(data["data"][0]["name"], "Survival")
self.assertIn("subcategories", data["data"][0])
self.assertEqual(len(data["data"][0]["subcategories"]), 2)
self.assertIn("_links", data)
# --- Subcategories ---
def test_get_subcategories(self):
response = self.client.get(
f"/api/categories/{self.category.id}/subcategories/",
**self.auth_headers(),
)
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data["type"], "Success")
self.assertEqual(len(data["data"]), 2)
self.assertIn("_links", data)
def test_get_subcategories_invalid_category(self):
response = self.client.get(
"/api/categories/9999/subcategories/",
**self.auth_headers(),
)
self.assertEqual(response.status_code, 404)
data = response.json()
self.assertEqual(data["type"], "ResourceError")
# --- Expenses ---
def test_create_expense(self):
response = self.client.post(
"/api/expenses/",
{
"concept": "Groceries",
"amount": "42.50",
"category": self.category.id,
"subcategory": self.subcategory.id,
},
format="json",
**self.auth_headers(),
)
self.assertEqual(response.status_code, 201)
data = response.json()
self.assertEqual(data["type"], "Success")
self.assertEqual(data["data"]["concept"], "Groceries")
self.assertEqual(data["data"]["amount"], "42.50")
self.assertIn("_links", data)
def test_create_expense_missing_fields(self):
response = self.client.post(
"/api/expenses/",
{"concept": "Test"},
format="json",
**self.auth_headers(),
)
self.assertEqual(response.status_code, 400)
data = response.json()
self.assertEqual(data["type"], "ParametersError")
self.assertTrue(len(data["errors"]) > 0)
def test_create_expense_mismatched_subcategory(self):
other_cat = Category.objects.create(name="Culture", order=2)
other_sub = Subcategory.objects.create(
name="Books", order=1, category=other_cat
)
response = self.client.post(
"/api/expenses/",
{
"concept": "Wrong combo",
"amount": "10.00",
"category": self.category.id,
"subcategory": other_sub.id,
},
format="json",
**self.auth_headers(),
)
self.assertEqual(response.status_code, 400)
data = response.json()
self.assertEqual(data["type"], "ParametersError")
def test_create_expense_invalid_amount(self):
response = self.client.post(
"/api/expenses/",
{
"concept": "Test",
"amount": "not-a-number",
"category": self.category.id,
"subcategory": self.subcategory.id,
},
format="json",
**self.auth_headers(),
)
self.assertEqual(response.status_code, 400)
def test_expenses_get_not_allowed(self):
response = self.client.get("/api/expenses/", **self.auth_headers())
self.assertEqual(response.status_code, 405)