|
18 | 18 | get_team_history, |
19 | 19 | handler, |
20 | 20 | update_s3_object, |
| 21 | + get_dict_value, |
| 22 | + get_config_file, |
21 | 23 | ) |
22 | 24 |
|
23 | 25 |
|
@@ -116,6 +118,22 @@ def test_get_and_update_copilot_teams_no_teams(self, mock_update_s3_object): |
116 | 118 | assert args[2] == "copilot_teams.json" |
117 | 119 | assert args[3] == [] |
118 | 120 |
|
| 121 | + def test_write_data_locally_creates_file(self, tmp_path): |
| 122 | + s3 = MagicMock() |
| 123 | + gh = MagicMock() |
| 124 | + response = MagicMock() |
| 125 | + response.links = {} |
| 126 | + gh.get.return_value = response |
| 127 | + |
| 128 | + with patch("src.main.get_copilot_team_date", return_value=[{"name": "teamA"}]): |
| 129 | + with patch("src.main.os.makedirs") as mock_makedirs, \ |
| 130 | + patch("src.main.open", create=True) as mock_open: |
| 131 | + result = get_and_update_copilot_teams(s3, gh, True) |
| 132 | + assert result == [{"name": "teamA"}] |
| 133 | + mock_makedirs.assert_called_once_with("output", exist_ok=True) |
| 134 | + mock_open.assert_called_once() |
| 135 | + s3.put_object.assert_not_called() |
| 136 | + |
119 | 137 |
|
120 | 138 | class TestGetTeamHistory: |
121 | 139 | def setup_method(self): |
@@ -412,6 +430,28 @@ def test_get_and_update_historic_usage_no_new_dates(self): |
412 | 430 | assert dates_added == [] |
413 | 431 | s3.put_object.assert_called_once() |
414 | 432 |
|
| 433 | + def test_write_data_locally_creates_file(self, tmp_path): |
| 434 | + s3 = MagicMock() |
| 435 | + gh = MagicMock() |
| 436 | + usage_data = [{"date": "2024-01-01", "usage": 10}] |
| 437 | + gh.get.return_value.json.return_value = usage_data |
| 438 | + |
| 439 | + # S3 get_object raises ClientError |
| 440 | + s3.get_object.side_effect = ClientError( |
| 441 | + error_response={"Error": {"Code": "404", "Message": "Not Found"}}, |
| 442 | + operation_name="GetObject", |
| 443 | + ) |
| 444 | + |
| 445 | + # Patch os.makedirs and open to use tmp_path |
| 446 | + 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() |
| 454 | + |
415 | 455 |
|
416 | 456 | class TestCreateDictionary: |
417 | 457 | def setup_method(self): |
@@ -490,3 +530,107 @@ def test_create_dictionary_no_new_history(self, caplog): |
490 | 530 | result = create_dictionary(gh, copilot_teams, existing_team_history) |
491 | 531 | assert result == [] |
492 | 532 | assert mock_get_team_history.call_count == 1 |
| 533 | + |
| 534 | + |
| 535 | +class TestGetTeamHistory: |
| 536 | + def setup_method(self): |
| 537 | + self.org_patch = patch("src.main.org", "test-org") |
| 538 | + self.org_patch.start() |
| 539 | + |
| 540 | + def teardown_method(self): |
| 541 | + self.org_patch.stop() |
| 542 | + |
| 543 | + def test_get_team_history_returns_metrics(self): |
| 544 | + gh = MagicMock() |
| 545 | + mock_response = MagicMock(spec=Response) |
| 546 | + mock_response.json.return_value = [{"date": "2024-01-01", "usage": 5}] |
| 547 | + gh.get.return_value = mock_response |
| 548 | + |
| 549 | + result = get_team_history(gh, "dev-team", {"since": "2024-01-01"}) |
| 550 | + gh.get.assert_called_once_with( |
| 551 | + "/orgs/test-org/team/dev-team/copilot/metrics", params={"since": "2024-01-01"} |
| 552 | + ) |
| 553 | + assert result == [{"date": "2024-01-01", "usage": 5}] |
| 554 | + |
| 555 | + def test_get_team_history_returns_empty_list(self): |
| 556 | + gh = MagicMock() |
| 557 | + mock_response = MagicMock(spec=Response) |
| 558 | + mock_response.json.return_value = [] |
| 559 | + gh.get.return_value = mock_response |
| 560 | + |
| 561 | + result = get_team_history(gh, "dev-team") |
| 562 | + gh.get.assert_called_once_with("/orgs/test-org/team/dev-team/copilot/metrics", params=None) |
| 563 | + assert result == [] |
| 564 | + |
| 565 | + def test_get_team_history_non_response_returns_none(self): |
| 566 | + gh = MagicMock() |
| 567 | + gh.get.return_value = "not_a_response" |
| 568 | + |
| 569 | + result = get_team_history(gh, "dev-team") |
| 570 | + gh.get.assert_called_once_with("/orgs/test-org/team/dev-team/copilot/metrics", params=None) |
| 571 | + assert result is None |
| 572 | + |
| 573 | + def test_get_team_history_with_query_params_none(self): |
| 574 | + gh = MagicMock() |
| 575 | + mock_response = MagicMock(spec=Response) |
| 576 | + mock_response.json.return_value = [{"date": "2024-01-01", "usage": 5}] |
| 577 | + gh.get.return_value = mock_response |
| 578 | + |
| 579 | + result = get_team_history(gh, "dev-team", None) |
| 580 | + gh.get.assert_called_once_with("/orgs/test-org/team/dev-team/copilot/metrics", params=None) |
| 581 | + assert result == [{"date": "2024-01-01", "usage": 5}] |
| 582 | + |
| 583 | + |
| 584 | +class TestGetDictValue: |
| 585 | + def test_get_dict_value_returns_value(self): |
| 586 | + d = {"foo": "bar", "baz": 42} |
| 587 | + assert get_dict_value(d, "foo") == "bar" |
| 588 | + assert get_dict_value(d, "baz") == 42 |
| 589 | + |
| 590 | + def test_get_dict_value_raises_for_missing_key(self): |
| 591 | + d = {"foo": "bar"} |
| 592 | + try: |
| 593 | + get_dict_value(d, "missing") |
| 594 | + except ValueError as e: |
| 595 | + assert str(e) == "Key missing not found in the dictionary." |
| 596 | + else: |
| 597 | + assert False, "ValueError not raised for missing key" |
| 598 | + |
| 599 | + def test_get_dict_value_returns_none_for_key_with_none_value(self): |
| 600 | + d = {"foo": None} |
| 601 | + try: |
| 602 | + get_dict_value(d, "foo") |
| 603 | + except ValueError as e: |
| 604 | + assert str(e) == "Key foo not found in the dictionary." |
| 605 | + else: |
| 606 | + assert False, "ValueError not raised for None value" |
| 607 | + |
| 608 | + |
| 609 | +class TestGetConfigFile: |
| 610 | + def test_get_config_file_success(self, tmp_path): |
| 611 | + config_data = {"features": {"show_log_locally": False}} |
| 612 | + config_path = tmp_path / "config.json" |
| 613 | + config_path.write_text(json.dumps(config_data), encoding="utf-8") |
| 614 | + |
| 615 | + result = get_config_file(str(config_path)) |
| 616 | + assert result == config_data |
| 617 | + |
| 618 | + def test_get_config_file_file_not_found(self): |
| 619 | + missing_path = "nonexistent_config.json" |
| 620 | + try: |
| 621 | + get_config_file(missing_path) |
| 622 | + except FileNotFoundError as e: |
| 623 | + assert missing_path in str(e) |
| 624 | + else: |
| 625 | + assert False, "FileNotFoundError not raised" |
| 626 | + |
| 627 | + def test_get_config_file_not_dict(self, tmp_path): |
| 628 | + config_path = tmp_path / "config.json" |
| 629 | + config_path.write_text(json.dumps([1, 2, 3]), encoding="utf-8") |
| 630 | + |
| 631 | + try: |
| 632 | + get_config_file(str(config_path)) |
| 633 | + except TypeError as e: |
| 634 | + assert "is not a dictionary" in str(e) |
| 635 | + else: |
| 636 | + assert False, "TypeError not raised for non-dict config" |
0 commit comments