1313
1414# Mock fuel code data structure
1515MockFuelCode = namedtuple (
16- "MockFuelCode" , ["fuel_code" , "contact_email" , "expiry_date" , "company" ]
16+ "MockFuelCode" , ["fuel_code_id" , " fuel_code" , "contact_email" , "expiry_date" , "company" ]
1717)
1818
1919
@@ -28,18 +28,21 @@ def mock_fuel_codes():
2828 """Mock fuel code data"""
2929 return [
3030 MockFuelCode (
31+ fuel_code_id = 1 ,
3132 fuel_code = "FC001" ,
3233 contact_email = "user1@example.com" ,
3334 expiry_date = date .today () + timedelta (days = 30 ),
3435 company = "Company A" ,
3536 ),
3637 MockFuelCode (
38+ fuel_code_id = 2 ,
3739 fuel_code = "FC002" ,
3840 contact_email = "user2@example.com" ,
3941 expiry_date = date .today () + timedelta (days = 60 ),
4042 company = "Company B" ,
4143 ),
4244 MockFuelCode (
45+ fuel_code_id = 3 ,
4346 fuel_code = "FC003" ,
4447 contact_email = "user1@example.com" , # Same email as FC001
4548 expiry_date = date .today () + timedelta (days = 45 ),
@@ -53,18 +56,21 @@ def mock_fuel_codes_invalid_emails():
5356 """Mock fuel code data with invalid emails"""
5457 return [
5558 MockFuelCode (
59+ fuel_code_id = 1 ,
5660 fuel_code = "FC001" ,
5761 contact_email = "invalid-email" ,
5862 expiry_date = date .today () + timedelta (days = 30 ),
5963 company = "Company A" ,
6064 ),
6165 MockFuelCode (
66+ fuel_code_id = 2 ,
6267 fuel_code = "FC002" ,
6368 contact_email = "user@valid.com" ,
6469 expiry_date = date .today () + timedelta (days = 60 ),
6570 company = "Company B" ,
6671 ),
6772 MockFuelCode (
73+ fuel_code_id = 3 ,
6874 fuel_code = "FC003" ,
6975 contact_email = "" ,
7076 expiry_date = date .today () + timedelta (days = 45 ),
@@ -96,6 +102,7 @@ async def test_notify_expiring_fuel_code_success(
96102 # Setup mocks
97103 mock_repo = AsyncMock ()
98104 mock_repo .get_expiring_fuel_codes .return_value = mock_fuel_codes
105+ mock_repo .mark_fuel_codes_notified = AsyncMock ()
99106 mock_repo_class .return_value = mock_repo
100107
101108 mock_email_repo = AsyncMock ()
@@ -117,6 +124,12 @@ async def test_notify_expiring_fuel_code_success(
117124 mock_email_service .send_fuel_code_expiry_notifications .call_count == 2
118125 )
119126
127+ # Verify that fuel codes are marked as notified after successful email sending
128+ mock_repo .mark_fuel_codes_notified .assert_called_once ()
129+ # All 3 fuel code IDs should be marked (1, 2, 3)
130+ notified_ids = mock_repo .mark_fuel_codes_notified .call_args [0 ][0 ]
131+ assert sorted (notified_ids ) == [1 , 2 , 3 ]
132+
120133 @pytest .mark .anyio
121134 @patch (
122135 "lcfs.scripts.tasks.fuel_code_expiry.settings.feature_fuel_code_expiry_email" ,
@@ -194,6 +207,7 @@ async def test_notify_expiring_fuel_code_email_service_failure(
194207
195208 mock_repo = AsyncMock ()
196209 mock_repo .get_expiring_fuel_codes .return_value = mock_fuel_codes
210+ mock_repo .mark_fuel_codes_notified = AsyncMock ()
197211 mock_repo_class .return_value = mock_repo
198212
199213 mock_email_repo = AsyncMock ()
@@ -206,6 +220,8 @@ async def test_notify_expiring_fuel_code_email_service_failure(
206220 result = await notify_expiring_fuel_code (mock_db_session )
207221
208222 assert result is False
223+ # No fuel codes should be marked as notified when all emails fail
224+ mock_repo .mark_fuel_codes_notified .assert_not_called ()
209225
210226 @pytest .mark .anyio
211227 async def test_notify_expiring_fuel_code_partial_success (
@@ -222,13 +238,14 @@ async def test_notify_expiring_fuel_code_partial_success(
222238
223239 mock_repo = AsyncMock ()
224240 mock_repo .get_expiring_fuel_codes .return_value = mock_fuel_codes
241+ mock_repo .mark_fuel_codes_notified = AsyncMock ()
225242 mock_repo_class .return_value = mock_repo
226243
227244 mock_email_repo = AsyncMock ()
228245 mock_email_repo_class .return_value = mock_email_repo
229246
230247 mock_email_service = AsyncMock ()
231- # First call succeeds, second fails
248+ # First call succeeds (Company A with codes 1,3), second fails (Company B with code 2)
232249 mock_email_service .send_fuel_code_expiry_notifications .side_effect = [
233250 True ,
234251 False ,
@@ -239,6 +256,12 @@ async def test_notify_expiring_fuel_code_partial_success(
239256
240257 assert result is True # Should return True if at least one email succeeded
241258
259+ # Only fuel codes from successful email should be marked as notified
260+ mock_repo .mark_fuel_codes_notified .assert_called_once ()
261+ notified_ids = mock_repo .mark_fuel_codes_notified .call_args [0 ][0 ]
262+ # Only codes 1 and 3 (Company A) should be marked since that email succeeded
263+ assert sorted (notified_ids ) == [1 , 3 ]
264+
242265 @pytest .mark .anyio
243266 @patch (
244267 "lcfs.scripts.tasks.fuel_code_expiry.settings.feature_fuel_code_expiry_email" ,
@@ -304,10 +327,10 @@ def test_group_codes_invalid_emails_fallback(self, mock_fuel_codes_invalid_email
304327 # Should have 2 email addresses (valid one + fallback)
305328 assert len (result ) == 2
306329 assert "user@valid.com" in result
307- assert "tfrs @gov.bc.ca" in result # fallback email
330+ assert "lcfs @gov.bc.ca" in result # fallback email
308331
309332 # Fallback should have 2 companies (invalid-email and empty email cases)
310- fallback_data = result ["tfrs @gov.bc.ca" ]
333+ fallback_data = result ["lcfs @gov.bc.ca" ]
311334 assert len (fallback_data ["companies" ]) == 2
312335 assert "Company A" in fallback_data ["companies" ]
313336 assert "Company C" in fallback_data ["companies" ]
@@ -373,6 +396,7 @@ async def test_notify_expiring_fuel_code_invalid_emails(
373396 mock_repo .get_expiring_fuel_codes .return_value = (
374397 mock_fuel_codes_invalid_emails
375398 )
399+ mock_repo .mark_fuel_codes_notified = AsyncMock ()
376400 mock_repo_class .return_value = mock_repo
377401
378402 mock_email_repo = AsyncMock ()
@@ -385,11 +409,16 @@ async def test_notify_expiring_fuel_code_invalid_emails(
385409 result = await notify_expiring_fuel_code (mock_db_session )
386410
387411 assert result is True
388- # Should have 3 email groups: valid email + tfrs @gov.bc.ca for 2 invalid companies
412+ # Should have 3 email groups: valid email + lcfs @gov.bc.ca for 2 invalid companies
389413 assert (
390414 mock_email_service .send_fuel_code_expiry_notifications .call_count == 3
391415 )
392416
417+ # Verify all fuel codes are marked as notified
418+ mock_repo .mark_fuel_codes_notified .assert_called_once ()
419+ notified_ids = mock_repo .mark_fuel_codes_notified .call_args [0 ][0 ]
420+ assert sorted (notified_ids ) == [1 , 2 , 3 ]
421+
393422 def test_edge_cases (self ):
394423 """Test edge cases for email validation"""
395424 edge_cases = [
@@ -435,9 +464,9 @@ async def test_end_to_end_workflow(self, mock_db_session):
435464 # Create test data
436465 test_codes = [
437466 MockFuelCode (
438- "FC001" , "valid@example.com" , date .today () + timedelta (days = 30 ), "Company A"
467+ 1 , "FC001" , "valid@example.com" , date .today () + timedelta (days = 30 ), "Company A"
439468 ),
440- MockFuelCode ("FC002" , "invalid-email" , date .today () + timedelta (days = 60 ), "Company B" ),
469+ MockFuelCode (2 , "FC002" , "invalid-email" , date .today () + timedelta (days = 60 ), "Company B" ),
441470 ]
442471
443472 with patch (
@@ -451,6 +480,7 @@ async def test_end_to_end_workflow(self, mock_db_session):
451480 # Setup mocks
452481 mock_repo = AsyncMock ()
453482 mock_repo .get_expiring_fuel_codes .return_value = test_codes
483+ mock_repo .mark_fuel_codes_notified = AsyncMock ()
454484 mock_repo_class .return_value = mock_repo
455485
456486 mock_email_repo = AsyncMock ()
@@ -488,6 +518,11 @@ async def test_end_to_end_workflow(self, mock_db_session):
488518 assert "contact_email" in context
489519 assert "expiry_count" in context
490520
521+ # Verify fuel codes are marked as notified
522+ mock_repo .mark_fuel_codes_notified .assert_called_once ()
523+ notified_ids = mock_repo .mark_fuel_codes_notified .call_args [0 ][0 ]
524+ assert sorted (notified_ids ) == [1 , 2 ]
525+
491526 @pytest .mark .anyio
492527 @patch (
493528 "lcfs.scripts.tasks.fuel_code_expiry.settings.feature_fuel_code_expiry_email" ,
@@ -507,6 +542,7 @@ async def test_logging_behavior(self, mock_db_session, mock_fuel_codes):
507542
508543 mock_repo = AsyncMock ()
509544 mock_repo .get_expiring_fuel_codes .return_value = mock_fuel_codes
545+ mock_repo .mark_fuel_codes_notified = AsyncMock ()
510546 mock_repo_class .return_value = mock_repo
511547
512548 mock_email_repo = AsyncMock ()
@@ -520,5 +556,5 @@ async def test_logging_behavior(self, mock_db_session, mock_fuel_codes):
520556
521557 # Verify that info logging occurred
522558 assert (
523- mock_logger .info .call_count >= 3
524- ) # At least start, found codes, and completion messages
559+ mock_logger .info .call_count >= 4
560+ ) # At least start, found codes, marking notified, and completion messages
0 commit comments