Files
richardtekula f78c2199e1 Fix: Rename directory to remove & character causing shell issues
Renamed ebook_backend&admin_panel to ebook_backend_admin_panel
  The & character was being interpreted by shell as background
  process operator, causing 'Dockerfile not found' errors in Coolify.
2025-11-11 17:06:39 +01:00

406 lines
16 KiB
Python

import pytest
from unittest.mock import patch, MagicMock
from fastapi import HTTPException
class TestCouponRoutes:
"""Test cases for coupon management routes"""
def test_generate_single_code_unauthorized(self, client):
"""Test generate single code without authentication"""
response = client.post("/generate", data={"mode": "single", "count": 1})
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Unauthorized"
def test_generate_single_code_success(self, client, auth_headers):
"""Test successful single code generation"""
with patch('routes.auth.generate_coupon') as mock_generate:
mock_generate.return_value = "ABC123DEF4"
response = client.post("/generate", data={"mode": "single", "count": 1}, headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["code"] == "ABC123DEF4"
mock_generate.assert_called_once()
def test_generate_bulk_codes_success(self, client, auth_headers):
"""Test successful bulk code generation"""
with patch('routes.auth.generate_coupon') as mock_generate:
mock_generate.side_effect = ["CODE1", "CODE2", "CODE3"]
response = client.post("/generate", data={"mode": "bulk", "count": 3}, headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["codes"] == ["CODE1", "CODE2", "CODE3"]
assert mock_generate.call_count == 3
def test_generate_invalid_mode(self, client, auth_headers):
"""Test code generation with invalid mode"""
response = client.post("/generate", data={"mode": "invalid", "count": 1}, headers=auth_headers)
assert response.status_code == 400
data = response.json()
assert data["detail"] == "Invalid mode"
def test_generate_bulk_zero_count(self, client, auth_headers):
"""Test bulk generation with zero count"""
response = client.post("/generate", data={"mode": "bulk", "count": 0}, headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["codes"] == []
def test_list_codes_pagination(self, client, sample_coupons):
"""Test coupon listing with pagination"""
response = client.get("/list?page=1&limit=2")
assert response.status_code == 200
data = response.json()
assert "codes" in data
assert "total" in data
assert "page" in data
assert "limit" in data
assert "total_pages" in data
assert data["page"] == 1
assert data["limit"] == 2
assert data["total"] == 3
assert len(data["codes"]) == 2
def test_list_codes_default_pagination(self, client, sample_coupons):
"""Test coupon listing with default pagination"""
response = client.get("/list")
assert response.status_code == 200
data = response.json()
assert data["page"] == 1
assert data["limit"] == 20
assert len(data["codes"]) == 3
def test_list_codes_empty_database(self, client):
"""Test coupon listing with empty database"""
response = client.get("/list")
assert response.status_code == 200
data = response.json()
assert data["codes"] == []
assert data["total"] == 0
assert data["page"] == 1
assert data["limit"] == 20
assert data["total_pages"] == 0
def test_list_codes_second_page(self, client, sample_coupons):
"""Test coupon listing second page"""
response = client.get("/list?page=2&limit=2")
assert response.status_code == 200
data = response.json()
assert data["page"] == 2
assert data["limit"] == 2
assert len(data["codes"]) == 1 # Only 1 code left on page 2
def test_search_codes_success(self, client, sample_coupons):
"""Test successful code search"""
response = client.get("/search-codes?query=TEST")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["code"] == "TEST123"
assert "used" in data[0]
assert "usage_count" in data[0]
assert "used_at" in data[0]
def test_search_codes_case_insensitive(self, client, sample_coupons):
"""Test case-insensitive code search"""
response = client.get("/search-codes?query=test")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["code"] == "TEST123"
def test_search_codes_partial_match(self, client, sample_coupons):
"""Test partial code search"""
response = client.get("/search-codes?query=123")
assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["code"] == "TEST123"
def test_search_codes_no_results(self, client, sample_coupons):
"""Test code search with no results"""
response = client.get("/search-codes?query=NONEXISTENT")
assert response.status_code == 200
data = response.json()
assert data == []
def test_search_codes_empty_query(self, client, sample_coupons):
"""Test code search with empty query"""
response = client.get("/search-codes?query=")
assert response.status_code == 200
data = response.json()
# Should return all codes when query is empty
assert len(data) == 3
def test_use_code_success(self, client, sample_coupons):
"""Test successful code usage"""
response = client.post("/use-code", json={"code": "TEST123"})
assert response.status_code == 200
data = response.json()
assert data["code"] == "TEST123"
assert "used_at" in data
def test_use_code_case_insensitive(self, client, sample_coupons):
"""Test case-insensitive code usage"""
response = client.post("/use-code", json={"code": "test123"})
assert response.status_code == 200
data = response.json()
assert data["code"] == "TEST123"
def test_use_code_not_found(self, client):
"""Test using non-existent code"""
response = client.post("/use-code", json={"code": "NONEXISTENT"})
assert response.status_code == 404
data = response.json()
assert data["detail"] == "Invalid code"
def test_use_code_already_used(self, client, used_coupon):
"""Test using already used code"""
response = client.post("/use-code", json={"code": "USED123"})
assert response.status_code == 400
data = response.json()
assert data["detail"] == "Coupon already used"
def test_use_code_whitespace_handling(self, client, sample_coupons):
"""Test code usage with whitespace"""
response = client.post("/use-code", json={"code": " TEST123 "})
assert response.status_code == 200
data = response.json()
assert data["code"] == "TEST123"
def test_check_code_success(self, client, sample_coupons):
"""Test successful code check"""
response = client.get("/check-code/TEST123")
assert response.status_code == 200
data = response.json()
assert data["code"] == "TEST123"
assert data["used"] == 0
def test_check_code_case_insensitive(self, client, sample_coupons):
"""Test case-insensitive code check"""
response = client.get("/check-code/test123")
assert response.status_code == 200
data = response.json()
assert data["code"] == "TEST123"
def test_check_code_not_found(self, client):
"""Test checking non-existent code"""
response = client.get("/check-code/NONEXISTENT")
assert response.status_code == 404
data = response.json()
assert data["detail"] == "Code not found"
def test_check_code_whitespace_handling(self, client, sample_coupons):
"""Test code check with whitespace"""
response = client.get("/check-code/ TEST123 ")
assert response.status_code == 200
data = response.json()
assert data["code"] == "TEST123"
def test_verify_coupon_success(self, client, sample_coupons):
"""Test successful coupon verification"""
response = client.post("/verify", json={"code": "TEST123"})
assert response.status_code == 200
data = response.json()
assert data["message"] == "Coupon verified"
assert "used_at" in data
def test_verify_coupon_case_insensitive(self, client, sample_coupons):
"""Test case-insensitive coupon verification"""
response = client.post("/verify", json={"code": "test123"})
assert response.status_code == 200
data = response.json()
assert data["message"] == "Coupon verified"
def test_verify_coupon_not_found(self, client):
"""Test verifying non-existent coupon"""
response = client.post("/verify", json={"code": "NONEXISTENT"})
assert response.status_code == 404
data = response.json()
assert data["detail"] == "Invalid coupon code"
def test_verify_coupon_already_used(self, client, used_coupon):
"""Test verifying already used coupon"""
response = client.post("/verify", json={"code": "USED123"})
assert response.status_code == 400
data = response.json()
assert data["detail"] == "Coupon already used"
def test_verify_coupon_whitespace_handling(self, client, sample_coupons):
"""Test coupon verification with whitespace"""
response = client.post("/verify", json={"code": " TEST123 "})
assert response.status_code == 200
data = response.json()
assert data["message"] == "Coupon verified"
def test_add_code_unauthorized(self, client):
"""Test adding code without authentication"""
code_data = {"code": "NEW123", "usage": 0}
response = client.post("/add-code", json=code_data)
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Unauthorized"
def test_add_code_success(self, client, auth_headers):
"""Test successful code addition"""
code_data = {"code": "NEW123", "usage": 0}
response = client.post("/add-code", json=code_data, headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Code added successfully"
def test_add_code_already_exists(self, client, sample_coupons, auth_headers):
"""Test adding code that already exists"""
code_data = {"code": "TEST123", "usage": 0}
response = client.post("/add-code", json=code_data, headers=auth_headers)
assert response.status_code == 400
data = response.json()
assert data["detail"] == "Code already exists"
def test_add_code_case_normalization(self, client, auth_headers):
"""Test code case normalization during addition"""
code_data = {"code": "new123", "usage": 0}
response = client.post("/add-code", json=code_data, headers=auth_headers)
assert response.status_code == 200
# Verify the code was stored in uppercase
response = client.get("/check-code/NEW123")
assert response.status_code == 200
def test_add_code_negative_usage(self, client, auth_headers):
"""Test adding code with negative usage count"""
code_data = {"code": "NEW123", "usage": -5}
response = client.post("/add-code", json=code_data, headers=auth_headers)
assert response.status_code == 200
# Verify usage count was normalized to 0
response = client.get("/check-code/NEW123")
assert response.status_code == 200
data = response.json()
assert data["used"] == 0
def test_delete_code_unauthorized(self, client):
"""Test deleting code without authentication"""
response = client.delete("/delete-code/TEST123")
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Unauthorized"
def test_delete_code_success(self, client, sample_coupons, auth_headers):
"""Test successful code deletion"""
response = client.delete("/delete-code/TEST123", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Code deleted successfully"
# Verify code is deleted
response = client.get("/check-code/TEST123")
assert response.status_code == 404
def test_delete_code_case_insensitive(self, client, sample_coupons, auth_headers):
"""Test case-insensitive code deletion"""
response = client.delete("/delete-code/test123", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Code deleted successfully"
def test_delete_code_not_found(self, client, auth_headers):
"""Test deleting non-existent code"""
response = client.delete("/delete-code/NONEXISTENT", headers=auth_headers)
assert response.status_code == 404
data = response.json()
assert data["detail"] == "Code not found"
def test_delete_code_whitespace_handling(self, client, sample_coupons, auth_headers):
"""Test code deletion with whitespace"""
response = client.delete("/delete-code/ TEST123 ", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Code deleted successfully"
def test_upload_codes_unauthorized(self, client):
"""Test uploading codes without authentication"""
upload_data = {
"codes": [
{"code": "UPLOAD1", "usage": 0},
{"code": "UPLOAD2", "usage": 0}
]
}
response = client.post("/upload-codes", json=upload_data)
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Unauthorized"
def test_upload_codes_success(self, client, auth_headers):
"""Test successful code upload"""
upload_data = {
"codes": [
{"code": "UPLOAD1", "usage": 0},
{"code": "UPLOAD2", "usage": 1}
]
}
response = client.post("/upload-codes", json=upload_data, headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["uploaded"] == 2
assert data["skipped"] == 0
assert data["total"] == 2
def test_upload_codes_with_duplicates(self, client, sample_coupons, auth_headers):
"""Test code upload with duplicate codes"""
upload_data = {
"codes": [
{"code": "TEST123", "usage": 0}, # Already exists
{"code": "NEW123", "usage": 0} # New code
]
}
response = client.post("/upload-codes", json=upload_data, headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["uploaded"] == 1
assert data["skipped"] == 1
assert data["total"] == 2
def test_upload_codes_case_normalization(self, client, auth_headers):
"""Test code case normalization during upload"""
upload_data = {
"codes": [
{"code": "lowercase", "usage": 0},
{"code": "MIXEDCase", "usage": 0}
]
}
response = client.post("/upload-codes", json=upload_data, headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["uploaded"] == 2
# Verify codes were stored in uppercase
response = client.get("/check-code/LOWERCASE")
assert response.status_code == 200
response = client.get("/check-code/MIXEDCASE")
assert response.status_code == 200