"""Tests for the sum-numbers example; compatible with pytest or unittest."""

import json
from unittest import IsolatedAsyncioTestCase

from function import new


class TestSumNumbers(IsolatedAsyncioTestCase):
    async def invoke(self, body, method="POST"):
        responses = []

        async def receive():
            return {
                "type": "http.request",
                "body": body,
                "more_body": False,
            }

        async def send(message):
            responses.append(message)

        await new().handle(
            {"type": "http", "method": method, "path": "/"},
            receive,
            send,
        )
        self.assertEqual(responses[0]["type"], "http.response.start")
        self.assertEqual(responses[1]["type"], "http.response.body")
        return responses[0]["status"], json.loads(responses[1]["body"])

    async def test_add_three_numbers(self):
        self.assertEqual(
            await self.invoke(b'{"numbers": [10, 20, 30]}'),
            (200, {"total": 60}),
        )

    async def test_empty_list(self):
        self.assertEqual(await self.invoke(b'{"numbers": []}'), (200, {"total": 0}))

    async def test_decimal_and_negative_numbers(self):
        self.assertEqual(
            await self.invoke(b'{"numbers": [1.5, -2, 3]}'),
            (200, {"total": 2.5}),
        )

    async def test_invalid_input(self):
        for body in (
            b"not JSON",
            b"{}",
            b'{"numbers": ["ten"]}',
            b'{"numbers": [true]}',
            b'{"numbers": [NaN]}',
        ):
            with self.subTest(body=body):
                status, response = await self.invoke(body)
                self.assertEqual(status, 400)
                self.assertIn("error", response)

    async def test_requires_post(self):
        status, response = await self.invoke(b"", method="GET")
        self.assertEqual(status, 405)
        self.assertIn("error", response)

    async def test_request_size_limit(self):
        status, response = await self.invoke(b"x" * 65537)
        self.assertEqual(status, 413)
        self.assertIn("error", response)

    async def test_health_checks(self):
        function = new()
        function.start({})
        self.assertTrue(function.alive()[0])
        self.assertTrue(function.ready()[0])
        function.stop()
