Compare commits
47 Commits
@ -0,0 +1,44 @@
|
||||
# Unit Testing Guide - be-lcca
|
||||
|
||||
This document provides instructions on how to set up and run unit tests for the **be-lcca** project.
|
||||
|
||||
## 1. Preparation
|
||||
|
||||
### Install Dependencies
|
||||
Ensure you have all dependencies installed. This project uses `poetry`.
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
poetry install
|
||||
```
|
||||
|
||||
## 2. Configuration
|
||||
|
||||
### Pytest Configuration
|
||||
Ensure the `pytest.ini` file in the root directory points to the `unit` test folder:
|
||||
|
||||
```ini
|
||||
[pytest]
|
||||
testpaths = tests/unit
|
||||
python_files = test_*.py
|
||||
asyncio_mode = auto
|
||||
```
|
||||
|
||||
## 3. Running Tests
|
||||
|
||||
### Run Unit Tests
|
||||
To run all unit tests in the project:
|
||||
|
||||
```bash
|
||||
poetry run pytest tests/unit
|
||||
```
|
||||
|
||||
### Run Specific Unit Test File
|
||||
```bash
|
||||
poetry run pytest tests/unit/test_specific_feature.py
|
||||
```
|
||||
|
||||
## 4. Best Practices
|
||||
|
||||
- **Isolation**: Ensure tests do not rely on a live database; use local data structures or mock objects.
|
||||
- **Factory Boy**: Use factories for creating complex models in your tests.
|
||||
@ -0,0 +1,88 @@
|
||||
# Panduan Menjalankan Script Testing di BE LCCA Digital Twin
|
||||
|
||||
Proyek ini menggunakan **Pytest** sebagai framework pengujian. Infrastruktur testing terletak di direktori `tests/` dan dikonfigurasi untuk menangani sifat asynchronous dari aplikasi FastAPI serta isolasi database.
|
||||
|
||||
---
|
||||
|
||||
## **1. Persiapan Lingkungan (Environment Setup)**
|
||||
Pastikan Anda berada di root direktori proyek dan environment sudah siap.
|
||||
|
||||
### **Opsi A: Menggunakan Virtual Environment (Direkomendasikan)**
|
||||
Aktifkan `venv` sebelum menjalankan perintah apapun:
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install poetry
|
||||
poetry install
|
||||
```
|
||||
|
||||
### **Opsi B: Menggunakan Poetry**
|
||||
Jika Anda lebih suka menggunakan Poetry secara langsung tanpa aktivasi manual:
|
||||
```bash
|
||||
poetry run pytest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **2. Menjalankan Pengujian**
|
||||
|
||||
| Tujuan | Perintah |
|
||||
| :--- | :--- |
|
||||
| **Jalankan Unit Tests** | `pytest tests/unit` |
|
||||
| **Jalankan E2E Tests** | `pytest tests/e2e` |
|
||||
| **Jalankan semua test** | `pytest` |
|
||||
| **Tampilkan statement print** | `pytest -s` |
|
||||
| **Berhenti di kegagalan pertama** | `pytest -x` |
|
||||
| **Jalankan file spesifik** | `pytest tests/unit/test_example.py` |
|
||||
|
||||
> **Catatan**: Verbose output (`-v`) sudah aktif secara default di konfigurasi `pyproject.toml`.
|
||||
|
||||
---
|
||||
|
||||
## **3. Peringatan Penting (Caution for E2E Tests)**
|
||||
|
||||
⚠️ **PENTING**: Saat menjalankan pengujian **End-to-End (E2E)**, pastikan Anda menggunakan **Testing Database**.
|
||||
|
||||
* **JANGAN PERNAH** menjalankan E2E tests menggunakan database **Production** atau **Development**.
|
||||
* Pengujian E2E seringkali melakukan operasi manipulasi data (create, update, delete) dan pembersihan database secara otomatis yang dapat mengakibatkan **kehilangan data permanen**.
|
||||
* Selalu gunakan database terpisah (misalnya PostgreSQL instance khusus testing atau SQLite) yang aman untuk dihapus isinya sewaktu-waktu.
|
||||
|
||||
---
|
||||
|
||||
## **4. Gambaran Infrastruktur Testing**
|
||||
Direktori `tests/` berisi beberapa utility script yang memudahkan proses testing:
|
||||
|
||||
* **`conftest.py`**: Berisi fixture global. Sudah terkonfigurasi dengan:
|
||||
* `client`: `AsyncClient` untuk simulasi request API ke aplikasi FastAPI Anda.
|
||||
* `setup_db`: Secara otomatis membuat dan menghapus database test (SQLite in-memory) untuk setiap sesi pengujian.
|
||||
* **`factories.py`**: Menggunakan `factory-boy` untuk menghasilkan mock data untuk model Anda.
|
||||
* **`database.py`**: Mengonfigurasi session database untuk kebutuhan pengujian.
|
||||
|
||||
---
|
||||
|
||||
## **5. Menulis Test Pertama Anda**
|
||||
Agar `pytest` mengenali sebuah file sebagai test, file tersebut harus dinamai dengan format `test_*.py` atau `*_test.py`.
|
||||
|
||||
**Contoh (`tests/test_api.py`):**
|
||||
```python
|
||||
import pytest
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_status(client):
|
||||
"""Contoh pengujian menggunakan fixture 'client' dari conftest.py"""
|
||||
response = await client.get("/")
|
||||
assert response.status_code == 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **6. Tips Troubleshooting**
|
||||
* **Masalah Module Path**: Jika Anda menemui error `ModuleNotFoundError`, jalankan test dengan menambahkan direktori saat ini ke `PYTHONPATH`:
|
||||
```bash
|
||||
export PYTHONPATH=$PYTHONPATH:.
|
||||
pytest
|
||||
```
|
||||
* **Menjalankan Test yang Gagal Saja**: Untuk menghemat waktu, jalankan hanya test yang gagal pada sesi sebelumnya:
|
||||
```bash
|
||||
pytest --lf
|
||||
```
|
||||
@ -0,0 +1,59 @@
|
||||
# Updated Equipment Acquisition & Simulation Algorithm
|
||||
|
||||
This document outlines the refactored logic for equipment acquisition cost calculation and simulation forecasting, implemented in February 2026.
|
||||
|
||||
## 1. Timeline Definitions
|
||||
|
||||
The simulation follows a strict temporal alignment to ensure consistency across the fleet:
|
||||
|
||||
| Parameter | Value | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **Base Year** | `2015` | The target year for all "Value of Money" (Net Present Value) calculations. |
|
||||
| **Forecasting Start** | `2015` | The year from which future predictions and Economic Life reports begin. |
|
||||
| **Calculation Start** | `2014` | The technical sequence start ($seq = 0$) used to establish an initial state. |
|
||||
|
||||
---
|
||||
|
||||
## 2. Capital Cost Adjustment (Value of Money)
|
||||
|
||||
To account for the time value of money, both the **Initial Acquisition Cost** and the **Replacement Cost** are normalized to the **2015 Base Year** using the project's inflation rate.
|
||||
|
||||
### 2.1 Adjustment Formula
|
||||
|
||||
The value of any cost $V$ at a specific $Year$ is adjusted to its equivalent value in $2015$ using the following formula:
|
||||
|
||||
$$V_{2015} = \frac{V_{Year}}{(1 + r)^{(Year - 2015)}}$$
|
||||
|
||||
Where:
|
||||
- $V_{2015}$ = Adjusted value in 2015 terms.
|
||||
- $V_{Year}$ = Raw cost recorded in the database or Maximo.
|
||||
- $r$ = Inflation rate (from `lcc_ms_master`, defaults to $0.05$ if undefined).
|
||||
- $Year$ = The year the cost was recorded ($Y_{acq}$ or $Y_{replace}$).
|
||||
|
||||
### 2.2 Total Acquisition Cost
|
||||
|
||||
The total capital cost $C_{total}$ stored in the master data is the sum of the adjusted initial cost and the adjusted first detected replacement cost:
|
||||
|
||||
$$C_{total} = \frac{C_{initial}}{(1+r)^{(Y_{acq} - 2015)}} + \frac{C_{replace}}{(1+r)^{(Y_{replace} - 2015)}}$$
|
||||
|
||||
---
|
||||
|
||||
## 3. Maintenance Cost Suppression Logic
|
||||
|
||||
A specific business rule is applied to prevent "double counting" or distorted maintenance records during major equipment replacement years:
|
||||
|
||||
### 3.1 Replacement Year Rule
|
||||
In the **first year** where a `replace_cost > 0` is detected in Maximo ($Y_{replace}$):
|
||||
- All **Material Costs** are set to $0.0$.
|
||||
- All **Labor Costs** (and labor hours) are set to $0.0$.
|
||||
|
||||
### 3.2 Logic Rationale
|
||||
The replacement cost is treated as a capital expenditure (CAPEX) that restarts the equipment's life cycle. Standard maintenance (OPEX) for that specific year is ignored because the replacement action supersedes regular repair tasks.
|
||||
|
||||
---
|
||||
|
||||
## 4. Implementation Reference
|
||||
|
||||
The logic is primarily contained in:
|
||||
- `src/equipment/service.py`: `check_and_update_acquisition_data()` (Cost adjustments).
|
||||
- `src/modules/equipment/insert_actual_data.py`: `query_data()` (Timeline and cost suppression).
|
||||
@ -0,0 +1,4 @@
|
||||
[pytest]
|
||||
testpaths = tests/unit
|
||||
python_files = test_*.py
|
||||
asyncio_mode = auto
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,111 @@
|
||||
Traceback (most recent call last):
|
||||
File "<frozen runpy>", line 198, in _run_module_as_main
|
||||
File "<frozen runpy>", line 88, in _run_code
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pytest\__main__.py", line 9, in <module>
|
||||
raise SystemExit(pytest.console_main())
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 201, in console_main
|
||||
code = main()
|
||||
^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 156, in main
|
||||
config = _prepareconfig(args, plugins)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 341, in _prepareconfig
|
||||
config = pluginmanager.hook.pytest_cmdline_parse(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
|
||||
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
|
||||
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
|
||||
raise exception.with_traceback(exception.__traceback__)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\helpconfig.py", line 105, in pytest_cmdline_parse
|
||||
config = yield
|
||||
^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
|
||||
res = hook_impl.function(*args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1140, in pytest_cmdline_parse
|
||||
self.parse(args)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1494, in parse
|
||||
self._preparse(args, addopts=addopts)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1398, in _preparse
|
||||
self.hook.pytest_load_initial_conftests(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
|
||||
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
|
||||
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
|
||||
raise exception.with_traceback(exception.__traceback__)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\warnings.py", line 151, in pytest_load_initial_conftests
|
||||
return (yield)
|
||||
^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\capture.py", line 154, in pytest_load_initial_conftests
|
||||
yield
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
|
||||
res = hook_impl.function(*args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1222, in pytest_load_initial_conftests
|
||||
self.pluginmanager._set_initial_conftests(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 581, in _set_initial_conftests
|
||||
self._try_load_conftest(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 619, in _try_load_conftest
|
||||
self._loadconftestmodules(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 659, in _loadconftestmodules
|
||||
mod = self._importconftest(
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 710, in _importconftest
|
||||
mod = import_path(
|
||||
^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\pathlib.py", line 587, in import_path
|
||||
importlib.import_module(module_name)
|
||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\importlib\__init__.py", line 126, in import_module
|
||||
return _bootstrap._gcd_import(name[level:], package, level)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
|
||||
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
|
||||
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
|
||||
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\assertion\rewrite.py", line 184, in exec_module
|
||||
exec(co, module.__dict__)
|
||||
File "C:\dev\be-lcca\tests\conftest.py", line 20, in <module>
|
||||
from fastapi import Request
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\fastapi\__init__.py", line 7, in <module>
|
||||
from .applications import FastAPI as FastAPI
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\fastapi\applications.py", line 16, in <module>
|
||||
from fastapi import routing
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\fastapi\routing.py", line 34, in <module>
|
||||
from fastapi.dependencies.models import Dependant
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\fastapi\dependencies\models.py", line 5, in <module>
|
||||
from fastapi.security.base import SecurityBase
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\fastapi\security\__init__.py", line 1, in <module>
|
||||
from .api_key import APIKeyCookie as APIKeyCookie
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\fastapi\security\api_key.py", line 6, in <module>
|
||||
from starlette.requests import Request
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\starlette\requests.py", line 12, in <module>
|
||||
from starlette.formparsers import FormParser, MultiPartException, MultiPartParser
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\starlette\formparsers.py", line 17, in <module>
|
||||
import python_multipart as multipart
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\python_multipart\__init__.py", line 7, in <module>
|
||||
from .multipart import (
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\python_multipart\multipart.py", line 115, in <module>
|
||||
class MultipartState(IntEnum):
|
||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\enum.py", line 647, in __new__
|
||||
delattr(enum_class, '_singles_mask_')
|
||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\enum.py", line 752, in __delattr__
|
||||
super().__delattr__(attr)
|
||||
^^^^^^^
|
||||
KeyboardInterrupt
|
||||
@ -0,0 +1,155 @@
|
||||
Traceback (most recent call last):
|
||||
File "<frozen runpy>", line 198, in _run_module_as_main
|
||||
File "<frozen runpy>", line 88, in _run_code
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pytest\__main__.py", line 9, in <module>
|
||||
raise SystemExit(pytest.console_main())
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 201, in console_main
|
||||
code = main()
|
||||
^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 156, in main
|
||||
config = _prepareconfig(args, plugins)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 341, in _prepareconfig
|
||||
config = pluginmanager.hook.pytest_cmdline_parse(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
|
||||
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
|
||||
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
|
||||
raise exception.with_traceback(exception.__traceback__)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\helpconfig.py", line 105, in pytest_cmdline_parse
|
||||
config = yield
|
||||
^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
|
||||
res = hook_impl.function(*args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1140, in pytest_cmdline_parse
|
||||
self.parse(args)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1494, in parse
|
||||
self._preparse(args, addopts=addopts)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1398, in _preparse
|
||||
self.hook.pytest_load_initial_conftests(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
|
||||
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
|
||||
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
|
||||
raise exception.with_traceback(exception.__traceback__)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\warnings.py", line 151, in pytest_load_initial_conftests
|
||||
return (yield)
|
||||
^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\capture.py", line 154, in pytest_load_initial_conftests
|
||||
yield
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
|
||||
res = hook_impl.function(*args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1222, in pytest_load_initial_conftests
|
||||
self.pluginmanager._set_initial_conftests(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 581, in _set_initial_conftests
|
||||
self._try_load_conftest(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 619, in _try_load_conftest
|
||||
self._loadconftestmodules(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 659, in _loadconftestmodules
|
||||
mod = self._importconftest(
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 710, in _importconftest
|
||||
mod = import_path(
|
||||
^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\pathlib.py", line 587, in import_path
|
||||
importlib.import_module(module_name)
|
||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\importlib\__init__.py", line 126, in import_module
|
||||
return _bootstrap._gcd_import(name[level:], package, level)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
|
||||
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
|
||||
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
|
||||
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\assertion\rewrite.py", line 184, in exec_module
|
||||
exec(co, module.__dict__)
|
||||
File "C:\dev\be-lcca\tests\conftest.py", line 22, in <module>
|
||||
from src.main import app
|
||||
File "C:\dev\be-lcca\src\main.py", line 33, in <module>
|
||||
from src.api import api_router
|
||||
File "C:\dev\be-lcca\src\api.py", line 22, in <module>
|
||||
from src.simulations.router import router as simulations_router
|
||||
File "C:\dev\be-lcca\src\simulations\__init__.py", line 1, in <module>
|
||||
from .router import router
|
||||
File "C:\dev\be-lcca\src\simulations\router.py", line 17, in <module>
|
||||
from src.simulations.service import create, delete, get, get_all, run_simulation, update
|
||||
File "C:\dev\be-lcca\src\simulations\service.py", line 34, in <module>
|
||||
column.key for column in sa_inspect(MasterData).mapper.column_attrs if column.key != "id"
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\util\langhelpers.py", line 1257, in __get__
|
||||
obj.__dict__[self.__name__] = result = self.fget(obj)
|
||||
^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\mapper.py", line 3172, in column_attrs
|
||||
return self._filter_properties(properties.ColumnProperty)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\mapper.py", line 3225, in _filter_properties
|
||||
self._check_configure()
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\mapper.py", line 2401, in _check_configure
|
||||
_configure_registries({self.registry}, cascade=True)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\mapper.py", line 4213, in _configure_registries
|
||||
_do_configure_registries(registries, cascade)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\mapper.py", line 4254, in _do_configure_registries
|
||||
mapper._post_configure_properties()
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\mapper.py", line 2421, in _post_configure_properties
|
||||
prop.post_instrument_class(self)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\interfaces.py", line 1113, in post_instrument_class
|
||||
self.strategy.init_class_attribute(mapper)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\strategies.py", line 254, in init_class_attribute
|
||||
_register_attribute(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\strategies.py", line 126, in _register_attribute
|
||||
desc = attributes.register_attribute_impl(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\attributes.py", line 2605, in register_attribute_impl
|
||||
"_Dispatch[QueryableAttribute[Any]]", manager[key].dispatch
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\event\base.py", line 465, in __get__
|
||||
if hasattr(obj, "_slots_dispatch"):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\attributes.py", line 472, in __getattr__
|
||||
return getattr(self.comparator, key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\util\langhelpers.py", line 1332, in __getattr__
|
||||
return self._fallback_getattr(key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\properties.py", line 472, in _fallback_getattr
|
||||
return getattr(self.__clause_element__(), key)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\util\langhelpers.py", line 1319, in oneshot
|
||||
result = fn(*args, **kw)
|
||||
^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\properties.py", line 439, in _memoized_method___clause_element__
|
||||
return self._orm_annotate_column(self.prop.columns[0])
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\orm\properties.py", line 425, in _orm_annotate_column
|
||||
return col._annotate(annotations)._set_propagate_attrs(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\sql\annotation.py", line 129, in _annotate
|
||||
return Annotated._as_annotated_instance(self, values) # type: ignore
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\sql\annotation.py", line 277, in _as_annotated_instance
|
||||
return cls(element, values)
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\sql\elements.py", line 5313, in __init__
|
||||
Annotated.__init__(self, element, values)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\sqlalchemy\sql\annotation.py", line 289, in __init__
|
||||
self.__dict__ = element.__dict__.copy()
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
KeyboardInterrupt
|
||||
@ -0,0 +1,38 @@
|
||||
C:\dev\be-lcca\venv\Lib\site-packages\pytest_asyncio\plugin.py:247: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset.
|
||||
The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session"
|
||||
|
||||
warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))
|
||||
============================= test session starts =============================
|
||||
platform win32 -- Python 3.11.9, pytest-8.3.4, pluggy-1.5.0 -- C:\dev\be-lcca\venv\Scripts\python.exe
|
||||
cachedir: .pytest_cache
|
||||
rootdir: C:\dev\be-lcca
|
||||
configfile: pyproject.toml
|
||||
plugins: anyio-4.8.0, Faker-30.10.0, asyncio-1.3.0
|
||||
asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
|
||||
collecting ... collected 1 item
|
||||
|
||||
tests/test_healthcheck.py::test_healthcheck PASSED [100%]
|
||||
|
||||
============================== warnings summary ===============================
|
||||
venv\Lib\site-packages\pydantic\_internal\_config.py:295
|
||||
C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_config.py:295: PydanticDeprecatedSince20: Support for class-based `config` is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.10/migration/
|
||||
warnings.warn(DEPRECATION_MESSAGE, DeprecationWarning)
|
||||
|
||||
venv\Lib\site-packages\pydantic\fields.py:1042: 473 warnings
|
||||
C:\dev\be-lcca\venv\Lib\site-packages\pydantic\fields.py:1042: PydanticDeprecatedSince20: Using extra keyword arguments on `Field` is deprecated and will be removed. Use `json_schema_extra` instead. (Extra keys: 'nullable'). Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.10/migration/
|
||||
warn(
|
||||
|
||||
venv\Lib\site-packages\pydantic\_internal\_generate_schema.py:297: 115 warnings
|
||||
C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py:297: PydanticDeprecatedSince20: `json_encoders` is deprecated. See https://docs.pydantic.dev/2.10/concepts/serialization/#custom-serializers for alternatives. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.10/migration/
|
||||
warnings.warn(
|
||||
|
||||
src\database\core.py:115
|
||||
C:\dev\be-lcca\src\database\core.py:115: MovedIn20Warning: The ``declarative_base()`` function is now available as sqlalchemy.orm.declarative_base(). (deprecated since: 2.0) (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
|
||||
Base = declarative_base(cls=CustomBase)
|
||||
|
||||
tests/test_healthcheck.py::test_healthcheck
|
||||
C:\dev\be-lcca\venv\Lib\site-packages\httpx\_client.py:1437: DeprecationWarning: The 'app' shortcut is now deprecated. Use the explicit style 'transport=ASGITransport(app=...)' instead.
|
||||
warnings.warn(message, DeprecationWarning)
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
======================= 1 passed, 591 warnings in 0.95s =======================
|
||||
@ -0,0 +1,141 @@
|
||||
Traceback (most recent call last):
|
||||
File "<frozen runpy>", line 198, in _run_module_as_main
|
||||
File "<frozen runpy>", line 88, in _run_code
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pytest\__main__.py", line 9, in <module>
|
||||
raise SystemExit(pytest.console_main())
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 201, in console_main
|
||||
code = main()
|
||||
^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 156, in main
|
||||
config = _prepareconfig(args, plugins)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 341, in _prepareconfig
|
||||
config = pluginmanager.hook.pytest_cmdline_parse(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
|
||||
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
|
||||
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
|
||||
raise exception.with_traceback(exception.__traceback__)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\helpconfig.py", line 105, in pytest_cmdline_parse
|
||||
config = yield
|
||||
^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
|
||||
res = hook_impl.function(*args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1140, in pytest_cmdline_parse
|
||||
self.parse(args)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1494, in parse
|
||||
self._preparse(args, addopts=addopts)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1398, in _preparse
|
||||
self.hook.pytest_load_initial_conftests(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_hooks.py", line 513, in __call__
|
||||
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_manager.py", line 120, in _hookexec
|
||||
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 139, in _multicall
|
||||
raise exception.with_traceback(exception.__traceback__)
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\warnings.py", line 151, in pytest_load_initial_conftests
|
||||
return (yield)
|
||||
^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 122, in _multicall
|
||||
teardown.throw(exception) # type: ignore[union-attr]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\capture.py", line 154, in pytest_load_initial_conftests
|
||||
yield
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pluggy\_callers.py", line 103, in _multicall
|
||||
res = hook_impl.function(*args)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 1222, in pytest_load_initial_conftests
|
||||
self.pluginmanager._set_initial_conftests(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 581, in _set_initial_conftests
|
||||
self._try_load_conftest(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 619, in _try_load_conftest
|
||||
self._loadconftestmodules(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 659, in _loadconftestmodules
|
||||
mod = self._importconftest(
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\config\__init__.py", line 710, in _importconftest
|
||||
mod = import_path(
|
||||
^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\pathlib.py", line 587, in import_path
|
||||
importlib.import_module(module_name)
|
||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\importlib\__init__.py", line 126, in import_module
|
||||
return _bootstrap._gcd_import(name[level:], package, level)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
|
||||
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
|
||||
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
|
||||
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\_pytest\assertion\rewrite.py", line 184, in exec_module
|
||||
exec(co, module.__dict__)
|
||||
File "C:\dev\be-lcca\tests\conftest.py", line 22, in <module>
|
||||
from src.main import app
|
||||
File "C:\dev\be-lcca\src\main.py", line 33, in <module>
|
||||
from src.api import api_router
|
||||
File "C:\dev\be-lcca\src\api.py", line 18, in <module>
|
||||
from src.acquisition_cost.router import router as acquisition_data_router
|
||||
File "C:\dev\be-lcca\src\acquisition_cost\router.py", line 6, in <module>
|
||||
from src.acquisition_cost.schema import AcquisitionCostDataPagination, AcquisitionCostDataRead, AcquisitionCostDataCreate, AcquisitionCostDataUpdate, ListQueryParams
|
||||
File "C:\dev\be-lcca\src\acquisition_cost\schema.py", line 20, in <module>
|
||||
class AcquisitionCostDataCreate(AcquisitionCostDataBase):
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_model_construction.py", line 224, in __new__
|
||||
complete_model_class(
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_model_construction.py", line 602, in complete_model_class
|
||||
schema = cls.__get_pydantic_core_schema__(cls, handler)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\main.py", line 702, in __get_pydantic_core_schema__
|
||||
return handler(source)
|
||||
^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_schema_generation_shared.py", line 84, in __call__
|
||||
schema = self._handler(source_type)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 610, in generate_schema
|
||||
schema = self._generate_schema_inner(obj)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 879, in _generate_schema_inner
|
||||
return self._model_schema(obj)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 691, in _model_schema
|
||||
{k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 691, in <dictcomp>
|
||||
{k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 1071, in _generate_md_field_schema
|
||||
common_field = self._common_field_schema(name, field_info, decorators)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 1263, in _common_field_schema
|
||||
schema = self._apply_annotations(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 2056, in _apply_annotations
|
||||
schema = get_inner_schema(source_type)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_schema_generation_shared.py", line 84, in __call__
|
||||
schema = self._handler(source_type)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 2040, in inner_handler
|
||||
metadata_js_function = _extract_get_pydantic_json_schema(obj, schema)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 2403, in _extract_get_pydantic_json_schema
|
||||
return _extract_get_pydantic_json_schema(tp.__origin__, schema)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\dev\be-lcca\venv\Lib\site-packages\pydantic\_internal\_generate_schema.py", line 2402, in _extract_get_pydantic_json_schema
|
||||
if hasattr(tp, '__origin__') and not _typing_extra.is_annotated(tp):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2544.0_x64__qbz5n2kfra8p0\Lib\typing.py", line 470, in __getattr__
|
||||
def __getattr__(self, item):
|
||||
|
||||
KeyboardInterrupt
|
||||
@ -0,0 +1,51 @@
|
||||
C:\dev\be-lcca\venv\Lib\site-packages\pytest_asyncio\plugin.py:247: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset.
|
||||
The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session"
|
||||
|
||||
warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))
|
||||
============================= test session starts =============================
|
||||
platform win32 -- Python 3.11.9, pytest-8.3.4, pluggy-1.5.0 -- C:\dev\be-lcca\venv\Scripts\python.exe
|
||||
cachedir: .pytest_cache
|
||||
rootdir: C:\dev\be-lcca
|
||||
configfile: pytest.ini
|
||||
plugins: anyio-4.8.0, Faker-30.10.0, asyncio-1.3.0
|
||||
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
|
||||
collecting ... collected 0 items / 2 errors
|
||||
|
||||
=================================== ERRORS ====================================
|
||||
____________ ERROR collecting tests/unit/test_masterdata_logic.py _____________
|
||||
tests\unit\test_masterdata_logic.py:2: in <module>
|
||||
from src.masterdata.service import calculate_pmt
|
||||
src\masterdata\service.py:6: in <module>
|
||||
from src.database.service import search_filter_sort_paginate
|
||||
src\database\service.py:7: in <module>
|
||||
from .core import DbSession
|
||||
src\database\core.py:19: in <module>
|
||||
from src.config import SQLALCHEMY_DATABASE_URI, COLLECTOR_URI
|
||||
src\config.py:99: in <module>
|
||||
DEV_USERNAME = config("DEV_USERNAME")
|
||||
venv\Lib\site-packages\starlette\config.py:90: in __call__
|
||||
return self.get(key, cast, default)
|
||||
venv\Lib\site-packages\starlette\config.py:107: in get
|
||||
raise KeyError(f"Config '{key}' is missing, and has no default.")
|
||||
E KeyError: "Config 'DEV_USERNAME' is missing, and has no default."
|
||||
___________ ERROR collecting tests/unit/test_masterdata_service.py ____________
|
||||
tests\unit\test_masterdata_service.py:3: in <module>
|
||||
from src.masterdata.service import create, get
|
||||
src\masterdata\service.py:6: in <module>
|
||||
from src.database.service import search_filter_sort_paginate
|
||||
src\database\service.py:7: in <module>
|
||||
from .core import DbSession
|
||||
src\database\core.py:19: in <module>
|
||||
from src.config import SQLALCHEMY_DATABASE_URI, COLLECTOR_URI
|
||||
src\config.py:99: in <module>
|
||||
DEV_USERNAME = config("DEV_USERNAME")
|
||||
venv\Lib\site-packages\starlette\config.py:90: in __call__
|
||||
return self.get(key, cast, default)
|
||||
venv\Lib\site-packages\starlette\config.py:107: in get
|
||||
raise KeyError(f"Config '{key}' is missing, and has no default.")
|
||||
E KeyError: "Config 'DEV_USERNAME' is missing, and has no default."
|
||||
=========================== short test summary info ===========================
|
||||
ERROR tests/unit/test_masterdata_logic.py - KeyError: "Config 'DEV_USERNAME' ...
|
||||
ERROR tests/unit/test_masterdata_service.py - KeyError: "Config 'DEV_USERNAME...
|
||||
!!!!!!!!!!!!!!!!!!! Interrupted: 2 errors during collection !!!!!!!!!!!!!!!!!!!
|
||||
============================== 2 errors in 0.67s ==============================
|
||||
@ -1,69 +1,115 @@
|
||||
import asyncio
|
||||
from typing import AsyncGenerator, Generator
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.pool import StaticPool
|
||||
|
||||
import pytest
|
||||
from sqlalchemy_utils import drop_database, database_exists
|
||||
from starlette.config import environ
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
# from src.database import Base, get_db
|
||||
import os
|
||||
|
||||
# Set dummy environment variables for testing
|
||||
os.environ["DATABASE_HOSTNAME"] = "localhost"
|
||||
os.environ["DATABASE_CREDENTIAL_USER"] = "test"
|
||||
os.environ["DATABASE_CREDENTIAL_PASSWORD"] = "test"
|
||||
os.environ["COLLECTOR_CREDENTIAL_USER"] = "test"
|
||||
os.environ["COLLECTOR_CREDENTIAL_PASSWORD"] = "test"
|
||||
os.environ["DEV_USERNAME"] = "test"
|
||||
os.environ["DEV_PASSWORD"] = "test"
|
||||
|
||||
# import asyncio
|
||||
# from typing import AsyncGenerator, Generator
|
||||
# import pytest
|
||||
# import pytest_asyncio
|
||||
# from httpx import AsyncClient, ASGITransport
|
||||
# from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
||||
# from sqlalchemy.orm import sessionmaker
|
||||
# from sqlalchemy.pool import StaticPool
|
||||
# from fastapi import Request
|
||||
|
||||
# from src.main import app
|
||||
# from src.database.core import Base, get_db, get_collector_db
|
||||
# from src.auth.service import JWTBearer
|
||||
# from src.auth.model import UserBase
|
||||
|
||||
# Test database URL
|
||||
TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
|
||||
# # Import all models to register them with Base
|
||||
# import src.acquisition_cost.model
|
||||
# import src.equipment.model
|
||||
# import src.equipment_master.model
|
||||
# import src.manpower_cost.model
|
||||
# import src.manpower_master.model
|
||||
# import src.masterdata.model
|
||||
# import src.masterdata_simulations.model
|
||||
# import src.plant_fs_transaction_data.model
|
||||
# import src.plant_masterdata.model
|
||||
# import src.plant_transaction_data.model
|
||||
# import src.plant_transaction_data_simulations.model
|
||||
# import src.simulations.model
|
||||
# import src.uploaded_file.model
|
||||
# import src.yeardata.model
|
||||
|
||||
engine = create_async_engine(
|
||||
TEST_DATABASE_URL,
|
||||
connect_args={"check_same_thread": False},
|
||||
poolclass=StaticPool,
|
||||
)
|
||||
# # Test database URL
|
||||
# TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
|
||||
|
||||
async_session = sessionmaker(
|
||||
engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False,
|
||||
autocommit=False,
|
||||
autoflush=False,
|
||||
)
|
||||
# engine = create_async_engine(
|
||||
# TEST_DATABASE_URL,
|
||||
# connect_args={"check_same_thread": False},
|
||||
# poolclass=StaticPool,
|
||||
# )
|
||||
|
||||
# TestingSessionLocal = sessionmaker(
|
||||
# engine,
|
||||
# class_=AsyncSession,
|
||||
# expire_on_commit=False,
|
||||
# autocommit=False,
|
||||
# autoflush=False,
|
||||
# )
|
||||
|
||||
async def override_get_db() -> AsyncGenerator[AsyncSession, None]:
|
||||
async with async_session() as session:
|
||||
try:
|
||||
yield session
|
||||
await session.commit()
|
||||
except Exception:
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
await session.close()
|
||||
# def pytest_sessionfinish(session, exitstatus):
|
||||
# """
|
||||
# Called after whole test run finished, right before returning the exit status to the system.
|
||||
# Used here to dispose of all SQLAlchemy engines to prevent hanging.
|
||||
# """
|
||||
# from src.database.core import engine as db_engine, collector_engine
|
||||
|
||||
# async def dispose_all():
|
||||
# # Dispose of both test engine and production engines
|
||||
# await engine.dispose()
|
||||
# await db_engine.dispose()
|
||||
# await collector_engine.dispose()
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
# try:
|
||||
# loop = asyncio.get_event_loop()
|
||||
# if loop.is_running():
|
||||
# # If the loop is already running, we create a task
|
||||
# loop.create_task(dispose_all())
|
||||
# else:
|
||||
# loop.run_until_complete(dispose_all())
|
||||
# except Exception:
|
||||
# # Fallback for environment where no loop is available or loop is closed
|
||||
# try:
|
||||
# asyncio.run(dispose_all())
|
||||
# except Exception:
|
||||
# pass
|
||||
|
||||
# # Removed custom event_loop fixture
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop() -> Generator:
|
||||
loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||
yield loop
|
||||
loop.close()
|
||||
# @pytest_asyncio.fixture(autouse=True)
|
||||
# async def setup_db():
|
||||
# async with engine.begin() as conn:
|
||||
# await conn.run_sync(Base.metadata.create_all)
|
||||
# yield
|
||||
# async with engine.begin() as conn:
|
||||
# await conn.run_sync(Base.metadata.drop_all)
|
||||
|
||||
# async def override_get_db(request: Request = None):
|
||||
# async with TestingSessionLocal() as session:
|
||||
# yield session
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def setup_db() -> AsyncGenerator[None, None]:
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
yield
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.drop_all)
|
||||
# app.dependency_overrides[get_db] = override_get_db
|
||||
# app.dependency_overrides[get_collector_db] = override_get_db
|
||||
|
||||
# @pytest.fixture(autouse=True)
|
||||
# def mock_auth(monkeypatch):
|
||||
# async def mock_call(self, request: Request):
|
||||
# user = UserBase(user_id="test-id", name="test-user", role="admin")
|
||||
# request.state.user = user
|
||||
# return user
|
||||
# monkeypatch.setattr(JWTBearer, "__call__", mock_call)
|
||||
|
||||
@pytest.fixture
|
||||
async def client() -> AsyncGenerator[AsyncClient, None]:
|
||||
async with AsyncClient(app=app, base_url="http://test") as client:
|
||||
yield client
|
||||
# @pytest_asyncio.fixture
|
||||
# async def client() -> AsyncGenerator[AsyncClient, None]:
|
||||
# async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||
# yield client
|
||||
@ -1,3 +0,0 @@
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
|
||||
Session = scoped_session(sessionmaker())
|
||||
@ -1,33 +0,0 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from factory import (
|
||||
LazyAttribute,
|
||||
LazyFunction,
|
||||
Sequence,
|
||||
SubFactory,
|
||||
post_generation,
|
||||
SelfAttribute,
|
||||
)
|
||||
from factory.alchemy import SQLAlchemyModelFactory
|
||||
from factory.fuzzy import FuzzyChoice, FuzzyDateTime, FuzzyInteger, FuzzyText
|
||||
from faker import Faker
|
||||
from faker.providers import misc
|
||||
# from pytz import UTC
|
||||
|
||||
|
||||
from .database import Session
|
||||
|
||||
fake = Faker()
|
||||
fake.add_provider(misc)
|
||||
|
||||
|
||||
class BaseFactory(SQLAlchemyModelFactory):
|
||||
"""Base Factory."""
|
||||
|
||||
class Meta:
|
||||
"""Factory configuration."""
|
||||
|
||||
abstract = True
|
||||
sqlalchemy_session = Session
|
||||
sqlalchemy_session_persistence = "commit"
|
||||
@ -0,0 +1,24 @@
|
||||
import pytest
|
||||
from src.masterdata.service import calculate_pmt
|
||||
|
||||
def test_calculate_pmt_zero_rate():
|
||||
# PMT = -PV / nper when rate is 0
|
||||
pv = 1000
|
||||
nper = 10
|
||||
rate = 0
|
||||
result = calculate_pmt(rate, nper, pv)
|
||||
assert result == -100
|
||||
|
||||
def test_calculate_pmt_standard():
|
||||
# Example: Loan 1000, 5% rate, 2 periods
|
||||
# PMT = -1000 * (0.05 * (1.05)^2) / ((1.05)^2 - 1)
|
||||
# PMT = -1000 * (0.05 * 1.1025) / (0.1025)
|
||||
# PMT = -1000 * (0.055125) / (0.1025) = -537.8048...
|
||||
result = calculate_pmt(5, 2, 1000)
|
||||
assert round(result, 2) == -537.80
|
||||
|
||||
def test_calculate_pmt_percentage():
|
||||
# If rate > 1, it divides by 100
|
||||
result_5 = calculate_pmt(5, 10, 1000)
|
||||
result_05 = calculate_pmt(0.05, 10, 1000)
|
||||
assert result_5 == result_05
|
||||
@ -0,0 +1,39 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from src.masterdata.service import create, get
|
||||
from src.masterdata.schema import MasterDataCreate
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_masterdata_service():
|
||||
mock_db = AsyncMock()
|
||||
mock_db.add = MagicMock()
|
||||
masterdata_in = MasterDataCreate(
|
||||
name="Test",
|
||||
description="Desc",
|
||||
unit_of_measurement="unit",
|
||||
value_num=10.0,
|
||||
seq=1
|
||||
)
|
||||
|
||||
result = await create(db_session=mock_db, masterdata_in=masterdata_in)
|
||||
|
||||
assert result.name == "Test"
|
||||
mock_db.add.assert_called_once()
|
||||
mock_db.commit.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_masterdata_service():
|
||||
mock_db = AsyncMock()
|
||||
mock_db.add = MagicMock()
|
||||
mock_result = MagicMock()
|
||||
mock_masterdata = MagicMock()
|
||||
mock_masterdata.id = "test-id"
|
||||
|
||||
# Mock behavior of db_session.execute().scalars().one_or_none()
|
||||
mock_result.scalars.return_value.one_or_none.return_value = mock_masterdata
|
||||
mock_db.execute.return_value = mock_result
|
||||
|
||||
result = await get(db_session=mock_db, masterdata_id="test-id")
|
||||
|
||||
assert result.id == "test-id"
|
||||
mock_db.execute.assert_called_once()
|
||||
Loading…
Reference in New Issue