Skip to content

Commit

Permalink
Merge pull request #1477 from bcgov/feat/daniel-requir-other-1455
Browse files Browse the repository at this point in the history
feat: Add validation rule for other_fuel_type
  • Loading branch information
dhaselhan authored Dec 18, 2024
2 parents 0f35a15 + 2a2cd6b commit 04a7a3b
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 16 deletions.
6 changes: 1 addition & 5 deletions backend/lcfs/tests/compliance_report/test_summary_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -810,8 +810,7 @@ async def test_calculate_fuel_quantities_renewable(
):
# Create a mock repository
mock_repo.aggregate_fuel_supplies.return_value = {"gasoline": 200.0}
mock_repo.aggregate_other_uses.return_value = {"diesel": 75.0}
mock_repo.aggregate_allocation_agreements.return_value = {"jet-fuel": 25.0}
mock_repo.aggregate_other_uses.return_value = {"diesel": 75.0, "jet-fuel": 25.0}

# Define test inputs
compliance_report_id = 2
Expand All @@ -830,7 +829,4 @@ async def test_calculate_fuel_quantities_renewable(
mock_repo.aggregate_other_uses.assert_awaited_once_with(
compliance_report_id, fossil_derived
)
mock_repo.aggregate_allocation_agreements.assert_awaited_once_with(
compliance_report_id
)
assert result == {"gasoline": 200.0, "diesel": 75.0, "jet-fuel": 25.0}
90 changes: 84 additions & 6 deletions backend/lcfs/tests/fuel_supply/test_fuel_supplies_validation.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
from unittest.mock import MagicMock, AsyncMock

import pytest
from fastapi import Request
from fastapi.exceptions import RequestValidationError

from lcfs.web.api.fuel_supply.repo import FuelSupplyRepository
from lcfs.web.api.fuel_code.repo import FuelCodeRepository
from lcfs.web.api.fuel_supply.schema import FuelSupplyCreateUpdateSchema
from lcfs.web.api.fuel_supply.validation import FuelSupplyValidation


@pytest.fixture
def fuel_supply_validation():
# Mock repositories
mock_fs_repo = MagicMock(spec=FuelSupplyRepository)
request = MagicMock(spec=Request)
mock_fc_repo = MagicMock(spec=FuelCodeRepository)

# Create the validation instance with mocked repositories
validation = FuelSupplyValidation(
request=request, fs_repo=mock_fs_repo
fs_repo=mock_fs_repo,
fc_repo=mock_fc_repo,
)
return validation, mock_fs_repo
return validation, mock_fs_repo, mock_fc_repo


@pytest.mark.anyio
async def test_check_duplicate(fuel_supply_validation):
validation, mock_fs_repo = fuel_supply_validation
validation, mock_fs_repo, _ = fuel_supply_validation
fuel_supply_data = FuelSupplyCreateUpdateSchema(
compliance_report_id=1,
fuel_type_id=1,
Expand All @@ -29,9 +33,83 @@ async def test_check_duplicate(fuel_supply_validation):
quantity=2000,
units="L",
)

mock_fs_repo.check_duplicate = AsyncMock(return_value=True)

result = await validation.check_duplicate(fuel_supply_data)

assert result is True
mock_fs_repo.check_duplicate.assert_awaited_once_with(fuel_supply_data)


@pytest.mark.anyio
async def test_validate_other_recognized_type(fuel_supply_validation):
validation, _, mock_fc_repo = fuel_supply_validation
# Mock a recognized fuel type (unrecognized = False)
mock_fc_repo.get_fuel_type_by_id = AsyncMock(
return_value=MagicMock(unrecognized=False)
)

fuel_supply_data = FuelSupplyCreateUpdateSchema(
compliance_report_id=1,
fuel_type_id=1, # Some recognized type ID
fuel_category_id=1,
provision_of_the_act_id=1,
quantity=2000,
units="L",
)

# Should not raise any error as fuel_type_other is not needed for recognized type
await validation.validate_other(fuel_supply_data)


@pytest.mark.anyio
async def test_validate_other_unrecognized_type_with_other(fuel_supply_validation):
validation, _, mock_fc_repo = fuel_supply_validation
# Mock an unrecognized fuel type
mock_fc_repo.get_fuel_type_by_id = AsyncMock(
return_value=MagicMock(unrecognized=True)
)

# Provide fuel_type_other since it's unrecognized
fuel_supply_data = FuelSupplyCreateUpdateSchema(
compliance_report_id=1,
fuel_type_id=99, # Assume 99 is unrecognized "Other" type
fuel_category_id=1,
provision_of_the_act_id=1,
quantity=2000,
units="L",
fuel_type_other="Some other fuel",
)

# Should not raise an error since fuel_type_other is provided
await validation.validate_other(fuel_supply_data)


@pytest.mark.anyio
async def test_validate_other_unrecognized_type_missing_other(fuel_supply_validation):
validation, _, mock_fc_repo = fuel_supply_validation
# Mock an unrecognized fuel type
mock_fc_repo.get_fuel_type_by_id = AsyncMock(
return_value=MagicMock(unrecognized=True)
)

# Missing fuel_type_other
fuel_supply_data = FuelSupplyCreateUpdateSchema(
compliance_report_id=1,
fuel_type_id=99, # Assume 99 is unrecognized "Other" type
fuel_category_id=1,
provision_of_the_act_id=1,
quantity=2000,
units="L",
)

# Should raise RequestValidationError since fuel_type_other is required
with pytest.raises(RequestValidationError) as exc:
await validation.validate_other(fuel_supply_data)

# Assert that the error message is as expected
errors = exc.value.errors()
assert len(errors) == 1
assert errors[0]["loc"] == ("fuelTypeOther",)
assert "required when using Other" in errors[0]["msg"]
8 changes: 8 additions & 0 deletions backend/lcfs/tests/internal_comment/test_internal_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from httpx import AsyncClient
from datetime import datetime

from lcfs.db.models import UserProfile
from lcfs.db.models.transfer.Transfer import Transfer, TransferRecommendationEnum
from lcfs.db.models.initiative_agreement.InitiativeAgreement import InitiativeAgreement
from lcfs.db.models.admin_adjustment.AdminAdjustment import AdminAdjustment
Expand Down Expand Up @@ -334,6 +335,13 @@ async def test_get_internal_comments_multiple_comments(
)
await add_models([transfer])

user = UserProfile(
keycloak_username="IDIRUSER",
first_name="Test",
last_name="User",
)
await add_models([user])

comments = []
for i in range(3):
internal_comment = InternalComment(
Expand Down
2 changes: 1 addition & 1 deletion backend/lcfs/tests/user/test_user_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ async def test_get_user_activities_as_manage_users_same_org(
add_models,
):
# Mock the current user as a user with MANAGE_USERS
set_mock_user(fastapi_app, [RoleEnum.MANAGE_USERS])
set_mock_user(fastapi_app, [RoleEnum.ADMINISTRATOR, RoleEnum.MANAGE_USERS])

# Assuming target user with user_profile_id=3 exists and is in organization_id=1
target_user_id = 1
Expand Down
23 changes: 20 additions & 3 deletions backend/lcfs/web/api/fuel_supply/validation.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
from fastapi import Depends, Request
from fastapi import Depends
from fastapi.exceptions import RequestValidationError

from lcfs.web.api.fuel_code.repo import FuelCodeRepository
from lcfs.web.api.fuel_supply.repo import FuelSupplyRepository
from lcfs.web.api.fuel_supply.schema import FuelSupplyCreateUpdateSchema


class FuelSupplyValidation:
def __init__(
self,
request: Request = None,
fs_repo: FuelSupplyRepository = Depends(FuelSupplyRepository),
fc_repo: FuelCodeRepository = Depends(FuelCodeRepository),
):
self.fs_repo = fs_repo
self.request = request
self.fc_repo = fc_repo

async def check_duplicate(self, fuel_supply: FuelSupplyCreateUpdateSchema):
return await self.fs_repo.check_duplicate(fuel_supply)

async def validate_other(self, fuel_supply: FuelSupplyCreateUpdateSchema):
fuel_type = await self.fc_repo.get_fuel_type_by_id(fuel_supply.fuel_type_id)

if fuel_type.unrecognized:
if not fuel_supply.fuel_type_other:
raise RequestValidationError(
[
{
"loc": ("fuelTypeOther",),
"msg": "required when using Other",
"type": "value_error",
}
]
)
1 change: 1 addition & 0 deletions backend/lcfs/web/api/fuel_supply/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ async def save_fuel_supply_row(
return await action_service.delete_fuel_supply(request_data, current_user_type)
else:
duplicate_id = await fs_validate.check_duplicate(request_data)
await fs_validate.validate_other(request_data)
if duplicate_id is not None:
duplicate_response = format_duplicate_error(duplicate_id)
return duplicate_response
Expand Down
5 changes: 4 additions & 1 deletion backend/lcfs/web/api/internal_comment/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ async def get_internal_comments(
List[InternalCommentResponseSchema]: A list of internal comments as data transfer objects.
"""
comments = await self.repo.get_internal_comments(entity_type, entity_id)
return [InternalCommentResponseSchema.from_orm(comment) for comment in comments]
return [
InternalCommentResponseSchema.model_validate(comment)
for comment in comments
]

@service_handler
async def get_internal_comment_by_id(
Expand Down
3 changes: 3 additions & 0 deletions backend/lcfs/web/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import os

from fastapi import HTTPException, Request
from fastapi.exceptions import RequestValidationError

from lcfs.services.clamav.client import VirusScanException
from lcfs.web.exception.exceptions import (
Expand Down Expand Up @@ -191,6 +192,8 @@ async def wrapper(request: Request, *args, **kwargs):
status_code=422,
detail="Viruses detected in file, please upload another",
)
except RequestValidationError as e:
raise e
except Exception as e:
context = extract_context()
log_unhandled_exception(logger, e, context, "view", func=func)
Expand Down

0 comments on commit 04a7a3b

Please sign in to comment.