Repository: googleapis/google-cloud-python

Issue:
Error reporting system tests needed
Follow up to #3263.


Discussion:

Resolving patch:
diff --git a/error_reporting/nox.py b/error_reporting/nox.py
--- a/error_reporting/nox.py
+++ b/error_reporting/nox.py
@@ -64,6 +64,28 @@ def lint_setup_py(session):
         'python', 'setup.py', 'check', '--restructuredtext', '--strict')
 
 
+@nox.session
+@nox.parametrize('python_version', ['2.7', '3.6'])
+def system_tests(session, python_version):
+    """Run the system test suite."""
+
+    # Sanity check: Only run system tests if the environment variable is set.
+    if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''):
+        return
+
+    # Run the system tests against latest Python 2 and Python 3 only.
+    session.interpreter = 'python{}'.format(python_version)
+
+    # Install all test dependencies, then install this package into the
+    # virtualenv's dist-packages.
+    session.install('mock', 'pytest', *LOCAL_DEPS)
+    session.install('../test_utils/')
+    session.install('.')
+
+    # Run py.test against the system tests.
+    session.run('py.test', '-vvv', 'tests/system.py')
+
+
 @nox.session
 def cover(session):
     """Run the final coverage report.


Test patch:
diff --git a/error_reporting/tests/system.py b/error_reporting/tests/system.py
new file mode 100644
--- /dev/null
+++ b/error_reporting/tests/system.py
@@ -0,0 +1,123 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import functools
+import operator
+import unittest
+
+from google.cloud import error_reporting
+from google.cloud.gapic.errorreporting.v1beta1 import (
+    error_stats_service_client)
+from google.cloud.proto.devtools.clouderrorreporting.v1beta1 import (
+    error_stats_service_pb2)
+from google.protobuf.duration_pb2 import Duration
+
+from test_utils.retry import RetryResult
+from test_utils.system import unique_resource_id
+
+
+ERROR_MSG = 'Stackdriver Error Reporting System Test'
+
+
+def setUpModule():
+    Config.CLIENT = error_reporting.Client()
+
+
+class Config(object):
+    """Run-time configuration to be modified at set-up.
+
+    This is a mutable stand-in to allow test set-up to modify
+    global state.
+    """
+    CLIENT = None
+
+
+def _list_groups(client):
+    """List Error Groups from the last 60 seconds.
+
+    This class provides a wrapper around making calls to the GAX
+    API. It's used by the system tests to find the appropriate error group
+    to verify the error was successfully reported.
+
+    :type client: :class:`~google.cloud.error_reporting.client.Client`
+    :param client: The client containing a project and credentials.
+
+    :rtype: :class:`~google.gax.ResourceIterator`
+    :returns: Iterable of :class:`~.error_stats_service_pb2.ErrorGroupStats`.
+    """
+    gax_api = error_stats_service_client.ErrorStatsServiceClient(
+        credentials=client._credentials)
+    project_name = gax_api.project_path(client.project)
+
+    time_range = error_stats_service_pb2.QueryTimeRange()
+    time_range.period = error_stats_service_pb2.QueryTimeRange.PERIOD_1_HOUR
+
+    duration = Duration(seconds=60 * 60)
+
+    return gax_api.list_group_stats(
+        project_name, time_range, timed_count_duration=duration)
+
+
+def _simulate_exception(class_name, client):
+    """Simulates an exception to verify it was reported.
+
+    :type class_name: str
+    :param class_name: The name of a custom error class to
+                       create (and raise).
+
+    :type client: :class:`~google.cloud.error_reporting.client.Client`
+    :param client: The client that will report the exception.
+    """
+    custom_exc = type(class_name, (RuntimeError,), {})
+    try:
+        raise custom_exc(ERROR_MSG)
+    except RuntimeError:
+        client.report_exception()
+
+
+def _get_error_count(class_name, client):
+    """Counts the number of errors in the group of the test exception.
+
+    :type class_name: str
+    :param class_name: The name of a custom error class used.
+
+    :type client: :class:`~google.cloud.error_reporting.client.Client`
+    :param client: The client containing a project and credentials.
+
+    :rtype: int
+    :returns: Group count for errors that match ``class_name``. If no
+              match is found, returns :data:`None`.
+    """
+    groups = _list_groups(client)
+    for group in groups:
+        if class_name in group.representative.message:
+            return group.count
+
+
+class TestErrorReporting(unittest.TestCase):
+
+    def test_report_exception(self):
+        # Get a class name unique to this test case.
+        class_name = 'RuntimeError' + unique_resource_id('_')
+
+        # Simulate an error: group won't exist until we report
+        # first exception.
+        _simulate_exception(class_name, Config.CLIENT)
+
+        is_one = functools.partial(operator.eq, 1)
+        is_one.__name__ = 'is_one'  # partial() has no name.
+        wrapped_get_count = RetryResult(is_one)(_get_error_count)
+
+        error_count = wrapped_get_count(class_name, Config.CLIENT)
+        self.assertEqual(error_count, 1)