11import json
22import os
33from unittest .mock import MagicMock , call , patch
4+ from io import BytesIO
45
56from botocore .exceptions import ClientError
67from requests import Response
@@ -362,79 +363,120 @@ def teardown_method(self):
362363 def test_get_and_update_historic_usage_success (self ):
363364 s3 = MagicMock ()
364365 gh = MagicMock ()
365- # Mock usage data returned from GitHub API
366- usage_data = [
367- {"date" : "2024-01-01" , "usage" : 10 },
368- {"date" : "2024-01-02" , "usage" : 20 },
369- ]
370- gh .get .return_value .json .return_value = usage_data
366+
367+ # Mock API response
368+ api_response = {
369+ "download_links" : [
370+ "https://example.com/org_history_api_response.json"
371+ ]
372+ # There are other fields in the API response, but we don't need them for this test
373+ }
374+
375+ # Mock usage data returned from GitHub API
376+ fetched_usage_data = {"day_totals" : [
377+ {"day" : "2024-01-01" , "usage" : 10 },
378+ {"day" : "2024-01-02" , "usage" : 20 },
379+ ]}
380+
381+ gh .get .return_value .json .return_value = api_response
371382
372383 # Mock S3 get_object returns existing historic usage with one date
373- existing_usage = [{"date " : "2024-01-01" , "usage" : 10 }]
384+ existing_usage = [{"day " : "2024-01-01" , "usage" : 10 }]
374385 s3 .get_object .return_value = {
375- "Body" : MagicMock (
376- read = MagicMock (return_value = json .dumps (existing_usage ).encode ("utf-8" ))
377- )
386+ "Body" : BytesIO (json .dumps (existing_usage ).encode ("utf-8" ))
378387 }
379388
380- result , dates_added = get_and_update_historic_usage (s3 , gh , False )
389+ # Mock urllib.request.urlopen returns usage data from download_links
390+ # We always patch dependencies imported inside the function we're testing.
391+ # Test environment initialisation ends here.
392+ with patch ("src.main.urllib.request.urlopen" ) as mock_urlopen :
393+ mock_urlopen .return_value = BytesIO (json .dumps (fetched_usage_data ).encode ("utf-8" ))
394+ result , dates_added = get_and_update_historic_usage (s3 , gh , False )
395+
381396 assert result == [
382- {"date " : "2024-01-01" , "usage" : 10 },
383- {"date " : "2024-01-02" , "usage" : 20 },
397+ {"day " : "2024-01-01" , "usage" : 10 },
398+ {"day " : "2024-01-02" , "usage" : 20 },
384399 ]
385400 assert dates_added == ["2024-01-02" ]
386401 s3 .get_object .assert_called_once ()
387402 s3 .put_object .assert_called_once ()
388403 args , kwargs = s3 .put_object .call_args
389404 assert kwargs ["Bucket" ].endswith ("copilot-usage-dashboard" )
390- assert kwargs ["Key" ] == "historic_usage_data .json"
405+ assert kwargs ["Key" ] == "org_history .json"
391406 assert json .loads (kwargs ["Body" ].decode ("utf-8" )) == result
392407
393408 def test_get_and_update_historic_usage_no_existing_data (self , caplog ):
394409 s3 = MagicMock ()
395410 gh = MagicMock ()
396- usage_data = [{"date" : "2024-01-01" , "usage" : 10 }]
397- gh .get .return_value .json .return_value = usage_data
411+ api_response = {
412+ "download_links" : [
413+ "https://example.com/org_history_api_response.json"
414+ ]
415+ }
416+ fetched_usage_data = {"day_totals" : [
417+ {"day" : "2024-01-01" , "usage" : 10 },
418+ ]}
419+
420+ gh .get .return_value .json .return_value = api_response
398421
399422 # S3 get_object raises ClientError
400423 s3 .get_object .side_effect = ClientError (
401424 error_response = {"Error" : {"Code" : "404" , "Message" : "Not Found" }},
402425 operation_name = "GetObject" ,
403426 )
404427
405- result , dates_added = get_and_update_historic_usage (s3 , gh , False )
406- assert result == [{"date" : "2024-01-01" , "usage" : 10 }]
428+ with patch ("src.main.urllib.request.urlopen" ) as mock_urlopen :
429+ mock_urlopen .return_value = BytesIO (json .dumps (fetched_usage_data ).encode ("utf-8" ))
430+ result , dates_added = get_and_update_historic_usage (s3 , gh , False )
431+
432+ assert result == [{"day" : "2024-01-01" , "usage" : 10 }]
407433 assert dates_added == ["2024-01-01" ]
408434 s3 .put_object .assert_called_once ()
409435 assert any (
410- "Error getting historic_usage_data .json" in record .getMessage ()
436+ "Error getting org_history .json" in record .getMessage ()
411437 for record in caplog .records
412438 )
413439
414440 def test_get_and_update_historic_usage_no_new_dates (self ):
415441 s3 = MagicMock ()
416442 gh = MagicMock ()
417- usage_data = [{"date" : "2024-01-01" , "usage" : 10 }]
418- gh .get .return_value .json .return_value = usage_data
443+ api_response = {
444+ "download_links" : [
445+ "https://example.com/org_history_api_response.json"
446+ ]
447+ }
448+ fetched_usage_data = {"day_totals" : [
449+ {"day" : "2024-01-01" , "usage" : 10 },
450+ ]}
451+
452+ gh .get .return_value .json .return_value = api_response
419453
420454 # S3 get_object returns same date as usage_data
421- existing_usage = [{"date " : "2024-01-01" , "usage" : 10 }]
455+ existing_usage = [{"day " : "2024-01-01" , "usage" : 10 }]
422456 s3 .get_object .return_value = {
423- "Body" : MagicMock (
424- read = MagicMock (return_value = json .dumps (existing_usage ).encode ("utf-8" ))
425- )
457+ "Body" : BytesIO (json .dumps (existing_usage ).encode ("utf-8" ))
426458 }
459+ with patch ("src.main.urllib.request.urlopen" ) as mock_urlopen :
460+ mock_urlopen .return_value = BytesIO (json .dumps (fetched_usage_data ).encode ("utf-8" ))
461+ result , dates_added = get_and_update_historic_usage (s3 , gh , False )
427462
428- result , dates_added = get_and_update_historic_usage (s3 , gh , False )
429- assert result == [{"date" : "2024-01-01" , "usage" : 10 }]
463+ assert result == [{"day" : "2024-01-01" , "usage" : 10 }]
430464 assert dates_added == []
431465 s3 .put_object .assert_called_once ()
432466
433467 def test_write_data_locally_creates_file (self , tmp_path ):
434468 s3 = MagicMock ()
435469 gh = MagicMock ()
436- usage_data = [{"date" : "2024-01-01" , "usage" : 10 }]
437- gh .get .return_value .json .return_value = usage_data
470+ api_response = {
471+ "download_links" : [
472+ "https://example.com/org_history_api_response.json"
473+ ]
474+ }
475+ fetched_usage_data = {"day_totals" : [
476+ {"day" : "2024-01-01" , "usage" : 10 },
477+ ]}
478+
479+ gh .get .return_value .json .return_value = api_response
438480
439481 # S3 get_object raises ClientError
440482 s3 .get_object .side_effect = ClientError (
@@ -444,13 +486,15 @@ def test_write_data_locally_creates_file(self, tmp_path):
444486
445487 # Patch os.makedirs and open to use tmp_path
446488 with patch ("src.main.os.makedirs" ) as mock_makedirs , \
447- patch ("src.main.open" , create = True ) as mock_open :
448- result , dates_added = get_and_update_historic_usage (s3 , gh , True )
449- assert result == [{"date" : "2024-01-01" , "usage" : 10 }]
450- assert dates_added == ["2024-01-01" ]
451- mock_makedirs .assert_called_once_with ("output" , exist_ok = True )
452- mock_open .assert_called_once ()
453- s3 .put_object .assert_not_called ()
489+ patch ("src.main.open" , create = True ) as mock_open , \
490+ patch ("src.main.urllib.request.urlopen" ) as mock_urlopen :
491+ mock_urlopen .return_value = BytesIO (json .dumps (fetched_usage_data ).encode ("utf-8" ))
492+ result , dates_added = get_and_update_historic_usage (s3 , gh , True )
493+ assert result == [{"day" : "2024-01-01" , "usage" : 10 }]
494+ assert dates_added == ["2024-01-01" ]
495+ mock_makedirs .assert_called_once_with ("output" , exist_ok = True )
496+ mock_open .assert_called_once ()
497+ s3 .put_object .assert_not_called ()
454498
455499
456500class TestCreateDictionary :
0 commit comments