diff --git a/spoolman/api/v1/spool.py b/spoolman/api/v1/spool.py index dc7762a..12b8081 100644 --- a/spoolman/api/v1/spool.py +++ b/spoolman/api/v1/spool.py @@ -159,7 +159,7 @@ async def find( description=( 'Sort the results by the given field. Should be a comma-separate string with "field:direction" items.' ), - example="filament.name:asc,vendor.id:asc,location:desc", + example="filament.name:asc,filament.vendor.id:asc,location:desc", ), limit: Optional[int] = Query( default=None, diff --git a/spoolman/database/utils.py b/spoolman/database/utils.py index 03cb441..54fb3d8 100644 --- a/spoolman/database/utils.py +++ b/spoolman/database/utils.py @@ -19,7 +19,7 @@ def parse_nested_field(base_obj: type[models.Base], field: str) -> attributes.In """Parse a nested field string into a sqlalchemy field object.""" fields = field.split(".") if not hasattr(base_obj, fields[0]): - raise ValueError(f"Invalid field name '{field}'") + raise ValueError(f"Invalid field name '{field}', '{fields[0]}' is not a valid field on '{base_obj.__name__}'.") if fields[0] == "filament" and len(fields) == 1: raise ValueError("No field specified for filament") diff --git a/tests_integration/tests/spool/test_find.py b/tests_integration/tests/spool/test_find.py index 05700e8..a288e49 100644 --- a/tests_integration/tests/spool/test_find.py +++ b/tests_integration/tests/spool/test_find.py @@ -104,7 +104,6 @@ def spools( def test_find_all_spools(spools: Fixture): - """Test finding all non-archived spools in the database.""" # Execute result = httpx.get(f"{URL}/api/v1/spool") result.raise_for_status() @@ -115,7 +114,6 @@ def test_find_all_spools(spools: Fixture): def test_find_all_spools_including_archived(spools: Fixture): - """Test finding all spools in the database, including archived ones.""" # Execute result = httpx.get(f"{URL}/api/v1/spool?allow_archived=true") result.raise_for_status() @@ -134,9 +132,75 @@ def test_find_all_spools_including_archived(spools: Fixture): ) +def test_find_all_spools_sort_asc(spools: Fixture): + # Execute + result = httpx.get(f"{URL}/api/v1/spool?sort=location:asc") + result.raise_for_status() + + # Verify + spools_result = result.json() + assert len(spools_result) == 4 + assert spools_result[3] == spools.spools[0] + + +def test_find_all_spools_sort_desc(spools: Fixture): + # Execute + result = httpx.get(f"{URL}/api/v1/spool?sort=location:desc") + result.raise_for_status() + + # Verify + spools_result = result.json() + assert len(spools_result) == 4 + assert spools_result[0] == spools.spools[0] + + +@pytest.mark.parametrize( + "field_name", + [ + "id", + "registered", + "first_used", + "last_used", + "filament_id", + "used_weight", + "location", + "lot_nr", + "comment", + "archived", + "filament.id", + "filament.registered", + "filament.name", + "filament.vendor_id", + "filament.material", + "filament.price", + "filament.density", + "filament.diameter", + "filament.weight", + "filament.spool_weight", + "filament.article_number", + "filament.comment", + "filament.settings_extruder_temp", + "filament.settings_bed_temp", + "filament.color_hex", + "filament.vendor.id", + "filament.vendor.registered", + "filament.vendor.name", + "filament.vendor.comment", + ], +) +def test_find_all_spools_sort_fields(spools: Fixture, field_name: str): # noqa: ARG001 + """Test sorting by all fields.""" + # Execute + result = httpx.get(f"{URL}/api/v1/spool?sort={field_name}:asc") + result.raise_for_status() + + # Verify + spools_result = result.json() + assert len(spools_result) == 4 + + @pytest.mark.parametrize("field_name", ["filament_name", "filament.name"]) def test_find_spools_by_filament_name(spools: Fixture, field_name: str): - """Test finding spools by filament name.""" # Execute result = httpx.get( f"{URL}/api/v1/spool", @@ -164,7 +228,6 @@ def test_find_spools_by_empty_filament_name(spools: Fixture): @pytest.mark.parametrize("field_name", ["filament_id", "filament.id"]) def test_find_spools_by_filament_id(spools: Fixture, field_name: str): - """Test finding spools by filament id.""" # Execute result = httpx.get( f"{URL}/api/v1/spool", @@ -179,7 +242,6 @@ def test_find_spools_by_filament_id(spools: Fixture, field_name: str): @pytest.mark.parametrize("field_name", ["filament_material", "filament.material"]) def test_find_spools_by_filament_material(spools: Fixture, field_name: str): - """Test finding spools by filament material.""" # Execute result = httpx.get( f"{URL}/api/v1/spool", @@ -207,7 +269,6 @@ def test_find_spools_by_empty_filament_material(spools: Fixture): @pytest.mark.parametrize("field_name", ["vendor_name", "vendor.name"]) def test_find_spools_by_filament_vendor_name(spools: Fixture, field_name: str): - """Test finding spools by filament vendor name.""" # Execute result = httpx.get( f"{URL}/api/v1/spool", @@ -235,7 +296,6 @@ def test_find_spools_by_empty_filament_vendor_name(spools: Fixture): @pytest.mark.parametrize("field_name", ["vendor_id", "vendor.id"]) def test_find_spools_by_filament_vendor_id(spools: Fixture, field_name: str): - """Test finding spools by filament vendor id.""" # Execute result = httpx.get( f"{URL}/api/v1/spool", @@ -262,7 +322,6 @@ def test_find_spools_by_empty_filament_vendor_id(spools: Fixture): def test_find_spools_by_location(spools: Fixture): - """Test finding spools by location.""" # Execute result = httpx.get( f"{URL}/api/v1/spool", @@ -289,7 +348,6 @@ def test_find_spools_by_empty_location(spools: Fixture): def test_find_spools_by_lot_nr(spools: Fixture): - """Test finding spools by lot nr.""" # Execute result = httpx.get( f"{URL}/api/v1/spool",