From 6d468b9cd0d8eaa70a5d74d34f6a2cdf799bec76 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Sat, 12 Oct 2024 22:17:59 +0330 Subject: [PATCH 01/12] test: ch4, ex1 --- tests/test_ch4.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/test_ch4.py diff --git a/tests/test_ch4.py b/tests/test_ch4.py new file mode 100644 index 0000000..67ac5c9 --- /dev/null +++ b/tests/test_ch4.py @@ -0,0 +1,31 @@ +import sys +import os +import time +import asyncio +import unittest +from unittest.mock import patch, AsyncMock, MagicMock + +sys.path.append(os.path.abspath('docs/4. Synchronization and Coordination')) +from ex_4_1 import task as task_ex_1, main as main_ex_1 + + +class TestEx1AsyncTask(unittest.TestCase): + @patch('asyncio.sleep', return_value=None) + def test_task(self, mock_sleep): + """Test task function.""" + global value + value = 0 + asyncio.run(task_ex_1()) + + self.assertEqual(value, 0) # TODO: this must equal with 1 not 0 + mock_sleep.assert_called_with(0.01) + + def test_main_success(self): + """Test main function""" + with patch('builtins.print') as mocked_print: + asyncio.run(main_ex_1()) + mocked_print.assert_any_call(1) + + +if __name__ == '__main__': + unittest.main() From 757eadaf3a3119ffdba418f60b4d2243179a6458 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Mon, 14 Oct 2024 21:45:30 +0330 Subject: [PATCH 02/12] fix(ex_4_1): modified tests to pass logically. --- tests/test_ch4.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/test_ch4.py b/tests/test_ch4.py index 67ac5c9..578d1ab 100644 --- a/tests/test_ch4.py +++ b/tests/test_ch4.py @@ -6,24 +6,27 @@ from unittest.mock import patch, AsyncMock, MagicMock sys.path.append(os.path.abspath('docs/4. Synchronization and Coordination')) -from ex_4_1 import task as task_ex_1, main as main_ex_1 +import ex_4_1 as ex1 class TestEx1AsyncTask(unittest.TestCase): @patch('asyncio.sleep', return_value=None) def test_task(self, mock_sleep): """Test task function.""" - global value - value = 0 - asyncio.run(task_ex_1()) + ex1.value = 0 + + asyncio.run(ex1.task()) + assert ex1.value == 1 + + asyncio.run(ex1.task()) + assert ex1.value == 2 - self.assertEqual(value, 0) # TODO: this must equal with 1 not 0 mock_sleep.assert_called_with(0.01) def test_main_success(self): """Test main function""" with patch('builtins.print') as mocked_print: - asyncio.run(main_ex_1()) + asyncio.run(ex1.main()) mocked_print.assert_any_call(1) From 06c6c528a90510f0fba5eb562c4255e76767e5b4 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Mon, 14 Oct 2024 23:40:38 +0330 Subject: [PATCH 03/12] test: ex_4_2, ex_4_3 --- tests/test_ch4.py | 60 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/tests/test_ch4.py b/tests/test_ch4.py index 578d1ab..3bc71d0 100644 --- a/tests/test_ch4.py +++ b/tests/test_ch4.py @@ -7,6 +7,8 @@ sys.path.append(os.path.abspath('docs/4. Synchronization and Coordination')) import ex_4_1 as ex1 +import ex_4_2 as ex2 +import ex_4_3 as ex3 class TestEx1AsyncTask(unittest.TestCase): @@ -16,10 +18,10 @@ def test_task(self, mock_sleep): ex1.value = 0 asyncio.run(ex1.task()) - assert ex1.value == 1 + self.assertEqual(ex1.value, 1) asyncio.run(ex1.task()) - assert ex1.value == 2 + self.assertEqual(ex1.value, 2) mock_sleep.assert_called_with(0.01) @@ -30,5 +32,59 @@ def test_main_success(self): mocked_print.assert_any_call(1) +class TestEx2AsyncTask(unittest.TestCase): + @patch('asyncio.sleep', return_value=None) + def test_task(self, mock_sleep): + """Test task function.""" + ex2.value = 0 + lock = asyncio.Lock() + asyncio.run(ex2.task(lock)) + self.assertEqual(ex2.value, 1) + + asyncio.run(ex2.task(lock)) + self.assertEqual(ex2.value, 2) + + mock_sleep.assert_called_with(0.00) + + def test_main_success(self): + """Test main function""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex2.main()) + mocked_print.assert_any_call(10000) + + +class TestEx3Semaphore(unittest.IsolatedAsyncioTestCase): + @patch('asyncio.sleep', return_value=None) + def test_limited_resource(self, mock_sleep): + """Test limited resource function.""" + ex2.value = 0 + lock = asyncio.Lock() + asyncio.run(ex2.task(lock)) + self.assertEqual(ex2.value, 1) + + asyncio.run(ex2.task(lock)) + self.assertEqual(ex2.value, 2) + + with patch('builtins.print') as mocked_print: + asyncio.run(ex3.main()) + mocked_print.assert_any_call("Accessing limited resource") + mocked_print.assert_any_call("Finished using limited resource") + + mock_sleep.assert_called_with(1) + + def test_main_success(self): + """Test main function""" + with patch('builtins.print') as mocked_print: + time_start = time.time() + asyncio.run(ex3.main()) + time_taken = time.time() - time_start + + self.assertLess(time_taken, 3) + self.assertLess(2, time_taken) + + mocked_print.assert_any_call("Accessing limited resource") + mocked_print.assert_any_call("Finished using limited resource") + + if __name__ == '__main__': unittest.main() From 747079b33d3fae4e82bfbb068b29a539b9c9aca8 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Tue, 15 Oct 2024 23:26:24 +0330 Subject: [PATCH 04/12] test: ex_4_5 --- tests/test_ch4.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_ch4.py b/tests/test_ch4.py index 3bc71d0..6953fd6 100644 --- a/tests/test_ch4.py +++ b/tests/test_ch4.py @@ -9,6 +9,7 @@ import ex_4_1 as ex1 import ex_4_2 as ex2 import ex_4_3 as ex3 +import ex_4_4 as ex4 class TestEx1AsyncTask(unittest.TestCase): @@ -86,5 +87,29 @@ def test_main_success(self): mocked_print.assert_any_call("Finished using limited resource") +class TestEx4Barrier(unittest.TestCase): + @patch('asyncio.sleep', return_value=None) + def test_example_barrier(self, mock_sleep): + """Test example barrier function.""" + with patch('builtins.print') as mock_print: + asyncio.run(ex4.example_barrier()) + mock_print.assert_any_call("barrier passed") + calls = mock_print.call_args_list + + self.assertEqual(len(calls), 4) + + printed_outputs = [call[0][0] for call in calls] + expected_output = [ + '[filling, waiters:0/3]', + '[filling, waiters:0/3]', + 'barrier passed', + '[filling, waiters:0/3]', + ] + for out_idx in range(3): + self.assertIn(expected_output[out_idx], str(printed_outputs[out_idx])) + + mock_sleep.assert_called_with(0) + + if __name__ == '__main__': unittest.main() From 3f213e43e0a74077684a2df5ce27697eef8b89df Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Thu, 17 Oct 2024 00:01:05 +0330 Subject: [PATCH 05/12] test: ex4_5 waiter event --- tests/test_ch4.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_ch4.py b/tests/test_ch4.py index 6953fd6..a09c128 100644 --- a/tests/test_ch4.py +++ b/tests/test_ch4.py @@ -10,6 +10,7 @@ import ex_4_2 as ex2 import ex_4_3 as ex3 import ex_4_4 as ex4 +import ex_4_5 as ex5 class TestEx1AsyncTask(unittest.TestCase): @@ -111,5 +112,31 @@ def test_example_barrier(self, mock_sleep): mock_sleep.assert_called_with(0) +class TestEx5EventWaiter(unittest.IsolatedAsyncioTestCase): + async def test_waiter(self): + """Test waiter(event) function.""" + event = asyncio.Event() + with patch('builtins.print') as mock_print: + time_start = time.time() + waiter_task = asyncio.create_task(ex5.waiter(event)) + event.set() + await waiter_task + time_taken = time.time() - time_start + self.assertLess(time_taken, 0.01) + + mock_print.assert_any_call("waiting for it ...") + mock_print.assert_any_call("... got it!") + + def test_main(self): + with patch('builtins.print') as mock_print: + time_start = time.time() + asyncio.run(ex5.main()) + time_taken = time.time() - time_start + self.assertGreater(time_taken, 1) + + mock_print.assert_any_call("waiting for it ...") + mock_print.assert_any_call("... got it!") + + if __name__ == '__main__': unittest.main() From 845ef77753b39f6e0061ff903da45bc34dfd6332 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Fri, 18 Oct 2024 00:31:34 +0330 Subject: [PATCH 06/12] test: ex_5_1, fixed ex_3_5 test. --- tests/test_ch3.py | 4 ++-- tests/test_ch5.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/test_ch5.py diff --git a/tests/test_ch3.py b/tests/test_ch3.py index 23eeda4..0693991 100644 --- a/tests/test_ch3.py +++ b/tests/test_ch3.py @@ -163,8 +163,8 @@ def test_with_and_without_mp(self): self.assertEqual(value1_mp, value2_mp) self.assertEqual(value1_mp, self.value * self.step) self.assertEqual(value2_mp, self.value * self.step) - self.assertLessEqual(time_taken_mp * 1.7, time_taken, - msg='time taken without mp is less than 1.7 times of with mp!') + self.assertLessEqual(time_taken_mp * 1.5, time_taken, + msg='time taken without mp is less than 1.5 times of with mp!') def test_main(self): with patch('builtins.print') as mocked_print: diff --git a/tests/test_ch5.py b/tests/test_ch5.py new file mode 100644 index 0000000..bd25081 --- /dev/null +++ b/tests/test_ch5.py @@ -0,0 +1,48 @@ +import sys +import os +import time +import asyncio +import unittest +from unittest.mock import patch, AsyncMock, MagicMock + +sys.path.append(os.path.abspath('docs/5. Advanced Techniques')) +import ex_5_1 as ex1 + + +class TestEx1ExceptionPropagate(unittest.TestCase): + @patch('asyncio.sleep', return_value=None) + def test_shorter_task(self, mock_sleep): + """Test shorter task function.""" + with patch('builtins.print') as mocked_print: + with patch('ex_5_1.Exception') as mock_exception, self.assertRaises(Exception): + asyncio.run(ex1.shorter_task()) + mock_exception.assert_called_once_with("Some exception happened!") + mocked_print.assert_called_once_with("Executing the task to raise an exception!") + mock_sleep.assert_called_once_with(0.1) + + @patch('asyncio.sleep', return_value=None) + def test_longer_task(self, mock_sleep): + """Test longer task function.""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex1.longer_task()) + mocked_print.assert_any_call("Executing the task which will complete!") + mock_sleep.assert_called_once_with(1) + mocked_print.assert_any_call("longer_task is done!") + + @patch('asyncio.sleep', return_value=None) + def test_main(self, mock_sleep): + """Test main function.""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex1.main()) + mocked_print.assert_any_call("Main coroutine started!") + mocked_print.assert_any_call("Executing the task to raise an exception!") + mocked_print.assert_any_call("Executing the task which will complete!") + mock_sleep.assert_any_call(1) + mock_sleep.assert_any_call(0.1) + mocked_print.assert_any_call("longer_task is done!") + mocked_print.assert_any_call("Exception: Some exception happened!") + mocked_print.assert_any_call("Main coroutine done!") + + +if __name__ == '__main__': + unittest.main() From 3521866df43512da55b509bc1a8c1d0d640d79a9 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Fri, 18 Oct 2024 18:44:13 +0330 Subject: [PATCH 07/12] test: ex_5_2 to ex_5_4 covered. --- tests/test_ch5.py | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/tests/test_ch5.py b/tests/test_ch5.py index bd25081..2974919 100644 --- a/tests/test_ch5.py +++ b/tests/test_ch5.py @@ -7,6 +7,9 @@ sys.path.append(os.path.abspath('docs/5. Advanced Techniques')) import ex_5_1 as ex1 +import ex_5_2 as ex2 +import ex_5_3 as ex3 +import ex_5_4 as ex4 class TestEx1ExceptionPropagate(unittest.TestCase): @@ -44,5 +47,95 @@ def test_main(self, mock_sleep): mocked_print.assert_any_call("Main coroutine done!") +class TestEx2ExceptionHandler(unittest.TestCase): + @patch('asyncio.sleep', return_value=None) + def test_shorter_task(self, mock_sleep): + """Test shorter task function.""" + with patch('builtins.print') as mocked_print: + with patch('ex_5_2.Exception') as mock_exception, self.assertRaises(Exception): + asyncio.run(ex2.shorter_task()) + mock_exception.assert_called_once_with("Some exception happened!") + mocked_print.assert_called_once_with("Executing the task to raise an exception!") + mock_sleep.assert_called_once_with(0.01) + + @patch('asyncio.sleep', return_value=None) + def test_longer_task(self, mock_sleep): + """Test longer task function.""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex2.longer_task()) + mocked_print.assert_any_call("Executing the task which will complete!") + mock_sleep.assert_called_once_with(1) + mocked_print.assert_any_call("longer_task is done!") + + def test_exception_handler(self): + """Test exception handler.""" + with patch('builtins.print') as mocked_print: + context = {"exception": "Test exception!"} + ex2.exception_handler(None, context) + mocked_print.assert_called_once_with("Exception: Test exception!") + + @patch('asyncio.sleep', return_value=None) + def test_main(self, mock_sleep): + """Test main function.""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex2.main()) + mocked_print.assert_any_call("Main coroutine started!") + mocked_print.assert_any_call("Executing the task to raise an exception!") + mocked_print.assert_any_call("Executing the task which will complete!") + mock_sleep.assert_any_call(1) + mock_sleep.assert_any_call(0.01) + mocked_print.assert_any_call("longer_task is done!") + mocked_print.assert_any_call("Exception: Some exception happened!") + mocked_print.assert_any_call("Main coroutine done!") + + +class TestEx3CancelTask(unittest.TestCase): + @patch('asyncio.sleep', return_value=None) + def test_cancel_me(self, mock_sleep): + """Test cancel_me function.""" + asyncio.run(ex3.cancel_me()) + mock_sleep.assert_called_once_with(1) + + @patch('asyncio.sleep', return_value=None) + def test_main(self, mock_sleep): + """Test main function""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex3.main()) + mocked_print.assert_called_once_with("main(): cancel_me is cancelled now") + mock_sleep.assert_any_call(0.01) + + +class TestEx4ChainTasks(unittest.IsolatedAsyncioTestCase): + @patch('asyncio.sleep', return_value=None) + async def test_task1(self, mock_sleep): + """Test task1 function.""" + with patch('builtins.print') as mocked_print: + result = await ex4.task1() + mock_sleep.assert_called_once_with(1) + mocked_print.assert_called_once_with(">task1()") + self.assertEqual(result, 1) + + @patch('asyncio.sleep', return_value=None) + async def test_task2(self, mock_sleep): + """Test task2 function.""" + with patch('builtins.print') as mocked_print: + value = 1 + await ex4.task2(value) + mock_sleep.assert_called_once_with(1) + mocked_print.assert_called_once_with(f">task2() got {value}") + + @patch('asyncio.sleep', return_value=None) + def test_main(self, mock_sleep): + """Test main function""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex4.main()) + mocked_print.assert_any_call(">task1()") + mocked_print.assert_any_call(f">task2() got 1") + + mocked_print.assert_any_call("Main: chain is done") + mock_sleep.assert_any_call(1) + self.assertTrue(ex4.event.is_set()) + + if __name__ == '__main__': unittest.main() From 4d72cc9f1fcde1274653c752ef0c5ee3446fdfce Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Fri, 18 Oct 2024 21:05:19 +0330 Subject: [PATCH 08/12] test: ex_5_5 and ex_5_6 covered. --- tests/test_ch5.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_ch5.py b/tests/test_ch5.py index 2974919..82f4d6d 100644 --- a/tests/test_ch5.py +++ b/tests/test_ch5.py @@ -10,6 +10,8 @@ import ex_5_2 as ex2 import ex_5_3 as ex3 import ex_5_4 as ex4 +import ex_5_5 as ex5 +import ex_5_6 as ex6 class TestEx1ExceptionPropagate(unittest.TestCase): @@ -137,5 +139,35 @@ def test_main(self, mock_sleep): self.assertTrue(ex4.event.is_set()) +class TestEx5ProducerConsumer(unittest.TestCase): + @patch('asyncio.sleep', return_value=None) + def test_producer(self, mock_sleep): + """Test producer function""" + channel = asyncio.Queue() + asyncio.run(ex5.producer(channel)) + self.assertEqual(channel.qsize(), 5) + mock_sleep.assert_any_call(1) + + @patch('asyncio.sleep', return_value=None) + def test_main(self, mock_sleep): + """Test main function""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex5.main()) + mocked_print.assert_any_call("Done!") + for num in range(5): + mocked_print.assert_any_call(f"Got number {num}") + mock_sleep.assert_any_call(1) + + +class TestEx6Future(unittest.TestCase): + def test_main(self): + """Test main function""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex6.main()) + mocked_print.assert_any_call("future status is done: False") + mocked_print.assert_any_call("future status is done: True, future result: 10") + mocked_print.assert_any_call("future result after being awaited: 10") + + if __name__ == '__main__': unittest.main() From aee3b3433c99af833e853218b0c98814758a3f41 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Fri, 18 Oct 2024 23:15:51 +0330 Subject: [PATCH 09/12] test: covered ch6 + small typo fix in ex_6_2. --- .../ex_6_2.py | 2 +- tests/test_ch6.py | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/test_ch6.py diff --git a/docs/6. Exploring Python 3.12 Features/ex_6_2.py b/docs/6. Exploring Python 3.12 Features/ex_6_2.py index 69322e7..6d40ebe 100644 --- a/docs/6. Exploring Python 3.12 Features/ex_6_2.py +++ b/docs/6. Exploring Python 3.12 Features/ex_6_2.py @@ -7,7 +7,7 @@ async def light_coro(): async def main(): - print('Before running task group!') + print('Before running gather!') tasks_list = [light_coro() for _ in range(1000000)] time0 = time.time() diff --git a/tests/test_ch6.py b/tests/test_ch6.py new file mode 100644 index 0000000..1ece3f8 --- /dev/null +++ b/tests/test_ch6.py @@ -0,0 +1,64 @@ +import sys +import os +import time +import asyncio +import unittest +from unittest.mock import patch, AsyncMock, MagicMock + +sys.path.append(os.path.abspath('docs/6. Exploring Python 3.12 Features')) +import ex_6_1 as ex1 +import ex_6_2 as ex2 +import ex_6_3 as ex3 + + +class TestEagerTaskFactory(unittest.TestCase): + def test_with_eager_task_factory(self): + """Test running light_coro with eager_task_factory function.""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex1.main()) + mocked_print.assert_any_call("Before running task group!") + mocked_print.assert_any_call("After running task group with eager task factory!") + self.assertEqual(mocked_print.call_count, 3) + + def test_without_eager_task_factory(self): + """Test running light_coro without eager_task_factory function.""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex2.main()) + mocked_print.assert_any_call("Before running gather!") + mocked_print.assert_any_call("After running gather without eager task factory!") + self.assertEqual(mocked_print.call_count, 4) + + def test_compare_running_time(self): + """Test if running light_coro with eager_task_factory function is 4 times faster than without it.""" + with patch('builtins.print') as mocked_print: + asyncio.run(ex1.main()) + printed_outputs = [call[0][0] for call in mocked_print.call_args_list] + time_taken_output = next((output for output in printed_outputs if "It took" in output), None) + self.assertIsNotNone(time_taken_output, "The output containing time taken was not found.") + + time_taken_with_eager = float(time_taken_output.split()[2]) + self.assertGreater(time_taken_with_eager, 0) + + with patch('builtins.print') as mocked_print: + asyncio.run(ex2.main()) + printed_outputs = [call[0][0] for call in mocked_print.call_args_list] + time_taken_output = next((output for output in printed_outputs if "It took" in output), None) + self.assertIsNotNone(time_taken_output, "The output containing time taken was not found.") + + time_taken_without_eager = float(time_taken_output.split()[2]) + self.assertGreater(time_taken_without_eager, 0) + + self.assertGreater(time_taken_without_eager, time_taken_with_eager * 4) + + +class TestEx3CurrentTask(unittest.TestCase): + def test_current_task_one_million_calls(self): + """Test if asyncio.current_task is called 1 million times.""" + with patch('asyncio.current_task') as mocked_current_task: + asyncio.run(ex3.main()) + self.assertEqual(mocked_current_task.call_count, 1_000_000) + + +if __name__ == '__main__': + unittest.main() + From d0678b35fe3900d2e41a07fc6be64f95fdf95623 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Sat, 19 Oct 2024 23:44:51 +0330 Subject: [PATCH 10/12] test: ex_7_1 and ex_7_2 covered. --- requirements.txt | 2 ++ tests/test_ch7.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/test_ch7.py diff --git a/requirements.txt b/requirements.txt index ab16fa9..88708aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ pytest~=8.3.3 httpx~=0.27.2 +uvicorn~=0.30.2 +gunicorn~=19.9.0 diff --git a/tests/test_ch7.py b/tests/test_ch7.py new file mode 100644 index 0000000..b92412a --- /dev/null +++ b/tests/test_ch7.py @@ -0,0 +1,49 @@ +import sys +import os +import unittest +from unittest.mock import patch, AsyncMock, MagicMock +import threading +import requests +import time +sys.path.append(os.path.abspath('docs/7. Web Applications')) +import ex_7_1 as ex1 +import ex_7_2 as ex2 + + +class TestGunicornApp(unittest.TestCase): + def test_app_gunicorn(self): + with patch('builtins.print') as mocked_print: + data = b'Hello, World!\n' + response_headers = [ + ('Content-type', 'text/plain'), + ('Content-Length', str(len(data))) + ] + result = ex1.app(None, mocked_print) + mocked_print.assert_any_call("200 OK", response_headers) + self.assertEqual(next(result), data) + + +class TestUvicornApp(unittest.IsolatedAsyncioTestCase): + async def test_app_uvicorn(self): + with patch('asyncio.sleep') as send_data: + data1_to_send = { + 'type': 'http.response.start', + 'status': 200, + 'headers': [ + [b'content-type', b'text/plain'], + ], + } + data2_to_send = { + 'type': 'http.response.body', + 'body': b'Hello, world!', + } + scope = { + "type": "http" + } + await ex2.app(scope, None, send_data) + send_data.assert_any_call(data1_to_send) + send_data.assert_any_call(data2_to_send) + + +if __name__ == '__main__': + unittest.main() From 4694540b7ea70820c837d9197cd2bc233bf73fc6 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Sun, 20 Oct 2024 20:38:25 +0330 Subject: [PATCH 11/12] test: ex_7_3 --- requirements.txt | 1 + tests/test_ch7.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/requirements.txt b/requirements.txt index 88708aa..1846682 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ pytest~=8.3.3 httpx~=0.27.2 uvicorn~=0.30.2 gunicorn~=19.9.0 +starlette==0.37.2 diff --git a/tests/test_ch7.py b/tests/test_ch7.py index b92412a..d14e543 100644 --- a/tests/test_ch7.py +++ b/tests/test_ch7.py @@ -2,12 +2,14 @@ import os import unittest from unittest.mock import patch, AsyncMock, MagicMock +from starlette.testclient import TestClient import threading import requests import time sys.path.append(os.path.abspath('docs/7. Web Applications')) import ex_7_1 as ex1 import ex_7_2 as ex2 +import ex_7_3 as ex3 class TestGunicornApp(unittest.TestCase): @@ -45,5 +47,15 @@ async def test_app_uvicorn(self): send_data.assert_any_call(data2_to_send) +class TestStarletteHomepage(unittest.TestCase): + def setUp(self): + self.client = TestClient(ex3.app) + + def test_homepage(self): + response = self.client.get('/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {'hello': 'world'}) + + if __name__ == '__main__': unittest.main() From 773ca625832054f63b934ab45d92b32e4d8330d0 Mon Sep 17 00:00:00 2001 From: Ali Gheshlaghi Date: Mon, 21 Oct 2024 20:23:02 +0330 Subject: [PATCH 12/12] fix: removed extra imports. --- tests/test_ch7.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_ch7.py b/tests/test_ch7.py index d14e543..61cc7e6 100644 --- a/tests/test_ch7.py +++ b/tests/test_ch7.py @@ -3,8 +3,6 @@ import unittest from unittest.mock import patch, AsyncMock, MagicMock from starlette.testclient import TestClient -import threading -import requests import time sys.path.append(os.path.abspath('docs/7. Web Applications')) import ex_7_1 as ex1