# Path: src/findings/finding_container.py
# class FindingContainer:
#     def __init__(self):
#         self.finding_results = []
# 
#     def add_finding(
#         self,
#         category: FindingCategory,
#         proto_file_name: str,
#         source_code_line: int,
#         change_type: ChangeType,
#         extra_info=None,
#         subject="",
#         oldsubject="",
#         context="",
#         type="",
#         oldtype="",
#     ):
#         self.finding_results.append(
#             Finding(
#                 category,
#                 proto_file_name,
#                 source_code_line,
#                 change_type,
#                 extra_info,
#                 subject=subject,
#                 oldsubject=oldsubject,
#                 context=context,
#                 type=type,
#                 oldtype=oldtype,
#             )
#         )
# 
#     def get_all_findings(self):
#         return sorted_filtered_findings(self.finding_results, lambda f: True)
# 
#     def get_actionable_findings(self):
#         return sorted_filtered_findings(
#             self.finding_results, lambda f: f.change_type == ChangeType.MAJOR
#         )
# 
#     def to_dict_arr(self):
#         return [finding.to_dict() for finding in self.finding_results]
# 
#     def to_human_readable_message(self, line_numbers=True):
#         output_message = ""
#         file_to_findings = defaultdict(list)
#         for finding in self.get_actionable_findings():
#             # Create a map to summarize the findings based on proto file name.
#             file_to_findings[finding.location.proto_file_name].append(finding)
#         # Add each finding to the output message.
#         for file_name, findings in file_to_findings.items():
#             # Customize sort key function to output the findings in the same
#             # file based on the source code line number.
#             # Sort message alphabetically if the line number is same.
#             sorted_findings = sorted_filtered_findings(findings, lambda f: True)
#             for finding in sorted_findings:
#                 message = finding.get_message()
#                 if finding.location.source_code_line == -1 or not line_numbers:
#                     output_message += f"{file_name}: {message}\n"
#                 else:
#                     output_message += (
#                         f"{file_name} L{finding.location.source_code_line}: {message}\n"
#                     )
#         return output_message
# 
# Path: src/findings/finding_category.py
# class FindingCategory(enum.Enum):
#     # Enums
#     ENUM_VALUE_ADDITION = 1
#     ENUM_VALUE_REMOVAL = 2
#     ENUM_VALUE_NAME_CHANGE = 3
#     ENUM_ADDITION = 4
#     ENUM_REMOVAL = 5
#     # Fields
#     FIELD_ADDITION = 6
#     FIELD_REMOVAL = 7
#     FIELD_NAME_CHANGE = 8
#     FIELD_REPEATED_CHANGE = 9
#     FIELD_TYPE_CHANGE = 10
#     FIELD_ONEOF_MOVE_OUT = 11
#     FIELD_ONEOF_MOVE_IN = 12
#     FIELD_PROTO3_OPTIONAL_CHANGE = 13
#     # Field annotations
#     FIELD_BEHAVIOR_CHANGE = 14
#     RESOURCE_REFERENCE_REMOVAL = 15
#     RESOURCE_REFERENCE_ADDITION = 16
#     RESOURCE_REFERENCE_CHANGE = 17
#     RESOURCE_REFERENCE_MOVED = 18
#     RESOURCE_REFERENCE_CHANGE_CHILD_TYPE = 19
#     # Messages
#     MESSAGE_ADDITION = 20
#     MESSAGE_REMOVAL = 21
#     # Message annotations
#     RESOURCE_DEFINITION_ADDITION = 22
#     RESOURCE_DEFINITION_REMOVAL = 24
#     RESOURCE_PATTERN_REMOVAL = 25
#     RESOURCE_PATTERN_ADDITION = 26
#     # Services
#     SERVICE_ADDITION = 30
#     SERVICE_REMOVAL = 31
#     # Service annotations
#     SERVICE_HOST_ADDITION = 32
#     SERVICE_HOST_REMOVAL = 33
#     SERVICE_HOST_CHANGE = 34
#     METHOD_SIGNATURE_REMOVAL = 35
#     METHOD_SIGNATURE_ADDITION = 36
#     OAUTH_SCOPE_REMOVAL = 37
#     OAUTH_SCOPE_ADDITION = 38
#     # Methods
#     METHOD_REMOVAL = 40
#     METHOD_ADDTION = 41
#     METHOD_INPUT_TYPE_CHANGE = 42
#     METHOD_RESPONSE_TYPE_CHANGE = 43
#     METHOD_CLIENT_STREAMING_CHANGE = 44
#     METHOD_SERVER_STREAMING_CHANGE = 45
#     METHOD_PAGINATED_RESPONSE_CHANGE = 46
#     # Method annotations
#     LRO_RESPONSE_CHANGE = 50
#     LRO_METADATA_CHANGE = 51
#     LRO_ANNOTATION_ADDITION = 52
#     LRO_ANNOTATION_REMOVAL = 53
#     HTTP_ANNOTATION_CHANGE = 54
#     HTTP_ANNOTATION_REMOVAL = 55
#     HTTP_ANNOTATION_ADDITION = 56
#     # Packaging options
#     PACKAGING_OPTION_REMOVAL = 60
#     PACKAGING_OPTION_ADDITION = 61
# 
# class ChangeType(enum.Enum):
#     MAJOR = 1
#     MINOR = 2


import unittest
from src.findings.finding_container import FindingContainer
from src.findings.finding_category import FindingCategory, ChangeType
    def test1_add_findings(self):
        self.finding_container.add_finding(
            category=FindingCategory.METHOD_REMOVAL,
            proto_file_name="my_proto.proto",
            source_code_line=12,
            change_type=ChangeType.MAJOR,
            subject="subject",
            context="context",
        )
        self.assertEqual(len(self.finding_container.get_all_findings()), 1)

    def test2_get_actionable_findings(self):
        self.finding_container.add_finding(
            category=FindingCategory.FIELD_ADDITION,
            proto_file_name="my_proto.proto",
            source_code_line=15,
            change_type=ChangeType.MINOR,
        )
        self.assertEqual(len(self.finding_container.get_actionable_findings()), 1)

    def test3_to_dict_arr(self):
        dict_arr_output = self.finding_container.to_dict_arr()
        self.assertEqual(dict_arr_output[0]["category"], "METHOD_REMOVAL")
        self.assertEqual(dict_arr_output[1]["category"], "FIELD_ADDITION")

    def test4_to_human_readable_message(self):
        self.finding_container.add_finding(
            category=FindingCategory.RESOURCE_DEFINITION_REMOVAL,
            proto_file_name="my_proto.proto",
            source_code_line=5,

# Path: src/findings/finding_container.py
# class FindingContainer:
#     def __init__(self):
#         self.finding_results = []
# 
#     def add_finding(
#         self,
#         category: FindingCategory,
#         proto_file_name: str,
#         source_code_line: int,
#         change_type: ChangeType,
#         extra_info=None,
#         subject="",
#         oldsubject="",
#         context="",
#         type="",
#         oldtype="",
#     ):
#         self.finding_results.append(
#             Finding(
#                 category,
#                 proto_file_name,
#                 source_code_line,
#                 change_type,
#                 extra_info,
#                 subject=subject,
#                 oldsubject=oldsubject,
#                 context=context,
#                 type=type,
#                 oldtype=oldtype,
#             )
#         )
# 
#     def get_all_findings(self):
#         return sorted_filtered_findings(self.finding_results, lambda f: True)
# 
#     def get_actionable_findings(self):
#         return sorted_filtered_findings(
#             self.finding_results, lambda f: f.change_type == ChangeType.MAJOR
#         )
# 
#     def to_dict_arr(self):
#         return [finding.to_dict() for finding in self.finding_results]
# 
#     def to_human_readable_message(self, line_numbers=True):
#         output_message = ""
#         file_to_findings = defaultdict(list)
#         for finding in self.get_actionable_findings():
#             # Create a map to summarize the findings based on proto file name.
#             file_to_findings[finding.location.proto_file_name].append(finding)
#         # Add each finding to the output message.
#         for file_name, findings in file_to_findings.items():
#             # Customize sort key function to output the findings in the same
#             # file based on the source code line number.
#             # Sort message alphabetically if the line number is same.
#             sorted_findings = sorted_filtered_findings(findings, lambda f: True)
#             for finding in sorted_findings:
#                 message = finding.get_message()
#                 if finding.location.source_code_line == -1 or not line_numbers:
#                     output_message += f"{file_name}: {message}\n"
#                 else:
#                     output_message += (
#                         f"{file_name} L{finding.location.source_code_line}: {message}\n"
#                     )
#         return output_message
# 
# Path: src/findings/finding_category.py
# class FindingCategory(enum.Enum):
#     # Enums
#     ENUM_VALUE_ADDITION = 1
#     ENUM_VALUE_REMOVAL = 2
#     ENUM_VALUE_NAME_CHANGE = 3
#     ENUM_ADDITION = 4
#     ENUM_REMOVAL = 5
#     # Fields
#     FIELD_ADDITION = 6
#     FIELD_REMOVAL = 7
#     FIELD_NAME_CHANGE = 8
#     FIELD_REPEATED_CHANGE = 9
#     FIELD_TYPE_CHANGE = 10
#     FIELD_ONEOF_MOVE_OUT = 11
#     FIELD_ONEOF_MOVE_IN = 12
#     FIELD_PROTO3_OPTIONAL_CHANGE = 13
#     # Field annotations
#     FIELD_BEHAVIOR_CHANGE = 14
#     RESOURCE_REFERENCE_REMOVAL = 15
#     RESOURCE_REFERENCE_ADDITION = 16
#     RESOURCE_REFERENCE_CHANGE = 17
#     RESOURCE_REFERENCE_MOVED = 18
#     RESOURCE_REFERENCE_CHANGE_CHILD_TYPE = 19
#     # Messages
#     MESSAGE_ADDITION = 20
#     MESSAGE_REMOVAL = 21
#     # Message annotations
#     RESOURCE_DEFINITION_ADDITION = 22
#     RESOURCE_DEFINITION_REMOVAL = 24
#     RESOURCE_PATTERN_REMOVAL = 25
#     RESOURCE_PATTERN_ADDITION = 26
#     # Services
#     SERVICE_ADDITION = 30
#     SERVICE_REMOVAL = 31
#     # Service annotations
#     SERVICE_HOST_ADDITION = 32
#     SERVICE_HOST_REMOVAL = 33
#     SERVICE_HOST_CHANGE = 34
#     METHOD_SIGNATURE_REMOVAL = 35
#     METHOD_SIGNATURE_ADDITION = 36
#     OAUTH_SCOPE_REMOVAL = 37
#     OAUTH_SCOPE_ADDITION = 38
#     # Methods
#     METHOD_REMOVAL = 40
#     METHOD_ADDTION = 41
#     METHOD_INPUT_TYPE_CHANGE = 42
#     METHOD_RESPONSE_TYPE_CHANGE = 43
#     METHOD_CLIENT_STREAMING_CHANGE = 44
#     METHOD_SERVER_STREAMING_CHANGE = 45
#     METHOD_PAGINATED_RESPONSE_CHANGE = 46
#     # Method annotations
#     LRO_RESPONSE_CHANGE = 50
#     LRO_METADATA_CHANGE = 51
#     LRO_ANNOTATION_ADDITION = 52
#     LRO_ANNOTATION_REMOVAL = 53
#     HTTP_ANNOTATION_CHANGE = 54
#     HTTP_ANNOTATION_REMOVAL = 55
#     HTTP_ANNOTATION_ADDITION = 56
#     # Packaging options
#     PACKAGING_OPTION_REMOVAL = 60
#     PACKAGING_OPTION_ADDITION = 61
# 
# class ChangeType(enum.Enum):
#     MAJOR = 1
#     MINOR = 2


# Path: test/findings/test_finding_container.py
import unittest
from src.findings.finding_container import FindingContainer
from src.findings.finding_category import FindingCategory, ChangeType

    def test1_add_findings(self):
        self.finding_container.add_finding(
            category=FindingCategory.METHOD_REMOVAL,
            proto_file_name="my_proto.proto",
            source_code_line=12,
            change_type=ChangeType.MAJOR,
            subject="subject",
            context="context",
        )
        self.assertEqual(len(self.finding_container.get_all_findings()), 1)

    def test2_get_actionable_findings(self):
        self.finding_container.add_finding(
            category=FindingCategory.FIELD_ADDITION,
            proto_file_name="my_proto.proto",
            source_code_line=15,
            change_type=ChangeType.MINOR,
        )
        self.assertEqual(len(self.finding_container.get_actionable_findings()), 1)

    def test3_to_dict_arr(self):
        dict_arr_output = self.finding_container.to_dict_arr()
        self.assertEqual(dict_arr_output[0]["category"], "METHOD_REMOVAL")
        self.assertEqual(dict_arr_output[1]["category"], "FIELD_ADDITION")

    def test4_to_human_readable_message(self):
        self.finding_container.add_finding(
            category=FindingCategory.RESOURCE_DEFINITION_REMOVAL,
            proto_file_name="my_proto.proto",
            source_code_line=5,

            change_type=ChangeType.MAJOR,