Skip to content

Commit 6bc654f

Browse files
committed
test(import): cover duplicate detection and price_per_unit auto-calc
1 parent a1635f0 commit 6bc654f

1 file changed

Lines changed: 96 additions & 1 deletion

File tree

tests/test_imports.py

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import tempfile
66
import pytest
77
from app import db as _db_ext
8-
from app.models import Vehicle, FuelLog
8+
from app.models import Vehicle, FuelLog, Expense, Trip, ChargingSession
99

1010

1111
# ---------------------------------------------------------------------------
@@ -690,6 +690,7 @@ def test_full_import_flow(self, auth_client, sample_vehicle):
690690
assert logs[0].odometer == 198042
691691
assert abs(logs[0].volume - 26.12) < 0.01
692692
assert abs(logs[0].total_cost - 44.12) < 0.01
693+
assert logs[0].price_per_unit == pytest.approx(44.12 / 26.12, abs=0.001)
693694
assert logs[0].is_full_tank is True
694695
assert logs[0].notes == 'Testtankung'
695696

@@ -715,3 +716,97 @@ def test_auto_suggest_maps_german_columns(self, auth_client, sample_vehicle):
715716
# The mapping page should auto-select fields for German column names
716717
# Check that "date" is pre-selected for "Datum"
717718
assert 'selected' in html
719+
720+
721+
# ---------------------------------------------------------------------------
722+
# Duplicate detection during CSV import
723+
# ---------------------------------------------------------------------------
724+
725+
class TestCsvImportDuplicateDetection:
726+
"""Importing the same CSV twice should skip duplicates on the second run."""
727+
728+
def _preview_and_execute(self, auth_client, vehicle_id, data_type, csv_bytes,
729+
mappings, date_format='auto'):
730+
"""Run preview→execute and return the final (followed) response."""
731+
preview_resp = auth_client.post(
732+
'/api/import/csv/preview',
733+
data={
734+
'data_type': data_type,
735+
'vehicle_id': str(vehicle_id),
736+
'file': (io.BytesIO(csv_bytes), 'import.csv'),
737+
},
738+
content_type='multipart/form-data',
739+
)
740+
assert preview_resp.status_code == 200
741+
form = {'data_type': data_type, 'vehicle_id': str(vehicle_id),
742+
'date_format': date_format}
743+
form.update(mappings)
744+
return auth_client.post(
745+
'/api/import/csv/execute', data=form,
746+
content_type='multipart/form-data',
747+
follow_redirects=True,
748+
)
749+
750+
def test_fuel_logs_skips_duplicates(self, auth_client, sample_vehicle):
751+
csv = b'date,odometer,volume,total_cost\n2024-03-01,20000,35.0,52.50\n'
752+
mappings = {'mapping_0': 'date', 'mapping_1': 'odometer',
753+
'mapping_2': 'volume', 'mapping_3': 'total_cost'}
754+
755+
self._preview_and_execute(auth_client, sample_vehicle.id,
756+
'fuel_logs', csv, mappings)
757+
assert FuelLog.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1
758+
759+
resp = self._preview_and_execute(auth_client, sample_vehicle.id,
760+
'fuel_logs', csv, mappings)
761+
html = resp.data.decode()
762+
assert '0 fuel logs imported' in html.lower()
763+
assert '1 duplicate' in html.lower()
764+
assert FuelLog.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1
765+
766+
def test_expenses_skips_duplicates(self, auth_client, sample_vehicle):
767+
csv = b'date,category,description,cost\n2024-03-01,maintenance,Oil change,75.00\n'
768+
mappings = {'mapping_0': 'date', 'mapping_1': 'category',
769+
'mapping_2': 'description', 'mapping_3': 'cost'}
770+
771+
self._preview_and_execute(auth_client, sample_vehicle.id,
772+
'expenses', csv, mappings)
773+
assert Expense.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1
774+
775+
resp = self._preview_and_execute(auth_client, sample_vehicle.id,
776+
'expenses', csv, mappings)
777+
html = resp.data.decode()
778+
assert '0 expenses imported' in html.lower()
779+
assert '1 duplicate' in html.lower()
780+
assert Expense.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1
781+
782+
def test_trips_skips_duplicates(self, auth_client, sample_vehicle):
783+
csv = b'date,start_odometer,end_odometer,purpose\n2024-03-01,20000,20050,business\n'
784+
mappings = {'mapping_0': 'date', 'mapping_1': 'start_odometer',
785+
'mapping_2': 'end_odometer', 'mapping_3': 'purpose'}
786+
787+
self._preview_and_execute(auth_client, sample_vehicle.id,
788+
'trips', csv, mappings)
789+
assert Trip.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1
790+
791+
resp = self._preview_and_execute(auth_client, sample_vehicle.id,
792+
'trips', csv, mappings)
793+
html = resp.data.decode()
794+
assert '0 trips imported' in html.lower()
795+
assert '1 duplicate' in html.lower()
796+
assert Trip.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1
797+
798+
def test_charging_sessions_skips_duplicates(self, auth_client, sample_vehicle):
799+
csv = b'date,kwh_added,total_cost,location\n2024-03-01,40.0,12.00,Home\n'
800+
mappings = {'mapping_0': 'date', 'mapping_1': 'kwh_added',
801+
'mapping_2': 'total_cost', 'mapping_3': 'location'}
802+
803+
self._preview_and_execute(auth_client, sample_vehicle.id,
804+
'charging_sessions', csv, mappings)
805+
assert ChargingSession.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1
806+
807+
resp = self._preview_and_execute(auth_client, sample_vehicle.id,
808+
'charging_sessions', csv, mappings)
809+
html = resp.data.decode()
810+
assert '0 charging sessions imported' in html.lower()
811+
assert '1 duplicate' in html.lower()
812+
assert ChargingSession.query.filter_by(vehicle_id=sample_vehicle.id).count() == 1

0 commit comments

Comments
 (0)