diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8c24b79 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +# Git +.git +.github +.githooks +.gitignore + +# Local environment and secrets +.venv +.env + +# Python cache and test cache +__pycache__ +.pytest_cache +.ruff_cache +.mypy_cache +.coverage +htmlcov + +# Build artifacts +dist +build +*.egg-info + +# Project docs not needed for the test image +docs \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b53865..3f87759 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,33 +4,9 @@ Thank you for your interest in contributing to ARGUS. ARGUS is a Python-based market analytics project focused on clean data workflows, reliable code, useful metrics and future AI-assisted monitoring. -This project is still growing, so contributions should help the project become more stable, understandable and useful step by step. +The project is still growing, so contributions should be small, focused and easy to review. You do not need to be an expert to contribute, but your changes should be understandable, reliable and related to the current project direction. -> [!IMPORTANT] -> ARGUS values reliability, clear communication and long-term skill building. -> Contributions should improve the project without creating unnecessary complexity. - ---- - -## Project Mindset - -ARGUS is not only about adding features quickly. - -The project is built around: - -- clean Python code -- understandable architecture -- reliable tests -- useful documentation -- careful data handling -- practical analytics -- continuous learning - -Good contributions should make the project easier to use, test, maintain or extend. - ---- - -## What You Can Contribute +Good starting points are issues labeled `good first issue`. These issues are usually smaller, easier to review and better suited for getting familiar with the project. Helpful contributions include: @@ -38,17 +14,14 @@ Helpful contributions include: - tests - documentation improvements - small refactorings -- validation improvements - analytics metrics -- chart improvements - data-source clients -- CI/CD improvements -- issue clarification -- architecture notes -- examples and usage instructions +- UI or chart improvements +- CI/CD and tooling improvements +- architecture or research notes -> [!NOTE] -> Large features should usually start with an issue or short discussion before implementation. +> [!IMPORTANT] +> Please keep changes focused and avoid adding unnecessary complexity. --- @@ -76,46 +49,45 @@ Bad examples: --- -## Development Setup +## Contribution Expectations -Clone the repository: +Contributors are expected to keep changes focused, understandable and related to the issue or task. -```bash -git clone https://github.com/BytecodeBrewer/argus.git -cd argus -``` +Please: -Create a virtual environment: +- explain your changes clearly +- be open to review feedback +- improve your contribution step by step after feedback +- avoid unrelated rewrites +- respect the existing architecture unless there is a clear reason to change it +- do not add scripts that automatically run `git add`, `git commit`, `git push` or create pull requests unless this was discussed first -```bash -python -m venv .venv -``` +A contribution may be declined or delayed if it: -Activate it. +- does not fit the current roadmap +- adds too much complexity too early +- breaks existing functionality +- lacks necessary checks or documentation +- duplicates existing work +- bypasses the repository workflow -On Windows PowerShell: +--- -```powershell -.venv\Scripts\Activate.ps1 -``` +## Branch Workflow -On macOS/Linux: +For issue-based work, create your branch from the related GitHub issue when possible. -```bash -source .venv/bin/activate -``` +GitHub may suggest a branch name based on the issue title. You can shorten it if the generated name is too long. -Install the project with development dependencies: +Good branch names are focused and describe the task: -```bash -pip install -e ".[dev]" +```text +43-research-forecasting-approach +33-add-yfinance-client +40-improve-test-coverage ``` ---- - -## Branch Workflow - -Create a new branch for your work: +If you create the branch manually, use: ```bash git checkout -b @@ -124,24 +96,30 @@ git checkout -b Example: ```bash -git checkout -b 12-add-volatility-metric +git checkout -b 43-research-forecasting-approach ``` -Use focused branch names that describe the work. - --- ## Commit Expectations Commits should be small, understandable and related to the current task. +ARGUS uses a conventional commit style with an issue reference: + +```text +type(#issue): short description +``` + Good commit messages: ```text -Add rolling volatility metric -Fix currency validation edge case -Update README setup instructions -Add tests for trend metrics +docs(#43): research first forecasting approach +feat(#33): add yfinance historical data client +test(#40): add tests for conversion service +fix(#33): handle empty historical data response +refactor(#34): split metric helpers +ci(#10): update commit message workflow ``` Avoid unclear messages: @@ -155,21 +133,25 @@ final ``` > [!TIP] -> A good commit tells future readers what changed and why it belongs to the task. +> A good commit tells future readers what changed and which issue it belongs to. --- -## Testing +## Checks -Before opening a pull request, run the test suite: +Before opening a pull request, run the project checks: ```bash pytest +ruff check . +ruff format --check . ``` -A pull request should not be opened as ready for review if tests are failing without explanation. +These checks verify that tests pass, code style is valid and formatting is consistent. + +A pull request should not be marked as ready for review if checks are failing without explanation. -If a test fails and you do not know why, mention it clearly in the pull request. +If a check fails and you are unsure why, mention it clearly in the pull request. > [!IMPORTANT] > CI checks must pass before a pull request can be merged. @@ -178,65 +160,23 @@ If a test fails and you do not know why, mention it clearly in the pull request. ## Pull Request Expectations -A good pull request should include: +Pull requests should target `develop` unless the maintainer explicitly says otherwise. -- a clear title -- a short explanation of what changed -- a link to the related issue if available -- notes about tests -- screenshots for UI changes if useful -- a short explanation of any trade-offs +Do not open feature, research or documentation pull requests directly against `main`. +The `main` branch is reserved for stable/release-ready changes. -Pull requests should be focused and reviewable. +Please use the pull request template and fill it out clearly. -Before opening a pull request, run: +The template helps reviewers understand: -```bash -pytest -ruff check . -ruff format --check . -``` - ---- - -## Reliability Expectations - -Contributors are expected to work reliably. +- what changed +- which issue is related +- whether tests were run +- whether documentation or screenshots are needed +- if there are any notes or trade-offs -This means: - -- do not submit random or unfinished code without context -- do not ignore failing tests -- do not introduce secrets, API keys or local machine paths -- do not rewrite unrelated parts of the project without discussion -- communicate if you are unsure -- keep changes understandable for future contributors -- respect the existing architecture unless there is a clear reason to change it - -Reliability does not mean knowing everything already. - -It means being honest, careful and consistent. - ---- - -## Learning Mindset - -ARGUS welcomes contributors who want to improve their technical skills. - -You do not need to be an expert to contribute. - -Helpful behavior includes: - -- asking clear questions -- explaining your reasoning -- being open to review feedback -- improving your code after feedback -- learning from tests, errors and architecture discussions -- documenting what you learned when it helps others - -> [!NOTE] -> This project values skill growth. -> A thoughtful small contribution is better than a large unclear one. +Do not bypass the pull request template or replace it with an unrelated auto-generated description. +It makes reviewing harder and may delay the merge. --- @@ -265,25 +205,14 @@ For analytics code: ## Secrets and API Keys -Never commit secrets. - -Do not commit: +Never commit secrets, API keys, tokens, passwords, `.env` files or local config files with private data. -- API keys -- tokens -- passwords -- `.env` files -- local config files with private data - -Use a local `.env` file for secrets. +Use a local `.env` file for secrets: ```env -api_key=your_api_key_here +EXCHANGE_RATE_API_KEY=your_api_key_here ``` -> [!WARNING] -> If you accidentally commit a secret, revoke it immediately and inform the maintainer. - --- ## Documentation @@ -299,44 +228,4 @@ Useful documentation includes: - data-source assumptions - troubleshooting notes -Repository-level files such as `README.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` and `LICENSE` belong in the repository root. - Technical notes, research and deeper explanations belong in `docs/`. - ---- - -## Communication - -Please communicate respectfully and constructively. - -When giving feedback: - -- focus on the code or idea, not the person -- explain the reason behind suggestions -- be specific -- stay open to alternatives - -When receiving feedback: - -- assume good intent -- ask questions if something is unclear -- improve the contribution step by step - -All contributors are expected to follow the project’s Code of Conduct. - ---- - -## Maintainer Notes - -The maintainer may ask for changes before merging a pull request. - -A contribution may be declined if it: - -- does not fit the current roadmap -- adds too much complexity too early -- breaks existing functionality -- lacks necessary tests -- duplicates existing work -- does not follow the project’s quality expectations - -This helps keep ARGUS stable, learnable and maintainable. \ No newline at end of file diff --git a/README.md b/README.md index 607f0d0..213fe1d 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ README.md - Tkinter - pytest -### Current data source +### Current data sources - ExchangeRate API for live currency conversion - yfinance for historical market-data retrieval and analytics @@ -140,8 +140,6 @@ Planned or likely future technologies include: ### Data processing -- pandas -- NumPy - possibly Polars later for larger datasets ### Storage @@ -153,14 +151,10 @@ Planned or likely future technologies include: ### Visualization and UI -- matplotlib -- Plotly - NiceGUI ### DevOps and deployment -- GitHub Actions -- Docker - Docker Compose - cloud deployment later @@ -199,6 +193,7 @@ Recommended for development: - VS Code - a virtual environment - pytest +- Docker, if you want to run tests in an isolated container environment > [!NOTE] > Runtime dependencies are managed through `pyproject.toml`. @@ -254,7 +249,7 @@ pip install -e ".[dev]" ## API Key Setup -ARGUS currently uses the ExchangeRate API for live currency conversion. +ARGUS uses the ExchangeRate API for live currency conversion. Historical analytics currently use yfinance and do not require an additional API key. ### 1. Create an API key @@ -284,7 +279,7 @@ The `.env` file must stay local and should never be committed. --- -## Running ARGUS +## Running ARGUS Locally Start the current Tkinter GUI: @@ -294,6 +289,22 @@ python -m argus.main This starts the local ARGUS prototype with calculator, currency conversion and basic analytics views. +## Running Argus in Docker + +ARGUS includes a minimal Docker setup for running the test suite in an isolated container environment. + +Build the Docker image: + +```bash +docker build -t argus . +``` + +Run ARGUS in a container: + +```bash +docker run --rm argus +``` + ### Legacy CLI / Debug Interface The legacy CLI is still available for quick local checks and debugging: @@ -310,7 +321,7 @@ python src/legacy/debug_main.py ## Running Tests -Run the test suite: +Run the test suite locally: ```bash pytest diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..1dce2e8 --- /dev/null +++ b/dockerfile @@ -0,0 +1,12 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY pyproject.toml README.md ./ +COPY src/ ./src/ +COPY tests/ ./tests/ + +RUN python -m pip install --upgrade pip \ + && pip install -e ".[dev]" + +CMD ["pytest"] \ No newline at end of file diff --git a/docs/forecast_research.md b/docs/forecast_research.md new file mode 100644 index 0000000..5db8c1b --- /dev/null +++ b/docs/forecast_research.md @@ -0,0 +1,38 @@ +# Research: First Forecasting Approach for Market Time Series + +## 1. Realistic First Prediction Task for ARGUS +A realistic first prediction task for ARGUS is **next-day exchange-rate movement** or **trend direction**. Predicting the exact next value (point forecast) is generally much harder and often less useful for trading/signal workflows than predicting the direction of the movement (up/down). A directional classification task serves as a simple, actionable signal for basic workflows. + +## 2. Baseline Methods to Implement First +Before jumping into complex models, the following baselines should be implemented to evaluate the added value of any machine learning model: +- **Naive last-value forecast**: The prediction for the next period is exactly the value from the current period. This is surprisingly hard to beat in random walk-like financial time series. +- **Moving average forecast**: A simple rolling average to predict the next value or determine trend direction. +- **Simple linear regression**: To capture basic linear trends over a given historical window. + +## 3. Libraries: NumPy, pandas, or scikit-learn? +The first implementation should use **pandas** and **scikit-learn**: +- **pandas**: Excellent for time-series manipulation, rolling windows, lagging features, and handling missing data. +- **scikit-learn**: Offers robust implementations of simple models (e.g., Linear Regression, Logistic Regression for direction) and provides standardized metrics and cross-validation tools designed for time series (e.g., `TimeSeriesSplit`). + +## 4. Evaluation Metrics +For the initial approaches, we should focus on: +- **Directional accuracy**: The percentage of times the model correctly predicts the direction of the price movement (up vs down). This is often more relevant than magnitude errors. +- **MAE (Mean Absolute Error)**: If point forecasting is used, MAE is more robust to outliers than RMSE and provides a linear penalty for errors. +- **RMSE (Root Mean Squared Error)**: Useful to penalize larger errors more heavily, but should be secondary to directional accuracy for basic signal generation. + +## 5. Why is LSTM not the first implementation step? +LSTMs are highly complex, require a large amount of well-structured data to train effectively without overfitting, and are notoriously difficult to tune. For financial time series, which suffer from low signal-to-noise ratios, an LSTM is likely to overfit the training data or collapse to predicting the last known value. Starting with an LSTM obscures whether the underlying data has any predictive power and sets a high barrier for debugging and infrastructure. + +## 6. Prerequisites for an LSTM Ticket +Before considering LSTMs or other deep learning approaches, the following must be established: +- A reliable data ingestion and preprocessing pipeline. +- Established baseline performance metrics (e.g., a naive model and a linear regression model) to compare against. +- Sufficient historical data size. +- A robust backtesting and cross-validation framework to ensure the LSTM isn't just memorizing data or overfitting. +- Hardware/infrastructure to support longer training times and hyperparameter tuning. + +## 7. Recommended First Implementation Approach +**Recommendation**: Start with **directional trend prediction** (predicting whether the next value is higher or lower than the current value) using a simple **Logistic Regression** model via **scikit-learn**. +- Use **pandas** to create basic lagged features (e.g., previous returns, moving averages). +- Evaluate using **directional accuracy**. +- Compare performance strictly against a **naive momentum** (predicting the trend continues) or **majority-class** baseline. diff --git a/docs/research-databases-and-storage.md b/docs/research-databases-and-storage.md new file mode 100644 index 0000000..2061a29 --- /dev/null +++ b/docs/research-databases-and-storage.md @@ -0,0 +1,383 @@ +# ARGUS Storage Research + +## Goal + +Research what ARGUS should store and which database/storage approach fits the project. + +ARGUS is moving from live API requests and in-memory analytics toward real data workflows. +The first storage decision should support local market analytics, SQL practice and future dashboard features without adding unnecessary infrastructure too early. + +--- + +## Storage Use Cases + +ARGUS should eventually store different kinds of data, but not all of them need to be implemented at once. + +Relevant storage use cases are: + +* historical exchange rates +* cleaned historical market data +* source information +* instruments that ARGUS can analyze +* later watchlists +* later generated reports +* later macroeconomic data +* later paper-trading history + +The first implementation should focus on historical market data and the basic entities needed to query it. + +--- + +## Storage Candidates + +ARGUS should compare storage options based on the current project phase. + +The project currently needs local analytical storage, not a full server or cloud database. + +### DuckDB + +DuckDB is a local analytical database. + +It is a strong fit for ARGUS because it supports SQL-based analytics without requiring a database server. + +Useful for: + +* historical market data +* local time-series analysis +* SQL practice +* Python-based analytics +* notebook-based exploration +* dashboard data preparation + +Limitations: + +* not a server database +* less suitable for multi-user product features later + +--- + +### SQLite + +SQLite is a simple local database. + +It is strong for small app storage and simple persistence. + +Useful for: + +* settings +* small app-state data +* simple local tables +* later watchlists +* lightweight metadata + +Limitations: + +* less analytics-focused than DuckDB +* not ideal as the main storage layer for historical market data +* better for app-state than analytical time-series queries + +--- + +### PostgreSQL + +PostgreSQL is a server-based relational database. + +It is a strong long-term option when ARGUS becomes more product-like. + +Useful for: + +* server-based storage +* user-facing features +* report history +* watchlists +* paper-trading history +* richer metadata +* cloud-ready architecture +* SQLGate usage later + +Limitations: + +* more setup than needed right now +* requires server or Docker setup +* adds infrastructure complexity too early + +Fit for ARGUS: + +PostgreSQL should be introduced later when ARGUS moves toward a server-based or cloud-ready architecture. + +--- + +## Local, Server and Cloud Options + +| Option | Meaning | Fit Now | Fit Later | +|---|---|---:|---:| +| Local storage | Database runs locally inside or next to the project | High | High | +| Server database | Database runs as a separate service, for example PostgreSQL | Medium | High | +| Cloud storage/database | Managed storage or database in the cloud | Low | High | + +ARGUS should start with local storage. + +Reason: + +* simpler setup +* easier learning curve +* good fit for a Python analytics project +* no cloud or server infrastructure required yet +* enough for historical data, metrics and dashboard development + +Server and cloud storage should come later when ARGUS has stronger product features such as reports, user state, paper-trading history or deployment needs. + +--- + +## Recommended First Storage Approach + +DuckDB should be the first storage technology for ARGUS. + +Reason: + +* ARGUS currently needs local analytical storage, not a full server database +* DuckDB fits historical time-series analysis well +* it supports SQL-based analytics without requiring a database server +* it works well with Python and notebook-based exploration +* it keeps the first storage implementation manageable +* it can later be replaced or complemented by PostgreSQL if ARGUS becomes more product-like + +The first storage implementation should focus on: + +* historical market data +* cleaned OHLCV-ready price data +* source information +* instruments that ARGUS can analyze + +PostgreSQL and SQLGate become more relevant later. + +For the first DuckDB phase, the goal is to build a clean local analytics workflow. + +--- + +## Developer Interaction Workflow + +ARGUS should use a practical developer workflow for DuckDB. + +The goal is to make the database easy to inspect, explore and validate before logic is moved into production code. + +### Notebook Exploration + +Notebooks should be the main exploration layer. + +They are useful for: + +* opening the DuckDB database +* testing SQL queries +* validating imported data +* comparing SQL results with pandas calculations +* exploring metric logic +* documenting research assumptions + +This workflow is especially useful before turning queries into reusable project code. + +Notebook exploration should be preferred over a GUI database tool in the first phase. + +### DuckDB CLI + +The DuckDB CLI should be used for quick database inspection. + +It is useful for: + +* checking available tables +* running small SQL queries +* validating stored records +* debugging the local database file + +The CLI is not the main research environment, but it is useful as a fast inspection tool. + +A GUI tool such as DBeaver can be tested if needed, but it should stay optional. + +--- + +## First Data Model Direction + +The first data model should support FX data now and broader market data later. + +ARGUS should not use a narrow `date | value` table as the main market-data model. + +That would work for simple exchange rates, but it would become limiting once ARGUS adds stocks, ETFs, indices or broader market APIs. + +The first model should focus on three tables: + +```text +data_sources +instruments +price_bars +``` + +### data_sources + +Stores where data came from. + +Recommended first fields: + +```text +id +name +provider_kind +requires_api_key +created_at +updated_at +``` + +Example: + +| name | provider_kind | requires_api_key | +|---|---|---:| +| Frankfurter | fx_rates | false | +| yfinance | market_prices | false | +| FRED | macro_data | true | + +### instruments + +Stores what ARGUS can analyze. + +Examples: + +* EUR/USD +* AAPL +* SPY +* S&P 500 +* BTC-USD + +Recommended first fields: + +```text +id +symbol +name +asset_class +currency +exchange +base_currency +quote_currency +created_at +updated_at +``` + +Example: + +| symbol | name | asset_class | currency | exchange | base_currency | quote_currency | +|---|---|---|---|---|---|---| +| EUR/USD | Euro / US Dollar | fx | null | null | EUR | USD | +| AAPL | Apple Inc. | stock | USD | NASDAQ | null | null | +| SPY | SPDR S&P 500 ETF | etf | USD | NYSE Arca | null | null | + +### price_bars + +Stores historical market data in an OHLCV-ready structure. + +Recommended first fields: + +```text +id +instrument_id +source_id +timestamp +timeframe +open +high +low +close +adjusted_close +volume +created_at +updated_at +``` + +For Frankfurter, the exchange rate can be stored in `close`. + +The other OHLCV fields can stay empty until ARGUS uses data sources that provide them. + +Example: + +| symbol | timestamp | timeframe | open | high | low | close | adjusted_close | volume | +|---|---|---|---:|---:|---:|---:|---:|---:| +| EUR/USD | 2024-01-02 | 1d | null | null | null | 1.095 | null | null | +| AAPL | 2024-01-02 | 1d | 187.15 | 188.44 | 183.89 | 185.64 | 184.25 | 50200000 | + +--- + +## Recommended First Implementation Step + +The first storage implementation should not be tied to one specific data provider. + +ARGUS currently works with an existing ExchangeRate API client and evaluates broader market data through yfinance. +Frankfurter may be added later as a stronger FX-oriented historical data source. + +The storage layer should therefore focus on a normalized internal market-data format instead of depending on one API response structure. + +Recommended first step: + +```text +active data client +→ normalize into instruments and price_bars +→ store in DuckDB +→ query with SQL +→ use results for analytics and charts +``` + +--- + +## Future Direction + +Later sprints can expand the storage layer step by step. + +Possible later additions: + +| Future Area | Possible Additions | +|---|---| +| Better source mapping | source-specific symbols, provider metadata | +| Watchlists | user-selected instruments | +| Reports | generated report metadata and history | +| Macro data | FRED indicators and observations | +| Paper trading | simulated orders, positions and portfolio history | +| Server architecture | PostgreSQL | +| SQL tooling | SQLGate with PostgreSQL | +| Cloud direction | managed PostgreSQL or cloud storage | + +SQLGate should be kept for a later PostgreSQL phase. + +It becomes useful when ARGUS moves toward: + +* server-based storage +* stronger database management +* richer metadata +* more stable application state +* user-facing features +* report history +* cloud-ready architecture + +Additional metadata such as documentation links, terms links or provider governance fields can also become useful later. + +For the first DuckDB phase, these details should stay in research documentation instead of the database schema. + +--- + +## Final Recommendation + +ARGUS should start with DuckDB as the first local analytics storage layer. + +DuckDB fits the current phase best because ARGUS needs local analytical SQL workflows, not a full server database yet. + +The first implementation should store historical market data in an OHLCV-ready structure. + +The recommended first data model is: + +```text +data_sources +instruments +price_bars +``` + +Notebook exploration should be the main developer workflow before SQL logic is moved into application code. + +The DuckDB CLI can be used for quick inspection. + +PostgreSQL and SQLGate should be introduced later when ARGUS moves toward a more product-like or cloud-based architecture. diff --git a/src/argus/clients/exchangerate_client.py b/src/argus/clients/exchangerate_client.py index 8ea5e89..d899718 100644 --- a/src/argus/clients/exchangerate_client.py +++ b/src/argus/clients/exchangerate_client.py @@ -24,6 +24,16 @@ def get_rates(curr1: str, curr2: str): resp.raise_for_status() payload = resp.json() + if payload["result"] == "success": + data["result"] = "success" + data["conversion_rate"] = payload["conversion_rate"] + return data + else: + data["result"] = "error" + data["error_type"] = payload.get("error_type") + check_error(data["error_type"]) + return None + except req.exceptions.Timeout: print("API hat zu lange gebraucht.") return None @@ -41,16 +51,6 @@ def get_rates(curr1: str, curr2: str): print("Unerwartete API-Antwortstruktur.") return None - if payload.get("result") == "success": - data["result"] = "success" - data["conversion_rate"] = payload.get("conversion_rate") - return data - else: - data["result"] = "error" - data["error_type"] = payload.get("error_type") - check_error(data["error_type"]) - return None - def check_error(err_type: str) -> None: """ diff --git a/tests/test_exchangerate_client.py b/tests/test_exchangerate_client.py index faf0864..1f15512 100644 --- a/tests/test_exchangerate_client.py +++ b/tests/test_exchangerate_client.py @@ -3,7 +3,7 @@ from argus.clients.exchangerate_client import get_rates, check_error -def test_check_currency_timeout(monkeypatch): +def test_check_currency_timeout(monkeypatch, capsys): def test_get_resp(url, timeout): raise req.exceptions.Timeout() @@ -12,8 +12,11 @@ def test_get_resp(url, timeout): data = get_rates("EUR", "USD") assert data is None + captured = capsys.readouterr() + assert "API hat zu lange gebraucht." in captured.out + -def test_check_currency_connection_error(monkeypatch): +def test_check_currency_connection_error(monkeypatch, capsys): def test_get_resp(url, timeout): raise req.exceptions.ConnectionError() @@ -22,8 +25,11 @@ def test_get_resp(url, timeout): data = get_rates("EUR", "USD") assert data is None + captured = capsys.readouterr() + assert "Keine Verbindung zur API." in captured.out + -def test_check_currency_request_exception(monkeypatch): +def test_check_currency_request_exception(monkeypatch, capsys): def test_get_resp(url, timeout): raise req.exceptions.RequestException("Testfehler") @@ -32,8 +38,11 @@ def test_get_resp(url, timeout): data = get_rates("EUR", "USD") assert data is None + captured = capsys.readouterr() + assert "Request fehlgeschlagen:" in captured.out + -def test_check_currency_value_error(monkeypatch): +def test_check_currency_value_error(monkeypatch, capsys): test_resp = Mock() test_resp.raise_for_status.return_value = None test_resp.json.side_effect = ValueError("Ungültige JSON-Antwort") @@ -46,12 +55,15 @@ def test_get_resp(url, timeout): data = get_rates("EUR", "USD") assert data is None + captured = capsys.readouterr() + assert "Fehler beim Verarbeiten der API-Antwort." in captured.out + -def test_check_currency_key_error(monkeypatch): +def test_check_currency_key_error(monkeypatch, capsys): test_resp = Mock() test_resp.raise_for_status.return_value = None test_resp.json.return_value = { - "result": "", + "result": "success", # not passing "success" bypases the "conversion_rate" checking "error_type": "", # "conversion_rate" fehlt absichtlich } @@ -64,6 +76,9 @@ def test_get_resp(url, timeout): data = get_rates("EUR", "USD") assert data is None + captured = capsys.readouterr() + assert "Unerwartete API-Antwortstruktur." in captured.out + def test_check_currency_valid(monkeypatch): test_resp = Mock() @@ -83,7 +98,7 @@ def test_get_resp(url, timeout): assert data == {"result": "success", "error_type": "", "conversion_rate": 1.2} -def test_check_currency_invalid(monkeypatch): +def test_check_currency_invalid(monkeypatch, capsys): test_resp = Mock() test_resp.raise_for_status.return_value = None test_resp.json.return_value = { @@ -100,6 +115,9 @@ def test_get_resp(url, timeout): data = get_rates("EUR", "USD") assert data is None + captured = capsys.readouterr() + assert "Invalid request! Please try again later." in captured.out + def test_check_error(capsys): check_error("unsupported-code") @@ -123,3 +141,7 @@ def test_check_error(capsys): captured.out == "Request limit reached! Please try again later or upgrade to exchangerate-api.com.\n" ) + + check_error("Some unknown Error") + captured = capsys.readouterr() + assert captured.out == "" diff --git a/tests/test_timeseries_service.py b/tests/test_timeseries_service.py index 7dd3c9f..cd5c97a 100644 --- a/tests/test_timeseries_service.py +++ b/tests/test_timeseries_service.py @@ -23,8 +23,9 @@ def test_get_a_full_timeseries(): "max_rate": [1.1055831909179688], } result = prepare_trend_analysis(test_curr, test_start, test_end, test_interval) - if result is None: - return False + + assert result is not None + result_df, result_dict = result result_df["date"] = result_df["date"].astype("str") result_dict["min_date"] = [str(result_dict["min_date"][0])] diff --git a/tests/test_validation_domain.py b/tests/test_validation_domain.py index a5bd41f..0166741 100644 --- a/tests/test_validation_domain.py +++ b/tests/test_validation_domain.py @@ -7,9 +7,13 @@ def test_op_is_valid(): - data = is_valid_op("+") - assert data is True + assert is_valid_op("+") is True + assert is_valid_op("-") is True + assert is_valid_op("*") is True + assert is_valid_op("/") is True + assert is_valid_op("%") is True + assert is_valid_op("**") is True def test_op_is_not_valid(): diff --git a/tests/test_yfinance_client.py b/tests/test_yfinance_client.py index faf15fc..6201b19 100644 --- a/tests/test_yfinance_client.py +++ b/tests/test_yfinance_client.py @@ -72,7 +72,7 @@ def test_error_raise(monkeypatch): def fake_yfinance_download( tickers=test_curr, start=test_start, end=test_end, interval=test_interval ): - return Exception("fake yfinance error") + raise Exception("fake yfinance error") monkeypatch.setattr("yfinance.download", fake_yfinance_download)