Example #1
0
 def test_http_annotation_change(self):
     method_original = make_method(
         name="Method",
         http_uri="http_uri",
         http_body="*",
     )
     method_update = make_method(
         name="Method",
         http_uri="http_uri_update",
         http_body="http_body",
     )
     ServiceComparator(
         make_service(methods=(method_original, )),
         make_service(methods=(method_update, )),
         self.finding_container,
         context="ctx",
     ).compare()
     uri_change_finding = next(
         f for f in self.finding_container.get_all_findings()
         if f.category.name == "HTTP_ANNOTATION_CHANGE"
         and f.type == "http_uri")
     body_change_finding = next(
         f for f in self.finding_container.get_all_findings()
         if f.category.name == "HTTP_ANNOTATION_CHANGE"
         and f.type == "http_body")
     self.assertEqual(
         uri_change_finding.location.proto_file_name,
         "foo",
     )
     self.assertEqual(
         body_change_finding.location.proto_file_name,
         "foo",
     )
Example #2
0
 def test_lro_annotation_metadata_change(self):
     lro_output_msg = make_message(
         name=".google.longrunning.Operation",
         full_name=".google.longrunning.Operation",
     )
     method_original = make_method(
         name="Method",
         output_message=lro_output_msg,
         lro_response_type="response_type",
         lro_metadata_type="FooMetadata",
     )
     method_update = make_method(
         name="Method",
         output_message=lro_output_msg,
         lro_response_type="response_type",
         lro_metadata_type="FooMetadataUpdate",
     )
     ServiceComparator(
         make_service(methods=(method_original, )),
         make_service(methods=(method_update, )),
         self.finding_container,
         context="ctx",
     ).compare()
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "LRO_METADATA_CHANGE")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #3
0
 def test_service_change(self):
     input_message = make_message(name="request",
                                  full_name=".example.v1.request")
     output_message = make_message(name="response",
                                   full_name=".example.v1.response")
     service_original = make_service(methods=[
         make_method(
             name="DoThing",
             input_message=input_message,
             output_message=output_message,
         )
     ])
     service_update = make_service()
     FileSetComparator(
         make_file_set(files=[
             make_file_pb2(
                 services=[service_original],
                 messages=[input_message, output_message],
             )
         ]),
         make_file_set(files=[make_file_pb2(services=[service_update])]),
         self.finding_container,
     ).compare()
     finding = self.finding_container.getAllFindings()[0]
     self.assertEqual(finding.message,
                      "An existing rpc method `DoThing` is removed.")
     self.assertEqual(finding.category.name, "METHOD_REMOVAL")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "my_proto.proto")
Example #4
0
 def test_lro_annotation_invalid(self):
     lro_output_msg = make_message(
         name=".google.longrunning.Operation",
         full_name=".google.longrunning.Operation",
     )
     method_lro = make_method(
         name="Method",
         output_message=lro_output_msg,
         lro_response_type="response_type",
         lro_metadata_type="FooMetadata",
     )
     method_not_lro = make_method(
         name="Method",
         output_message=lro_output_msg,
     )
     # `method_not_lro` returns `google.longrunning.Operation`
     # but is missing a response type or metadata type, the definition
     # is invalid. We still compare the two methods and take it as
     # lro annotation removal.
     ServiceComparator(
         make_service(methods=(method_lro, )),
         make_service(methods=(method_not_lro, )),
         self.finding_container,
         context="ctx",
     ).compare()
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "LRO_ANNOTATION_REMOVAL")
     self.assertTrue(finding)
Example #5
0
 def test_detector_basic(self):
     # Mock original and updated FileDescriptorSet.
     L = desc.SourceCodeInfo.Location
     # fmt: off
     locations = [
         L(path=(6, 0, 2, 0), span=(1, 2, 3, 4)),
         L(
             path=(
                 4,
                 0,
             ),
             span=(5, 6, 7, 8),
         ),
         L(path=(4, 1), span=(11, 12, 13, 14)),
     ]
     # fmt: on
     input_msg = make_message(name="input", full_name=".example.v1.input")
     output_msg = make_message(name="output",
                               full_name=".example.v1.output")
     service_original = make_service(methods=(make_method(
         name="DoThing", input_message=input_msg,
         output_message=output_msg), ))
     service_update = make_service()
     file_set_original = desc.FileDescriptorSet(file=[
         make_file_pb2(
             services=[service_original],
             locations=locations,
             messages=[input_msg, output_msg],
         )
     ])
     file_set_update = desc.FileDescriptorSet(
         file=[make_file_pb2(services=[service_update])])
     with mock.patch("os.path.isdir") as mocked_isdir:
         with mock.patch("os.path.isfile") as mocked_isfile:
             mocked_isdir.return_value = True
             mocked_isfile.return_value = True
             opts = Options(
                 original_api_definition_dirs="c,d",
                 update_api_definition_dirs="a,b",
                 original_proto_files="pf1, pf2",
                 update_proto_files="pf3, pf4",
                 original_descriptor_set_file_path=None,
                 update_descriptor_set_file_path=None,
                 human_readable_message=True,
             )
     with mock.patch("sys.stdout", new=StringIO()) as fake_output:
         result = Detector(file_set_original, file_set_update,
                           opts).detect_breaking_changes()
         self.assertEqual(
             fake_output.getvalue(),
             "my_proto.proto L2: An existing method `DoThing` is removed from service `Placeholder`.\n"
             +
             "my_proto.proto L6: An existing message `input` is removed.\n"
             +
             "my_proto.proto L12: An existing message `output` is removed.\n",
         )
         self.assertEqual(len(result), 3)
Example #6
0
 def test_service_host_change(self):
     service_original = make_service(host="default.host")
     service_update = make_service(host="default.host.update")
     ServiceComparator(service_original,
                       service_update,
                       self.finding_container,
                       context="ctx").compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "SERVICE_HOST_CHANGE")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #7
0
 def test_method_signature_removal(self):
     ServiceComparator(
         make_service(methods=(make_method(name="NotInteresting",
                                           signatures=["sig1", "sig2"]), )),
         make_service(methods=(
             make_method(name="NotInteresting", signatures=["sig1"]), )),
         self.finding_container,
         context="ctx",
     ).compare()
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "METHOD_SIGNATURE_REMOVAL")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #8
0
 def test_method_removal(self):
     method_foo = make_method(name="foo")
     method_bar = make_method(name="bar")
     service_original = make_service(methods=(method_foo, method_bar))
     service_update = make_service(methods=(method_foo, ))
     ServiceComparator(service_original,
                       service_update,
                       self.finding_container,
                       context="ctx").compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "METHOD_REMOVAL")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #9
0
 def test_service_host_removal(self):
     service_without_host = make_service()
     service_with_host = make_service(host="api.google.com")
     ServiceComparator(
         service_with_host,
         service_without_host,
         self.finding_container,
         context="ctx",
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "SERVICE_HOST_REMOVAL")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #10
0
 def test_http_annotation_removal(self):
     method_without_http_annotation = make_method(name="Method", )
     method_with_http_annotation = make_method(
         name="Method",
         http_uri="http_uri_update",
         http_body="http_body",
     )
     ServiceComparator(
         make_service(methods=(method_with_http_annotation, )),
         make_service(methods=(method_without_http_annotation, )),
         self.finding_container,
         context="ctx",
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "HTTP_ANNOTATION_REMOVAL")
     self.assertEqual(finding.change_type.name, "MAJOR")
 def test_service_methods(self):
     input_message = make_message("InputRequest")
     output_message = make_message("OutputResponse")
     service = make_service(
         name="ThingDoer",
         methods=(
             make_method(
                 name="DoThing",
                 input_message=input_message,
                 output_message=output_message,
             ),
             make_method(
                 name="Jump",
                 input_message=input_message,
                 output_message=output_message,
             ),
             make_method(
                 name="Yawn",
                 input_message=input_message,
                 output_message=output_message,
             ),
         ),
     )
     expected_names = ["DoThing", "Jump", "Yawn"]
     self.assertEqual(list(service.methods.keys()), expected_names)
Example #12
0
 def test_method_input_type_change(self):
     message_foo_request = make_message(name="FooRequest",
                                        full_name="FooRequest")
     message_bar_request = make_message(name="BarRequest",
                                        full_name="BarRequest")
     service_original = make_service(methods=(
         make_method(name="Foo", input_message=message_foo_request), ))
     service_update = make_service(methods=(
         make_method(name="Foo", input_message=message_bar_request), ))
     ServiceComparator(service_original,
                       service_update,
                       self.finding_container,
                       context="ctx").compare()
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "METHOD_INPUT_TYPE_CHANGE")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #13
0
 def test_service_oauth_scopes_change(self):
     service_original = make_service(scopes=("https://foo/user/",
                                             "https://foo/admin/"))
     service_update = make_service(
         scopes=("https://www.googleapis.com/auth/cloud-platform"))
     ServiceComparator(service_original,
                       service_update,
                       self.finding_container,
                       context="ctx").compare()
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "OAUTH_SCOPE_REMOVAL"
                    and f.subject == "https://foo/user/")
     self.assertEqual(finding.location.proto_file_name, "foo")
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "OAUTH_SCOPE_REMOVAL"
                    and f.subject == "https://foo/admin/")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #14
0
 def test_method_output_type_change(self):
     service_original = make_service(methods=(make_method(
         name="Foo",
         output_message=make_message(name="FooResponse",
                                     full_name=".example.FooResponse"),
     ), ))
     service_update = make_service(methods=(make_method(
         name="Foo",
         output_message=make_message(name="BarResponse",
                                     full_name=".example.BarResponse"),
     ), ))
     ServiceComparator(service_original,
                       service_update,
                       self.finding_container,
                       context="ctx").compare()
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "METHOD_RESPONSE_TYPE_CHANGE")
     self.assertEqual(finding.change_type.name, "MAJOR")
     self.assertEqual(finding.location.proto_file_name, "foo")
Example #15
0
 def test_service_removal(self):
     file_set = make_file_set(
         files=[make_file_pb2(services=[make_service()], )])
     FileSetComparator(
         file_set,
         make_file_set(),
         self.finding_container,
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.change_type.name, "MAJOR")
 def test_service_properties(self):
     service = make_service(name="ThingDoer")
     self.assertEqual(service.name, "ThingDoer")
     self.assertEqual(service.proto_file_name, "foo")
     self.assertEqual(service.path, ())
     self.assertFalse(service.api_version)
     self.assertEqual(
         service.source_code_line,
         -1,
     )
Example #17
0
 def test_service_addition(self):
     file_set = make_file_set(
         files=[make_file_pb2(services=[make_service()], )])
     FileSetComparator(
         make_file_set(),
         file_set,
         self.finding_container,
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "SERVICE_ADDITION")
     self.assertEqual(finding.change_type.name, "MINOR")
Example #18
0
    def test_method_streaming_state_change(self):
        service_original = make_service(
            methods=(make_method(name="Bar", client_streaming=True), ))
        service_update = make_service(
            methods=(make_method(name="Bar", server_streaming=True), ))
        ServiceComparator(service_original,
                          service_update,
                          self.finding_container,
                          context="ctx").compare()

        client_streaming_finding = next(
            f for f in self.finding_container.get_all_findings()
            if f.category.name == "METHOD_CLIENT_STREAMING_CHANGE")
        self.assertEqual(client_streaming_finding.location.proto_file_name,
                         "foo")
        server_streaming_finding = next(
            f for f in self.finding_container.get_all_findings()
            if f.category.name == "METHOD_SERVER_STREAMING_CHANGE")
        self.assertEqual(server_streaming_finding.change_type.name, "MAJOR")
        self.assertEqual(server_streaming_finding.location.proto_file_name,
                         "foo")
Example #19
0
 def test_service_addition(self):
     file_set = make_file_set(
         files=[make_file_pb2(services=[make_service()], )])
     FileSetComparator(
         make_file_set(),
         file_set,
         self.finding_container,
     ).compare()
     finding = self.finding_container.getAllFindings()[0]
     self.assertEqual(finding.message,
                      "A new service `Placeholder` is added.")
     self.assertEqual(finding.change_type.name, "MINOR")
Example #20
0
 def test_service_removal(self):
     file_set = make_file_set(
         files=[make_file_pb2(services=[make_service()], )])
     FileSetComparator(
         file_set,
         make_file_set(),
         self.finding_container,
     ).compare()
     finding = self.finding_container.getAllFindings()[0]
     self.assertEqual(finding.message,
                      "An existing service `Placeholder` is removed.")
     self.assertEqual(finding.change_type.name, "MAJOR")
 def test_source_code_line(self):
     L = descriptor_pb2.SourceCodeInfo.Location
     locations = [
         L(path=(4, 0, 2, 1), span=(1, 2, 3, 4)),
     ]
     service = make_service(
         proto_file_name="test.proto",
         locations=locations,
         path=(4, 0, 2, 1),
     )
     self.assertEqual(service.source_code_line, 2)
     self.assertEqual(service.proto_file_name, "test.proto")
Example #22
0
 def test_lro_annotation_removal(self):
     lro_output_msg = make_message(
         name=".google.longrunning.Operation",
         full_name=".google.longrunning.Operation",
     )
     method_lro = make_method(
         name="Method",
         output_message=lro_output_msg,
         lro_response_type="response_type",
         lro_metadata_type="FooMetadata",
     )
     method_not_lro = make_method(name="Method", )
     ServiceComparator(
         make_service(methods=(method_lro, )),
         make_service(methods=(method_not_lro, )),
         self.finding_container,
         context="ctx",
     ).compare()
     finding = next(f for f in self.finding_container.get_all_findings()
                    if f.category.name == "LRO_ANNOTATION_REMOVAL")
     self.assertTrue(finding)
Example #23
0
 def test_http_annotation_minor_version_update(self):
     method_original = make_method(
         name="Method",
         http_uri="/v1/{name=projects/*}",
         http_body="*",
     )
     method_update = make_method(
         name="Method",
         http_uri="/v1alpha/{name=projects/*}",
         http_body="*",
     )
     ServiceComparator(
         make_service(methods=(method_original, ), api_version="v1"),
         make_service(methods=(method_update, ), api_version="v1alpha"),
         self.finding_container,
         context="ctx",
     ).compare()
     findings_map = {
         f.message: f
         for f in self.finding_container.get_all_findings()
     }
     # No breaking changes, since only minor version update in the http URI.
     self.assertFalse(findings_map)
Example #24
0
 def test_method_paginated_state_change(self):
     paginated_response_message = make_message(
         name="PaginatedResponseMessage",
         fields=[
             make_field(
                 name="repeated_field",
                 proto_type="TYPE_STRING",
                 repeated=True,
                 number=1,
             ),
             make_field(
                 name="next_page_token",
                 proto_type="TYPE_STRING",
                 number=2,
             ),
         ],
         full_name=".example.v1.PaginatedResponseMessage",
     )
     paginated_request_message = make_message(
         name="PaginatedRequestMessage",
         fields=[
             make_field(
                 name="page_size",
                 proto_type="TYPE_INT32",
                 number=1,
             ),
             make_field(
                 name="page_token",
                 proto_type="TYPE_STRING",
                 number=2,
             ),
         ],
         full_name=".example.v1.PaginatedRequestMessage",
     )
     messages_map_original = {
         ".example.v1.PaginatedResponseMessage": paginated_response_message,
         ".example.v1.PaginatedRequestMessage": paginated_request_message,
     }
     paged_method = make_method(
         name="NotInteresting",
         input_message=paginated_request_message,
         output_message=paginated_response_message,
     )
     messages_map_update = {
         ".example.v1alpha.MethodInput":
         make_message(name="MethodInput",
                      full_name=".example.v1alpha.MethodInput"),
         ".example.v1alpha.MethodOutput":
         make_message(name="MethodOutput",
                      full_name=".example.v1alpha.MethodOutput"),
     }
     non_paged_method = make_method(name="NotInteresting")
     service_original = make_service(methods=(paged_method, ),
                                     messages_map=messages_map_original)
     service_update = make_service(methods=(non_paged_method, ),
                                   messages_map=messages_map_update)
     ServiceComparator(service_original,
                       service_update,
                       self.finding_container,
                       context="ctx").compare()
     finding = next(
         f for f in self.finding_container.get_all_findings()
         if f.category.name == "METHOD_PAGINATED_RESPONSE_CHANGE")
     self.assertEqual(finding.location.proto_file_name, "foo")
 def test_service_no_scopes(self):
     service = make_service()
     self.assertEqual(len(service.oauth_scopes), 0)
 def test_service_scopes(self):
     service = make_service(scopes=("https://foo/user/",
                                    "https://foo/admin/"))
     oauth_scopes = [scope.value for scope in service.oauth_scopes]
     self.assertIn("https://foo/user/", oauth_scopes)
     self.assertIn("https://foo/admin/", oauth_scopes)
 def test_service_no_host(self):
     service = make_service()
     self.assertFalse(service.host)
 def test_service_host(self):
     service = make_service(host="thingdoer.googleapis.com")
     self.assertEqual(service.host.value, "thingdoer.googleapis.com")
 def test_service_api_version(self):
     service = make_service(api_version="v1alpha")
     self.assertEqual(service.api_version, "v1alpha")
 def test_file_set_properties(self):
     input_message = make_message(name="request",
                                  full_name=".example.v1.request")
     output_message = make_message(name="response",
                                   full_name=".example.v1.response")
     messages = [
         make_message(
             "InnerMessage",
             fields=(make_field(name="hidden_message",
                                number=1,
                                type_name=".example.v1.request"), ),
             full_name=".example.v1.InnerMessage",
         ),
         input_message,
         output_message,
     ]
     services = [
         make_service(
             name="ThingDoer",
             methods=(make_method(
                 name="DoThing",
                 input_message=input_message,
                 output_message=output_message,
             ), ),
         )
     ]
     enums = [
         make_enum(
             name="Irrelevant",
             values=(
                 ("RED", 1),
                 ("GREEN", 2),
                 ("BLUE", 3),
             ),
         )
     ]
     file_foo = make_file_pb2(
         name="foo.proto",
         package="example.v1",
         services=services,
         enums=enums,
         dependency=["bar.proto"],
     )
     file_bar = make_file_pb2(name="bar.proto",
                              package="example.v1",
                              messages=messages)
     file_set = make_file_set(files=[file_bar, file_foo])
     # Default to be empty.
     self.assertFalse(file_set.packaging_options_map)
     self.assertEqual(
         list(file_set.messages_map.keys()),
         [
             ".example.v1.InnerMessage", ".example.v1.request",
             ".example.v1.response"
         ],
     )
     self.assertEqual(list(file_set.enums_map.keys()),
                      [".example.v1.Irrelevant"])
     self.assertEqual(list(file_set.services_map.keys()), ["ThingDoer"])
     self.assertEqual(
         file_set.messages_map[".example.v1.InnerMessage"].fields[1].name,
         "hidden_message",
     )
     self.assertEqual(
         file_set.enums_map[".example.v1.Irrelevant"].values[2].name,
         "GREEN")
     self.assertEqual(
         list(file_set.services_map["ThingDoer"].methods.keys()),
         ["DoThing"],
     )
     self.assertEqual(list(file_set.global_enums_map.keys()),
                      [".example.v1.Irrelevant"])
     self.assertEqual(
         list(file_set.global_messages_map.keys()),
         [
             ".example.v1.response", ".example.v1.request",
             ".example.v1.InnerMessage"
         ],
     )