1#!/usr/bin/env python
2#
3# Copyright 2014 Google Inc. All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Tests for errors handling
18"""
19from __future__ import absolute_import
20
21__author__ = "afshar@google.com (Ali Afshar)"
22
23
24import unittest
25import httplib2
26
27
28from googleapiclient.errors import HttpError
29
30
31JSON_ERROR_CONTENT = b"""
32{
33 "error": {
34  "errors": [
35   {
36    "domain": "global",
37    "reason": "required",
38    "message": "country is required",
39    "locationType": "parameter",
40    "location": "country"
41   }
42  ],
43  "code": 400,
44  "message": "country is required",
45  "details": "error details"
46 }
47}
48"""
49
50JSON_ERROR_CONTENT_NO_DETAIL = b"""
51{
52 "error": {
53  "errors": [
54   {
55    "domain": "global",
56    "reason": "required",
57    "message": "country is required",
58    "locationType": "parameter",
59    "location": "country"
60   }
61  ],
62  "code": 400,
63  "message": "country is required"
64 }
65}
66"""
67
68
69def fake_response(data, headers, reason="Ok"):
70    response = httplib2.Response(headers)
71    response.reason = reason
72    return response, data
73
74
75class Error(unittest.TestCase):
76    """Test handling of error bodies."""
77
78    def test_json_body(self):
79        """Test a nicely formed, expected error response."""
80        resp, content = fake_response(
81            JSON_ERROR_CONTENT,
82            {"status": "400", "content-type": "application/json"},
83            reason="Failed",
84        )
85        error = HttpError(resp, content, uri="http://example.org")
86        self.assertEqual(error.error_details, "error details")
87        self.assertEqual(error.status_code, 400)
88        self.assertEqual(
89            str(error),
90            '<HttpError 400 when requesting http://example.org returned "country is required". Details: "error details">',
91        )
92
93    def test_bad_json_body(self):
94        """Test handling of bodies with invalid json."""
95        resp, content = fake_response(
96            b"{", {"status": "400", "content-type": "application/json"}, reason="Failed"
97        )
98        error = HttpError(resp, content)
99        self.assertEqual(str(error), '<HttpError 400 when requesting None returned "Failed". Details: "{">')
100
101    def test_with_uri(self):
102        """Test handling of passing in the request uri."""
103        resp, content = fake_response(
104            b"{",
105            {"status": "400", "content-type": "application/json"},
106            reason="Failure",
107        )
108        error = HttpError(resp, content, uri="http://example.org")
109        self.assertEqual(
110            str(error),
111            '<HttpError 400 when requesting http://example.org returned "Failure". Details: "{">',
112        )
113
114    def test_missing_message_json_body(self):
115        """Test handling of bodies with missing expected 'message' element."""
116        resp, content = fake_response(
117            b"{}",
118            {"status": "400", "content-type": "application/json"},
119            reason="Failed",
120        )
121        error = HttpError(resp, content)
122        self.assertEqual(str(error), '<HttpError 400 "Failed">')
123
124    def test_non_json(self):
125        """Test handling of non-JSON bodies"""
126        resp, content = fake_response(b"Invalid request", {"status": "400"})
127        error = HttpError(resp, content)
128        self.assertEqual(str(error), '<HttpError 400 when requesting None returned "Ok". Details: "Invalid request">')
129
130    def test_missing_reason(self):
131        """Test an empty dict with a missing resp.reason."""
132        resp, content = fake_response(b"}NOT OK", {"status": "400"}, reason=None)
133        error = HttpError(resp, content)
134        self.assertEqual(str(error), '<HttpError 400 when requesting None returned "". Details: "}NOT OK">')
135
136    def test_error_detail_for_missing_message_in_error(self):
137        """Test handling of data with missing 'details' or 'detail' element."""
138        resp, content = fake_response(
139            JSON_ERROR_CONTENT_NO_DETAIL,
140            {"status": "400", "content-type": "application/json"},
141            reason="Failed",
142        )
143        error = HttpError(resp, content)
144        expected_error_details = "[{'domain': 'global', 'reason': 'required', 'message': 'country is required', 'locationType': 'parameter', 'location': 'country'}]"
145        self.assertEqual(str(error), '<HttpError 400 when requesting None returned "country is required". Details: "%s">' % expected_error_details)
146        self.assertEqual(str(error.error_details), expected_error_details)
147