import unittest
from unittest.mock import patch
from fastapi.testclient import TestClient
from backend import app as api
from backend.diagnostics import ip_report


class ApiTests(unittest.TestCase):
    def setUp(self):
        api.cache.clear();api.requests_by_client.clear();api.global_requests.clear()
        self.client=TestClient(api.app,client=("8.8.8.8",10000))

    def test_client_cannot_spoof_proxy_header(self):
        with patch.object(api,"TRUST_NGINX",True):
            response=self.client.get('/api/context',headers={'X-Real-IP':'1.1.1.1'})
            self.assertEqual(response.json()['ip'],'8.8.8.8')

    def test_trusted_local_proxy_header(self):
        with patch.object(api,"TRUST_NGINX",True):
            client=TestClient(api.app,client=("127.0.0.1",10000))
            self.assertEqual(client.get('/api/context',headers={'X-Real-IP':'1.1.1.1'}).json()['ip'],'1.1.1.1')

    def test_unix_proxy_header(self):
        class Fake:
            client=None
            headers={'x-real-ip':'1.1.1.1'}
        with patch.object(api,"TRUST_NGINX",True):self.assertEqual(api.client_ip(Fake()),'1.1.1.1')

    def test_headers_are_json_escaped(self):
        text='a"quote\\slash<script>alert(1)</script>'
        response=self.client.get('/all.json',headers={'User-Agent':text})
        self.assertEqual(response.json()['ua'],text)
        self.assertEqual(response.headers['cache-control'],'no-store')

    def test_private_ip_rejected_without_upstream_call(self):
        with patch.object(api,'ip_report') as upstream:
            self.assertEqual(self.client.post('/api/ip-report',json={'ip':'127.0.0.1'}).status_code,422)
            upstream.assert_not_called()

    def test_cache_and_no_service_probe(self):
        fixture=ip_report('8.8.8.8',env={})
        with patch.object(api,'ip_report',return_value=fixture) as upstream:
            first=self.client.post('/api/ip-report',json={'ip':'8.8.8.8'})
            second=self.client.post('/api/ip-report',json={'ip':'8.8.8.8'})
            self.assertEqual(first.status_code,200)
            self.assertFalse(first.json()['cached']);self.assertTrue(second.json()['cached'])
            self.assertEqual(second.json()['services'],[]);upstream.assert_called_once()

    def test_rate_limit(self):
        with patch.object(api,'ip_report',return_value=ip_report('8.8.8.8',env={})):
            for _ in range(6):self.assertEqual(self.client.post('/api/ip-report',json={}).status_code,200)
            self.assertEqual(self.client.post('/api/ip-report',json={}).status_code,429)

    def test_foreign_origin_is_rejected(self):
        self.assertEqual(self.client.post('/api/ip-report',json={},headers={'Origin':'https://unrelated.invalid'}).status_code,403)


if __name__=="__main__":unittest.main()
