From 1952f8cdc73db0d4f4a33b2efeb6e0f97d79367c Mon Sep 17 00:00:00 2001 From: Mike Holler Date: Sun, 16 Aug 2020 11:49:41 -0500 Subject: [PATCH 1/2] first test --- .github/workflows/integration_test.yml | 32 ++++++ README.md | 27 ++++- integration_tests/__init__.py | 86 +++++++++++++++ .../basic_client_test.py | 104 +++++++----------- 4 files changed, 183 insertions(+), 66 deletions(-) create mode 100644 .github/workflows/integration_test.yml create mode 100644 integration_tests/__init__.py rename client_test.py => integration_tests/basic_client_test.py (65%) diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml new file mode 100644 index 0000000..acad6e6 --- /dev/null +++ b/.github/workflows/integration_test.yml @@ -0,0 +1,32 @@ +name: Integration Test + +on: + push: + branches: master + tags: + - "*" + schedule: + # 4 AM Central Daylight Time + - cron: '0 9 * * *' + +jobs: + flake8_py3: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.8.0 + architecture: x64 + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Run Integration Tests + env: + IRACING_USERNAME: ${{ secrets.IRACING_USERNAME }} + IRACING_PASSWORD: ${{ secrets.IRACING_PASSWORD }} + run: | + python -m unittest discover integration_tests/ -p "*_test.py" \ No newline at end of file diff --git a/README.md b/README.md index deb6f5d..41c3266 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,32 @@ The goal of this project is to provide access to iRacing stats in a manner that The contributors of this project use Discord as the primary means of communication; The [iRacing Open Wheel server](https://discord.gg/UwnhM7w) was created by the author of this project and hosts the channels for discussion there. When joining, please ask Jacob Anderson for the role to see the appropriate channels. # Documentation & Discussion + All documentation for this project is available through the [Github Pages project site](https://esterni.github.io/pyracing/). +# Dependencies + +* [httpx](https://www.python-httpx.org/) = 0.13.x + +# Running Tests + +Before running tests, you have to prepare the environment variables. +This is different for windows and linux. + +**Windows** +``` +set "IRACING_USERNAME=user@example.com" +set "IRACING_PASSWORD=hunter2" +``` + +**Linux** +``` +set IRACING_USERNAME="user@example.com" +set IRACING_PASSWORD="hunter2" +``` +Running the tests is the same on both systems: -# Dependancies -[httpx](https://www.python-httpx.org/) = 0.13.x +``` +python -m unitttest discover integration_tests/ -p "*_test.py" +``` diff --git a/integration_tests/__init__.py b/integration_tests/__init__.py new file mode 100644 index 0000000..13ad071 --- /dev/null +++ b/integration_tests/__init__.py @@ -0,0 +1,86 @@ +import asyncio +import logging +import os +import sys +import unittest + +from pyracing.client import Client + + +__all__ = [ + "IRacingIntegrationTest", + "async_test", +] + +logging.basicConfig( + stream=sys.stdout, + level=logging.INFO, + datefmt="%Y-%m-%dT%H:%M:%S%Z", + format="%(asctime)s [%(levelname)s] -- %(message)s" +) + + +def get_required_env(key: str) -> str: + """ + Get an environment variable's value, raising an exception if it isn't set. + """ + value = os.getenv(key) + if value is None: + raise EnvironmentError( + f"Must set {key} environment variable to to run integration tests." + ) + return value + + +IRACING_USERNAME = get_required_env("IRACING_USERNAME") +IRACING_PASSWORD = get_required_env("IRACING_PASSWORD") + +# We create a singleton client that we use for all tests to avoid constantly +# re-authenticating. iRacing may see constant re-authentication as suspicious +# activity, we see the disadvantages of 1) using a singleton and 2) sharing +# state between tests are outweighed by being good citizens to the iRacing +# platform. +client = Client( + username=IRACING_USERNAME, + password=IRACING_PASSWORD, +) + + +def async_test(f): + """ + Function that can be used as a decorator for tests of async functions. + + For example, + + class MyTest(unittests.TestCase): + @async_test + async def test_example(self): + self.assertTrue(True) + + :param f: the function to wrap + :return: the wrapped function + + >>> class MyTest(unittest.TestCase): + ... @async_test + ... async def test_example(self): + ... self.assertTrue(True) + """ + def wrapper(*args, **kwargs): + coro = asyncio.coroutine(f) + future = coro(*args, **kwargs) + loop = asyncio.get_event_loop() + loop.run_until_complete(future) + + return wrapper + + +class IRacingIntegrationTest(unittest.TestCase): + """ + Test service that runs against iRacing's server. + """ + + def setUp(self) -> None: + self.client = Client( + username=IRACING_USERNAME, + password=IRACING_PASSWORD, + ) diff --git a/client_test.py b/integration_tests/basic_client_test.py similarity index 65% rename from client_test.py rename to integration_tests/basic_client_test.py index c1804b5..7d1125c 100644 --- a/client_test.py +++ b/integration_tests/basic_client_test.py @@ -1,80 +1,60 @@ -import asyncio -import dotenv -import logging -import os -import sys -from pyracing import client as pyracing -from pyracing import constants -from pyracing.response_objects import career_stats, chart_data, iracing_data, \ - upcoming_events, historical_data, session_data -import unittest - -dotenv.load_dotenv() -client = pyracing.Client( - os.getenv('IRACING_USERNAME'), - os.getenv('IRACING_PASSWORD') +from integration_tests import ( + IRacingIntegrationTest, + async_test, ) - -logging.basicConfig( - stream=sys.stdout, - level=logging.INFO, - datefmt="%Y-%m-%d %H:%M:%S", - format="%(asctime)s;%(levelname)s;%(message)s" +from pyracing import constants +from pyracing.response_objects import ( + career_stats, + chart_data, + historical_data, + iracing_data, + session_data, + upcoming_events, ) -def async_test(f): - def wrapper(*args, **kwargs): - coro = asyncio.coroutine(f) - future = coro(*args, **kwargs) - loop = asyncio.get_event_loop() - loop.run_until_complete(future) - - return wrapper - - -class ClientTest(unittest.TestCase): +class ClientTest(IRacingIntegrationTest): @async_test async def test_career_stats(self): - response_list = await client.career_stats(499343) + response_list = await self.client.career_stats(499343) for career_stat in response_list: self.assertIsInstance(career_stat, career_stats.CareerStats) @async_test async def test_yearly_stats(self): - response_list = await client.yearly_stats(499343) + response_list = await self.client.yearly_stats(499343) for yearly_stat in response_list: self.assertIsInstance(yearly_stat, career_stats.YearlyStats) @async_test async def test_last_races_stats(self): - response_list = await client.last_races_stats(499343) + response_list = await self.client.last_races_stats(499343) for career_stat in response_list: self.assertIsInstance(career_stat, career_stats.LastRacesStats) @async_test async def test_current_seasons(self): - response_list = await client.current_seasons() + response_list = await self.client.current_seasons() for season in response_list: self.assertIsInstance(season, iracing_data.Season) @async_test async def test_irating(self): - response = await client.irating(499343, constants.Category.road.value) + response = await self.client.irating(499343, constants.Category.road.value) self.assertIsInstance(response, chart_data.ChartData) for irating in response.list: self.assertIsInstance(irating, chart_data.IRating) @async_test async def test_ttrating(self): - response = await client.ttrating(499343, constants.Category.road.value) + response = await self.client.ttrating(499343, constants.Category.road.value) self.assertIsInstance(response, chart_data.ChartData) for ttrating in response.list: self.assertIsInstance(ttrating, chart_data.TTRating) @async_test async def test_license_class(self): - response = await client.license_class( + response = await self.client.license_class( 499343, constants.Category.road.value ) @@ -84,41 +64,41 @@ async def test_license_class(self): @async_test async def test_active_op_counts(self): - response = await client.active_op_counts() + response = await self.client.active_op_counts() for op_count in response: self.assertIsInstance(op_count, upcoming_events.OpenPractice) @async_test async def test_all_subsessions(self): - response = await client.all_subsessions(33618467) + response = await self.client.all_subsessions(33618467) for subsession_id in response: self.assertIsInstance(subsession_id, int) @async_test async def test_car_class(self): - response = await client.car_class() + response = await self.client.car_class() self.assertIsInstance(response, iracing_data.CarClass) @async_test async def test_car_class_with_value(self): - response = await client.car_class(1) + response = await self.client.car_class(1) self.assertIsInstance(response, iracing_data.CarClass) @async_test async def test_last_series(self): - response = await client.last_series(499343) + response = await self.client.last_series(499343) for last_series in response: self.assertIsInstance(last_series, career_stats.LastSeries) @async_test async def test_member_cars_driven(self): - response = await client.member_cars_driven(499343) + response = await self.client.member_cars_driven(499343) for car_id in response: self.assertIsInstance(car_id, int) @async_test async def test_member_subsession_id_from_session(self): - response = await client.member_subsession_id_from_session( + response = await self.client.member_subsession_id_from_session( 499343, 134975301 ) @@ -126,17 +106,17 @@ async def test_member_subsession_id_from_session(self): @async_test async def test_driver_status(self): - response = await client.driver_status(499343) + response = await self.client.driver_status(499343) self.assertIsInstance(response, iracing_data.DriverStatus) @async_test async def test_next_event(self): - response = await client.next_event(228) + response = await self.client.next_event(228) self.assertIsInstance(response, upcoming_events.NextEvent) @async_test async def test_next_session_times(self): - response = await client.next_session_times(2826) + response = await self.client.next_session_times(2826) for session_time in response: self.assertIsInstance( session_time, @@ -145,66 +125,62 @@ async def test_next_session_times(self): @async_test async def test_personal_bests(self): - response = await client.personal_bests(499343, 4) + response = await self.client.personal_bests(499343, 4) for personal_best in response: self.assertIsInstance(personal_best, career_stats.PersonalBests) @async_test async def test_private_results(self): - response = await client.private_results(499343, 0, 1595792756654) + response = await self.client.private_results(499343, 0, 1595792756654) for result in response: self.assertIsInstance(result, historical_data.PrivateResults) @async_test async def test_race_guide(self): - response = await client.race_guide() + response = await self.client.race_guide() for race_guide in response: self.assertIsInstance(race_guide, upcoming_events.RaceGuide) @async_test async def test_race_laps_all(self): - response = await client.race_laps_all(33618467) + response = await self.client.race_laps_all(33618467) self.assertIsInstance(response, session_data.RaceLapsAll) @async_test async def test_race_laps_driver(self): - response = await client.race_laps_driver(499343, 33618467) + response = await self.client.race_laps_driver(499343, 33618467) self.assertIsInstance(response, session_data.RaceLapsDriver) @async_test async def test_season_from_session(self): - response = await client.season_from_session(134975301) + response = await self.client.season_from_session(134975301) self.assertIsInstance(response, int) @async_test async def test_season_standings(self): - response = await client.season_standings(2826) + response = await self.client.season_standings(2826) for standings in response: self.assertIsInstance(standings, historical_data.SeasonStandings) @async_test async def test_series_race_results(self): - response = await client.series_race_results(2826) + response = await self.client.series_race_results(2826) for standings in response: self.assertIsInstance(standings, historical_data.SeriesRaceResults) @async_test async def test_subsession_data(self): - response = await client.subsession_data(33618467) + response = await self.client.subsession_data(33618467) self.assertIsInstance(response, session_data.SubSessionData) @async_test async def test_world_records(self): - response = await client.world_records(2019, 1, 1, 1) + response = await self.client.world_records(2019, 1, 1, 1) for record in response: self.assertIsInstance(record, historical_data.WorldRecords) @async_test async def test_event_results(self): - response = await client.event_results(499343, 3) + response = await self.client.event_results(499343, 3) for result in response: self.assertIsInstance(result, historical_data.EventResults) - - -if __name__ == '__main__': - unittest.main() From c7c55531632a1305a5c24d2d7c4b8f1d902d0832 Mon Sep 17 00:00:00 2001 From: Mike Holler Date: Sun, 16 Aug 2020 12:04:52 -0500 Subject: [PATCH 2/2] Remove tags --- .github/workflows/integration_test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index acad6e6..d39c38d 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -3,8 +3,6 @@ name: Integration Test on: push: branches: master - tags: - - "*" schedule: # 4 AM Central Daylight Time - cron: '0 9 * * *'