Skip to content

Commit eeaa542

Browse files
committed
introduce-testing-of-functions-and-mcp
1 parent 027557e commit eeaa542

19 files changed

Lines changed: 546 additions & 21 deletions

inspect2.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ if [ -z "${1:-}" ] || [ $1 == "run" ]; then
2424
-p 6277:6277 \
2525
-e BASE_URL="http://0.0.0.0:6274" \
2626
-e HOST=0.0.0.0 \
27-
-e 'ALLOWED_ORIGINS=http://0.0.0.0:6274,http://localhost:6274,http://127.0.0.1:6274' \
27+
-e 'ALLOWED_ORIGINS=*' \
2828
-e 'DEBUG=*' \
2929
-e LOG_LEVEL=debug \
3030
-e DANGEROUSLY_OMIT_AUTH=true \
31-
inspector
31+
inspector "$@"
3232
fi

pytest.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[pytest]
2+
testpaths = tests
3+
python_files = test_*.py
4+
python_classes = Test*
5+
python_functions = test_*
6+
addopts =
7+
-v
8+
--tb=short
9+
--strict-markers
10+
--disable-warnings
11+
markers =
12+
live: marks tests that use live data connection

requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ jwt
88
# mcp
99
mcp[cli]
1010
fastapi
11+
fastmcp
1112

1213
# tests
1314
pytest
15+
pytest-asyncio
1416
python-dotenv
17+
18+
# agent
19+
mcp-agent

run-mcp.sh

100644100755
File mode changed.

run.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@
2929

3030
# spendee = SpendeeApi(EMAIL, PASSWORD)
3131

32-
# accounts = spendee.wallet_get_all()
33-
# print('Available accounts:')
34-
# for acc in accounts:
35-
# print(f"- {acc.get('name', 'Unnamed')} (ID: {acc.get('id')}, Balance: {acc.get('balance')} {acc.get('currency')})")
36-
3732
spendee = SpendeeFirestore(EMAIL, PASSWORD)
3833

3934

@@ -42,31 +37,24 @@
4237
start='2025-08-01T00:00:00Z',
4338
end='2025-08-31T23:59:59Z',
4439
filters=[{"field": "category", "op": "=", "value": "Étkezés"}],
45-
)))
40+
)))# -30000 < x < -180000
4641

4742
print("Raiffeisen Szigetspicc label sum in 2025.08: " + str(spendee.aggregate_transactions(
4843
wallet_id='b368c5c2-68fe-4f98-9d4f-08e0cdca57a7',
4944
start='2025-08-01T00:00:00Z',
5045
end='2025-08-31T23:59:59Z',
5146
filters=[{"field": "labels", "op": "array-contains", "value": "szigetspicc"}],
52-
)))
47+
))) # -50000 < x < -60000
48+
5349

54-
#print(spendee.list_labels(as_json=True))
55-
#print(spendee.list_categories(as_json=True))
5650
#print(spendee._get_raw_transaction('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', 'a15d8379-6884-4e7d-a007-a1748b62e9d3', as_json=True))
5751
#print(spendee._get_raw_transaction('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', 'd2b4caa7-12eb-4c04-a744-d2bf7e02bdd2', as_json=True))
58-
# Example raw document dumps using the new helper. Paste these outputs back here to expand the schema.
5952

53+
# Example raw document dumps using the new helper. Paste these outputs back here to expand the schema.
6054
#print(spendee._get_raw_document(f"users/{spendee.user_id}", as_json=True))
6155

62-
#print(spendee._list_raw_wallets(as_json=True))
63-
64-
65-
#print("listwallets: ", spendee.list_wallets())
56+
# ---
6657
#print("rafi balance: ", spendee.get_wallet_balance('Rafi'))
67-
# for wallet in wallets:
68-
# print(wallet['name'])
69-
# print(spendee.get_wallet_balance(wallet['id'], start='2025-07-02T00:00:00Z'))
7058

7159
#print(spendee.list_transactions('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', start='2025-08-01T00:00:00Z', filters=[{"field": "amount", "op": ">=", "value": 5000}], as_json=True))
7260
#print(spendee.list_transactions('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', start='2025-08-01T00:00:00Z', filters=[{"field": "note", "op": "~=", "value": "GYED"}], as_json=True))
@@ -92,8 +80,6 @@
9280
# 'category': "Szórakozás"
9381
# }
9482
# )
95-
# print(spendee._get_raw_transaction('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', 'd2b4caa7-12eb-4c04-a744-d2bf7e02bdd2', as_json=True))
96-
#print(spendee.get_transaction('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', '875ebb1c-e03d-433f-a6c9-be0316f6c838', as_json=True))
9783
#print(spendee._get_transation_labels('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', '875ebb1c-e03d-433f-a6c9-be0316f6c838', as_json=True))
9884

9985
# print(spendee.list_transactions('b368c5c2-68fe-4f98-9d4f-08e0cdca57a7', start='2025-08-01T00:00:00Z', filters=[{"field": "category", "op": "=", "value": None}], fields=["id", "note", "category"], as_json=True))

run_tests.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/bash
2+
3+
# Test runner script for spendee-python-client
4+
5+
echo "Running Spendee Python Client tests..."
6+
7+
# Check if .env file exists
8+
if [ ! -f .env ]; then
9+
echo "Error: .env file not found. Please create one with EMAIL and PASSWORD variables."
10+
exit 1
11+
fi
12+
13+
# Run pytest with appropriate options
14+
python -m pytest tests/ -v --tb=short
15+
16+
echo "Tests completed."

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Tests package for spendee-python-client

tests/firestore_client/README.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Firestore Client Test Fixtures
2+
3+
This directory contains shared fixtures for testing the SpendeeFirestore client. The fixtures are defined in `conftest.py` and can be used across all test files in this directory.
4+
5+
## How Fixtures Work
6+
7+
Pytest automatically discovers fixtures defined in `conftest.py` files. When you define a fixture in a `conftest.py` file, it becomes available to all test files in that directory and its subdirectories.
8+
9+
## Available Shared Fixtures
10+
11+
### Authentication and Client Fixtures
12+
13+
- **`firestore_client`** (session scope): Authenticated SpendeeFirestore client instance
14+
- **`spendee_client`** (session scope): Alternative name for the same client (from parent conftest.py)
15+
16+
### Data Fixtures
17+
18+
- **`sample_wallet_id`** (session scope): A valid wallet ID for testing
19+
- **`sample_transaction_id`** (session scope): A valid transaction ID for testing
20+
- **`editable_transaction_id_with_wallet`** (session scope): Tuple of (wallet_id, transaction_id) for edit tests
21+
- **`test_categories`** (session scope): List of all available categories
22+
- **`test_labels`** (session scope): List of all available labels
23+
24+
### Sample Data Fixtures
25+
26+
- **`sample_transaction_data`** (function scope): Mock transaction data for testing
27+
- **`sample_filter_data`** (function scope): Mock filter data for testing
28+
- **`sample_date_range`** (function scope): Tuple of (start_date, end_date) in ISO format
29+
30+
### Error Testing Fixtures
31+
32+
- **`invalid_uuid`** (function scope): Invalid UUID string for testing error cases
33+
- **`invalid_date_format`** (function scope): Invalid date format for testing error cases
34+
35+
## How to Use Shared Fixtures
36+
37+
### Basic Usage
38+
39+
Simply add the fixture name as a parameter to your test function:
40+
41+
```python
42+
def test_my_function(firestore_client, sample_wallet_id):
43+
result = firestore_client.some_method(sample_wallet_id)
44+
assert result is not None
45+
```
46+
47+
### Using Multiple Fixtures
48+
49+
You can use multiple fixtures in a single test:
50+
51+
```python
52+
def test_complex_scenario(firestore_client, sample_wallet_id, test_categories, sample_date_range):
53+
# Your test logic here
54+
pass
55+
```
56+
57+
### Fixture Scopes
58+
59+
- **Session scope**: Fixture is created once per test session and reused
60+
- **Function scope**: Fixture is created fresh for each test function
61+
62+
## Adding New Shared Fixtures
63+
64+
To add a new shared fixture:
65+
66+
1. Open `conftest.py`
67+
2. Add your fixture function with the `@pytest.fixture` decorator
68+
3. Choose an appropriate scope (session, function, etc.)
69+
4. Document the fixture with a docstring
70+
71+
Example:
72+
73+
```python
74+
@pytest.fixture(scope="session")
75+
def my_new_fixture(firestore_client):
76+
"""
77+
Description of what this fixture provides.
78+
79+
Returns: Description of return value
80+
"""
81+
# Your fixture logic here
82+
return some_value
83+
```
84+
85+
## Best Practices
86+
87+
1. **Use session scope** for expensive operations (like authentication)
88+
2. **Use function scope** for simple data that can be recreated easily
89+
3. **Document your fixtures** with clear docstrings
90+
4. **Make fixtures reusable** by keeping them generic
91+
5. **Use descriptive names** for your fixtures
92+
6. **Test your fixtures** to ensure they work as expected
93+
94+
## Running Tests
95+
96+
To run tests in this directory:
97+
98+
```bash
99+
# Run all tests in the firestore_client directory
100+
pytest tests/firestore_client/
101+
102+
# Run a specific test file
103+
pytest tests/firestore_client/test_transactions.py
104+
105+
# Run tests with verbose output
106+
pytest tests/firestore_client/ -v
107+
108+
# Run tests and show fixture setup/teardown
109+
pytest tests/firestore_client/ -v --setup-show
110+
```
111+
112+
## Environment Variables
113+
114+
Make sure you have the following environment variables set in your `.env` file:
115+
116+
- `EMAIL`: Your Spendee account email
117+
- `PASSWORD`: Your Spendee account password
118+
119+
## Example Test File
120+
121+
See `test_shared_fixtures_example.py` for a complete example of how to use the shared fixtures.

tests/firestore_client/conftest.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Shared fixtures for firestore_client tests.
4+
5+
This module provides fixtures specific to the firestore_client test suite,
6+
building upon the global fixtures from the parent conftest.py.
7+
"""
8+
9+
import pytest
10+
import os
11+
from datetime import datetime, timedelta
12+
from dotenv import load_dotenv
13+
14+
# Load environment variables
15+
load_dotenv(os.path.join(os.path.dirname(__file__), '../../.env'))
16+
17+
@pytest.fixture(scope="session")
18+
def firestore_client():
19+
"""
20+
Shared SpendeeFirestore client for firestore_client tests.
21+
22+
This fixture creates a single authenticated client that can be reused
23+
across all firestore_client test modules.
24+
"""
25+
from spendee import SpendeeFirestore
26+
27+
email = os.getenv('EMAIL')
28+
password = os.getenv('PASSWORD')
29+
30+
if not email or not password:
31+
pytest.fail("EMAIL and PASSWORD environment variables required")
32+
else:
33+
client = SpendeeFirestore(email, password)
34+
return client
35+
36+
37+
@pytest.fixture(scope="session")
38+
def sample_wallet_id(firestore_client):
39+
"""
40+
Get a sample wallet ID for testing.
41+
42+
Returns a specific wallet ID that's known to exist in the test environment.
43+
"""
44+
wallets = firestore_client.list_wallets()
45+
if not wallets:
46+
pytest.skip("No wallets available for testing")
47+
48+
# Try to find a specific wallet ID, or use the first one
49+
target_wallet_id = 'b368c5c2-68fe-4f98-9d4f-08e0cdca57a7'
50+
for wallet in wallets:
51+
if wallet['id'] == target_wallet_id:
52+
return target_wallet_id
53+
pytest.fail(f"Target wallet ID {target_wallet_id} not found in available wallets")
54+
55+
56+
@pytest.fixture(scope="session")
57+
def sample_transaction_id(firestore_client, sample_wallet_id):
58+
"""
59+
Get a sample transaction ID for testing.
60+
61+
Returns a transaction ID from the sample wallet.
62+
"""
63+
transactions = firestore_client.list_transactions(
64+
sample_wallet_id,
65+
fields=['id'],
66+
start=(datetime.now() - timedelta(days=60)).isoformat(),
67+
limit=1
68+
)
69+
70+
if not transactions:
71+
pytest.skip("No transactions available for testing")
72+
73+
return transactions[0]['id']
74+
75+
@pytest.fixture(scope="session")
76+
def editable_transaction_id_with_wallet(firestore_client):
77+
"""
78+
Get a wallet ID and transaction ID pair for testing edit operations.
79+
80+
Returns a tuple of (wallet_id, transaction_id) that can be used for editing.
81+
"""
82+
# Specific known editable transaction
83+
transaction_id = 'd2b4caa7-12eb-4c04-a744-d2bf7e02bdd2'
84+
wallet_id = 'b368c5c2-68fe-4f98-9d4f-08e0cdca57a7'
85+
86+
# Verify the transaction exists
87+
try:
88+
firestore_client.get_transaction(wallet_id, transaction_id)
89+
return wallet_id, transaction_id
90+
except Exception:
91+
pytest.skip("Editable transaction not available for testing")
92+
93+
@pytest.fixture(scope="session")
94+
def test_categories(firestore_client):
95+
"""
96+
Get all categories for testing.
97+
98+
Returns the list of all available categories.
99+
"""
100+
return firestore_client.list_categories()
101+
102+
@pytest.fixture(scope="session")
103+
def test_labels(firestore_client):
104+
"""
105+
Get all labels for testing.
106+
107+
Returns the list of all available labels.
108+
"""
109+
return firestore_client.list_labels()
110+
111+
@pytest.fixture
112+
def sample_date_range():
113+
"""
114+
Sample date range for testing.
115+
116+
Returns a tuple of (start_date, end_date) in ISO format.
117+
"""
118+
end_date = datetime.now()
119+
start_date = end_date - timedelta(days=30)
120+
121+
return start_date.isoformat(), end_date.isoformat()
122+
123+
@pytest.fixture
124+
def invalid_uuid():
125+
"""
126+
Invalid UUID for testing error cases.
127+
128+
Returns a string that is not a valid UUID.
129+
"""
130+
return 'invalid-uuid'
131+
132+
@pytest.fixture
133+
def invalid_date_format():
134+
"""
135+
Invalid date format for testing error cases.
136+
137+
Returns a string that is not a valid date format.
138+
"""
139+
return 'bad-date'

0 commit comments

Comments
 (0)