mirror of
https://github.com/Floriansylvain/FDJ-SLAYER.git
synced 2026-08-19 11:43:21 +02:00
refac: project dir struct + tests
This commit is contained in:
@@ -173,3 +173,7 @@ cython_debug/
|
|||||||
|
|
||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pip install -r requirements.txt
|
|||||||
|
|
||||||
Run the main script to generate lottery draws:
|
Run the main script to generate lottery draws:
|
||||||
```bash
|
```bash
|
||||||
python src/main.py
|
python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
The program will:
|
The program will:
|
||||||
@@ -60,9 +60,27 @@ The application uses a combination of entropy sources to generate truly random l
|
|||||||
|
|
||||||
All sources are combined, hashed, and processed to create seeds for the random number generator that selects the lottery numbers.
|
All sources are combined, hashed, and processed to create seeds for the random number generator that selects the lottery numbers.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Unit tests are included to verify the functionality of all major components.
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
Run the test suite with pytest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pytest test/
|
||||||
|
```
|
||||||
|
|
||||||
|
For a coverage report:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pytest --cov-report term --cov=./fdj_slayer test/
|
||||||
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Edit the constants in ``scr/constants.py`` to modify:
|
Edit the constants in ``src/constants.py`` to modify:
|
||||||
|
|
||||||
| Parameter | Description |
|
| Parameter | Description |
|
||||||
|------------------------|-------------------------------------------------------|
|
|------------------------|-------------------------------------------------------|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Add the parent directory to the sys.path so the tests can import the modules from the parent dir.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Empty file to make directory a package
|
||||||
@@ -16,7 +16,7 @@ import multiprocessing
|
|||||||
import psutil
|
import psutil
|
||||||
from progress.bar import Bar
|
from progress.bar import Bar
|
||||||
|
|
||||||
from constants import MAX_NUMBER, MAX_STAR, NUMBER_OF_NUMBERS, NUMBER_OF_STARS
|
from .constants import MAX_NUMBER, MAX_STAR, NUMBER_OF_NUMBERS, NUMBER_OF_STARS
|
||||||
|
|
||||||
|
|
||||||
class Draw:
|
class Draw:
|
||||||
@@ -9,7 +9,7 @@ import openmeteo_requests
|
|||||||
import requests_cache
|
import requests_cache
|
||||||
from retry_requests import retry
|
from retry_requests import retry
|
||||||
|
|
||||||
from constants import OPENMETEO_API_URL, OPENMETEO_HOURLY_PARAMS
|
from .constants import OPENMETEO_API_URL, OPENMETEO_HOURLY_PARAMS
|
||||||
|
|
||||||
|
|
||||||
class Weather:
|
class Weather:
|
||||||
@@ -5,9 +5,9 @@ This script generates random draws for EuroMillions using various entropy source
|
|||||||
import random
|
import random
|
||||||
import numpy as _
|
import numpy as _
|
||||||
|
|
||||||
from draw import Draw
|
from fdj_slayer.draw import Draw
|
||||||
from weather import Weather
|
from fdj_slayer.weather import Weather
|
||||||
from constants import NUMBER_OF_DRAWS
|
from fdj_slayer.constants import NUMBER_OF_DRAWS
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -5,3 +5,6 @@ psutil==7.0.0
|
|||||||
requests-cache==1.2.1
|
requests-cache==1.2.1
|
||||||
retry-requests==2.0.0
|
retry-requests==2.0.0
|
||||||
numpy==2.2.4
|
numpy==2.2.4
|
||||||
|
pytest==8.3.5
|
||||||
|
pytest-cov==6.0.0
|
||||||
|
pytest-mock==3.14.0
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
# Empty file to make directory a package
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for the Draw class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
from io import StringIO
|
||||||
|
import sys
|
||||||
|
from fdj_slayer.draw import Draw
|
||||||
|
from fdj_slayer.constants import NUMBER_OF_NUMBERS, NUMBER_OF_STARS
|
||||||
|
|
||||||
|
|
||||||
|
class TestDraw(unittest.TestCase):
|
||||||
|
"""Test suite for the Draw class"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up test fixtures"""
|
||||||
|
self.mock_weather = MagicMock()
|
||||||
|
self.mock_weather.get_weather_entropy.return_value = "a" * 64
|
||||||
|
self.draw = Draw(self.mock_weather)
|
||||||
|
|
||||||
|
def test_get_static_entropy_pool(self):
|
||||||
|
"""Test the static entropy pool generation"""
|
||||||
|
pool = self.draw.get_static_entropy_pool()
|
||||||
|
|
||||||
|
self.assertIsInstance(pool, list)
|
||||||
|
self.assertIn("a" * 64, pool)
|
||||||
|
self.assertEqual(len(pool), 9)
|
||||||
|
|
||||||
|
def test_get_dynamic_entropy_pool(self):
|
||||||
|
"""Test that dynamic entropy pool returns different values"""
|
||||||
|
pool1 = self.draw.get_dynamic_entropy_pool()
|
||||||
|
pool2 = self.draw.get_dynamic_entropy_pool()
|
||||||
|
|
||||||
|
self.assertIsInstance(pool1, list)
|
||||||
|
self.assertIsInstance(pool2, list)
|
||||||
|
self.assertEqual(len(pool1), len(pool2))
|
||||||
|
self.assertNotEqual(pool1, pool2)
|
||||||
|
|
||||||
|
def test_generate_seed(self):
|
||||||
|
"""Test that seed generation produces an integer from entropy pool"""
|
||||||
|
base_pool = ["test1", "test2"]
|
||||||
|
seed = self.draw.generate_seed(base_pool)
|
||||||
|
|
||||||
|
self.assertIsInstance(seed, int)
|
||||||
|
|
||||||
|
@patch('fdj_slayer.draw.random')
|
||||||
|
def test_make_draw(self, mock_random):
|
||||||
|
"""Test the draw creation with specified numbers and stars"""
|
||||||
|
mock_random.seed.return_value = None
|
||||||
|
mock_random.sample.side_effect = [
|
||||||
|
[10, 20, 30, 40, 50],
|
||||||
|
[5, 10]
|
||||||
|
]
|
||||||
|
|
||||||
|
draw = self.draw.make_draw(["test_entropy"])
|
||||||
|
|
||||||
|
self.assertIn("seed", draw)
|
||||||
|
self.assertIn("numbers", draw)
|
||||||
|
self.assertIn("stars", draw)
|
||||||
|
self.assertEqual(len(draw["numbers"]), NUMBER_OF_NUMBERS)
|
||||||
|
self.assertEqual(len(draw["stars"]), NUMBER_OF_STARS)
|
||||||
|
self.assertEqual(draw["numbers"], [10, 20, 30, 40, 50])
|
||||||
|
self.assertEqual(draw["stars"], [5, 10])
|
||||||
|
|
||||||
|
@patch('fdj_slayer.draw.Bar')
|
||||||
|
def test_generate_draws(self, mock_bar):
|
||||||
|
"""Test generation of multiple draws"""
|
||||||
|
mock_bar_instance = mock_bar.return_value
|
||||||
|
|
||||||
|
self.draw.make_draw = MagicMock(
|
||||||
|
side_effect=[{"numbers": [1, 2, 3, 4, 5],
|
||||||
|
"stars": [1, 2], "seed": i} for i in range(3)]
|
||||||
|
)
|
||||||
|
|
||||||
|
draws = self.draw.generate_draws(3)
|
||||||
|
|
||||||
|
self.assertEqual(len(draws), 3)
|
||||||
|
mock_bar_instance.next.assert_called()
|
||||||
|
mock_bar_instance.finish.assert_called_once()
|
||||||
|
|
||||||
|
def test_display_draw(self):
|
||||||
|
"""Test draw display formatting"""
|
||||||
|
captured_output = StringIO()
|
||||||
|
sys.stdout = captured_output
|
||||||
|
|
||||||
|
try:
|
||||||
|
draw = {"numbers": [1, 2, 3, 4, 5], "stars": [1, 2], "seed": 12345}
|
||||||
|
self.draw.display_draw(draw, index=0, title="TEST")
|
||||||
|
|
||||||
|
output = captured_output.getvalue()
|
||||||
|
self.assertIn("TEST", output)
|
||||||
|
self.assertIn("[1, 2, 3, 4, 5]", output)
|
||||||
|
self.assertIn("[1, 2]", output)
|
||||||
|
finally:
|
||||||
|
sys.stdout = sys.__stdout__
|
||||||
|
|
||||||
|
@patch('builtins.input')
|
||||||
|
def test_display_additional_draws(self, mock_input):
|
||||||
|
"""Test the additional draws display functionality"""
|
||||||
|
mock_input.side_effect = ['y', 'n']
|
||||||
|
|
||||||
|
self.draw.display_draw = MagicMock()
|
||||||
|
|
||||||
|
draws = [
|
||||||
|
{"numbers": [1, 2, 3, 4, 5], "stars": [1, 2], "seed": 1},
|
||||||
|
{"numbers": [6, 7, 8, 9, 10], "stars": [3, 4], "seed": 2},
|
||||||
|
{"numbers": [11, 12, 13, 14, 15], "stars": [5, 6], "seed": 3}
|
||||||
|
]
|
||||||
|
displayed_draws = {0}
|
||||||
|
|
||||||
|
self.draw.display_additional_draws(draws, displayed_draws)
|
||||||
|
|
||||||
|
self.assertEqual(len(displayed_draws), 2)
|
||||||
|
self.draw.display_draw.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for the Weather class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
from fdj_slayer.weather import Weather
|
||||||
|
|
||||||
|
|
||||||
|
class TestWeather(unittest.TestCase):
|
||||||
|
"""Test suite for the Weather class"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.weather = Weather()
|
||||||
|
|
||||||
|
@patch('fdj_slayer.weather.Weather._get_weather_data')
|
||||||
|
def test_get_weather_entropy_success(self, mock_get_data):
|
||||||
|
"""Test the get_weather_entropy method"""
|
||||||
|
expected_hash = "a" * 64
|
||||||
|
mock_get_data.return_value = expected_hash
|
||||||
|
|
||||||
|
result = self.weather.get_weather_entropy()
|
||||||
|
self.assertEqual(result, expected_hash)
|
||||||
|
|
||||||
|
@patch('fdj_slayer.weather.Weather._get_weather_data')
|
||||||
|
def test_get_weather_entropy_fallback(self, mock_get_data):
|
||||||
|
"""Test the get_weather_entropy method when the API fails"""
|
||||||
|
mock_get_data.side_effect = Exception("API Error")
|
||||||
|
|
||||||
|
result = self.weather.get_weather_entropy()
|
||||||
|
|
||||||
|
self.assertEqual(len(result), 64)
|
||||||
|
self.assertTrue(all(c in '0123456789abcdef' for c in result))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user