Add pretty serializer for betamax fixture
Saving json responses all on one line escaped inside of json cassettes is great for computers, but is impossible for humans to read. Add a serializer that is nicely flowed yaml that emits multi-line values as yaml blocks. Additionally, re-flow and indent the nested json, which will stay as json. An example of the output produced can be seen at: https://review.openstack.org/#/c/328338/2/shade/tests/unit/fixtures/test_create_flavor.yaml Hook it in to the keystoneauth1 betamax fixture by default, because why in the world would you want ugly when you can have pretty. Change-Id: I457408fcbbdca240090228d18f0482f958a7d6e4
This commit is contained in:
parent
bc90281f27
commit
c21ce26ff3
@ -20,6 +20,7 @@ import mock
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from keystoneauth1.fixture import hooks
|
from keystoneauth1.fixture import hooks
|
||||||
|
from keystoneauth1.fixture import serializer as yaml_serializer
|
||||||
from keystoneauth1 import session
|
from keystoneauth1 import session
|
||||||
|
|
||||||
|
|
||||||
@ -29,11 +30,12 @@ class BetamaxFixture(fixtures.Fixture):
|
|||||||
serializer=None, record=False,
|
serializer=None, record=False,
|
||||||
pre_record_hook=hooks.pre_record_hook):
|
pre_record_hook=hooks.pre_record_hook):
|
||||||
self.cassette_library_dir = cassette_library_dir
|
self.cassette_library_dir = cassette_library_dir
|
||||||
self.serializer = serializer
|
|
||||||
self.record = record
|
self.record = record
|
||||||
self.cassette_name = cassette_name
|
self.cassette_name = cassette_name
|
||||||
if serializer:
|
if not serializer:
|
||||||
betamax.Betamax.register_serializer(serializer)
|
serializer = yaml_serializer.YamlJsonSerializer
|
||||||
|
self.serializer = serializer
|
||||||
|
betamax.Betamax.register_serializer(serializer)
|
||||||
self.pre_record_hook = pre_record_hook
|
self.pre_record_hook = pre_record_hook
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
92
keystoneauth1/fixture/serializer.py
Normal file
92
keystoneauth1/fixture/serializer.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""A serializer to emit YAML but with request body in nicely formatted JSON."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import betamax.serializers.base
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def _should_use_block(value):
|
||||||
|
for c in u"\u000a\u000d\u001c\u001d\u001e\u0085\u2028\u2029":
|
||||||
|
if c in value:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _represent_scalar(self, tag, value, style=None):
|
||||||
|
if style is None:
|
||||||
|
if _should_use_block(value):
|
||||||
|
style = '|'
|
||||||
|
else:
|
||||||
|
style = self.default_style
|
||||||
|
|
||||||
|
node = yaml.representer.ScalarNode(tag, value, style=style)
|
||||||
|
if self.alias_key is not None:
|
||||||
|
self.represented_objects[self.alias_key] = node
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
def _unicode_representer(dumper, uni):
|
||||||
|
node = yaml.ScalarNode(tag=u'tag:yaml.org,2002:str', value=uni)
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
def _indent_json(val):
|
||||||
|
if not val:
|
||||||
|
return ''
|
||||||
|
return json.dumps(
|
||||||
|
json.loads(val), indent=2,
|
||||||
|
separators=(',', ': '), sort_keys=False,
|
||||||
|
default=unicode)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_json_body(interaction):
|
||||||
|
content_type = interaction['headers'].get('Content-Type', [])
|
||||||
|
return 'application/json' in content_type
|
||||||
|
|
||||||
|
|
||||||
|
class YamlJsonSerializer(betamax.serializers.base.BaseSerializer):
|
||||||
|
|
||||||
|
name = "yamljson"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_cassette_name(cassette_library_dir, cassette_name):
|
||||||
|
return os.path.join(
|
||||||
|
cassette_library_dir, "{name}.yaml".format(name=cassette_name))
|
||||||
|
|
||||||
|
def serialize(self, cassette_data):
|
||||||
|
# Reserialize internal json with indentation
|
||||||
|
for interaction in cassette_data['http_interactions']:
|
||||||
|
for key in ('request', 'response'):
|
||||||
|
if _is_json_body(interaction[key]):
|
||||||
|
interaction[key]['body']['string'] = _indent_json(
|
||||||
|
interaction[key]['body']['string'])
|
||||||
|
|
||||||
|
class MyDumper(yaml.Dumper):
|
||||||
|
"""Specialized Dumper which does nice blocks and unicode."""
|
||||||
|
|
||||||
|
yaml.representer.BaseRepresenter.represent_scalar = _represent_scalar
|
||||||
|
MyDumper.add_representer(unicode, _unicode_representer)
|
||||||
|
|
||||||
|
return yaml.dump(
|
||||||
|
cassette_data, Dumper=MyDumper, default_flow_style=False)
|
||||||
|
|
||||||
|
def deserialize(self, cassette_data):
|
||||||
|
try:
|
||||||
|
# There should be only one document
|
||||||
|
return list(yaml.load_all(cassette_data))[0]
|
||||||
|
except yaml.error.YAMLError:
|
||||||
|
return {}
|
@ -1,2 +0,0 @@
|
|||||||
{"http_interactions": [{"request": {"body": {"string": "{\"auth\": {\"tenantName\": \"test_tenant_name\", \"passwordCredentials\": {\"username\": \"test_user_name\", \"password\": \"test_password\"}}}", "encoding": "utf-8"}, "headers": {"Content-Length": ["128"], "Accept-Encoding": ["gzip, deflate"], "Accept": ["application/json"], "User-Agent": ["keystoneauth1"], "Connection": ["keep-alive"], "Content-Type": ["application/json"]}, "method": "POST", "uri": "http://keystonauth.betamax_test/v2.0/tokens"}, "response": {"body": {"string": "{\"access\": {\"token\": {\"issued_at\": \"2015-11-27T15:17:19.755470\", \"expires\": \"2015-11-27T16:17:19Z\", \"id\": \"c000c5ee4ba04594a00886028584b50d\", \"tenant\": {\"description\": null, \"enabled\": true, \"id\": \"6932cad596634a61ac9c759fb91beef1\", \"name\": \"test_tenant_name\"}, \"audit_ids\": [\"jY3gYg_YTbmzY2a4ioGuCw\"]}, \"user\": {\"username\": \"test_user_name\", \"roles_links\": [], \"id\": \"96995e6cc15b40fa8e7cd762f6a5d4c0\", \"roles\": [{\"name\": \"_member_\"}], \"name\": \"67eff5f6-9477-4961-88b4-437e6596a795\"}, \"metadata\": {\"is_admin\": 0, \"roles\": [\"9fe2ff9ee4384b1894a90878d3e92bab\"]}}}", "encoding": null}, "headers": {"X-Openstack-Request-Id": ["req-f9e188b4-06fd-4a4c-a952-2315b368218c"], "Content-Length": ["2684"], "Connection": ["keep-alive"], "Date": ["Fri, 27 Nov 2015 15:17:19 GMT"], "Content-Type": ["application/json"], "Vary": ["X-Auth-Token"], "X-Distribution": ["Ubuntu"], "Server": ["Fake"]}, "status": {"message": "OK", "code": 200}, "url": "http://keystonauth.betamax_test/v2.0/tokens"}, "recorded_at": "2015-11-27T15:17:19"}], "recorded_with": "betamax/0.5.1"}
|
|
||||||
|
|
92
keystoneauth1/tests/unit/data/ksa_betamax_test_cassette.yaml
Normal file
92
keystoneauth1/tests/unit/data/ksa_betamax_test_cassette.yaml
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
http_interactions:
|
||||||
|
- request:
|
||||||
|
body:
|
||||||
|
string: |-
|
||||||
|
{
|
||||||
|
"auth": {
|
||||||
|
"tenantName": "test_tenant_name",
|
||||||
|
"passwordCredentials": {
|
||||||
|
"username": "test_user_name",
|
||||||
|
"password": "test_password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encoding: utf-8
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '128'
|
||||||
|
Accept-Encoding:
|
||||||
|
- gzip, deflate
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
User-Agent:
|
||||||
|
- keystoneauth1
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
method: POST
|
||||||
|
uri: http://keystonauth.betamax_test/v2.0/tokens
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: |-
|
||||||
|
{
|
||||||
|
"access": {
|
||||||
|
"token": {
|
||||||
|
"issued_at": "2015-11-27T15:17:19.755470",
|
||||||
|
"expires": "2015-11-27T16:17:19Z",
|
||||||
|
"id": "c000c5ee4ba04594a00886028584b50d",
|
||||||
|
"tenant": {
|
||||||
|
"enabled": true,
|
||||||
|
"description": null,
|
||||||
|
"name": "test_tenant_name",
|
||||||
|
"id": "6932cad596634a61ac9c759fb91beef1"
|
||||||
|
},
|
||||||
|
"audit_ids": [
|
||||||
|
"jY3gYg_YTbmzY2a4ioGuCw"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"username": "test_user_name",
|
||||||
|
"roles_links": [],
|
||||||
|
"id": "96995e6cc15b40fa8e7cd762f6a5d4c0",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "_member_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "67eff5f6-9477-4961-88b4-437e6596a795"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"is_admin": 0,
|
||||||
|
"roles": [
|
||||||
|
"9fe2ff9ee4384b1894a90878d3e92bab"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encoding: null
|
||||||
|
headers:
|
||||||
|
X-Openstack-Request-Id:
|
||||||
|
- req-f9e188b4-06fd-4a4c-a952-2315b368218c
|
||||||
|
Content-Length:
|
||||||
|
- '2684'
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Date:
|
||||||
|
- Fri, 27 Nov 2015 15:17:19 GMT
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Vary:
|
||||||
|
- X-Auth-Token
|
||||||
|
X-Distribution:
|
||||||
|
- Ubuntu
|
||||||
|
Server:
|
||||||
|
- Fake
|
||||||
|
status:
|
||||||
|
message: OK
|
||||||
|
code: 200
|
||||||
|
url: http://keystonauth.betamax_test/v2.0/tokens
|
||||||
|
recorded_at: '2015-11-27T15:17:19'
|
||||||
|
recorded_with: betamax/0.5.1
|
||||||
|
|
@ -23,3 +23,4 @@ sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
|
|||||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||||
testresources>=0.2.4 # Apache-2.0/BSD
|
testresources>=0.2.4 # Apache-2.0/BSD
|
||||||
testtools>=1.4.0 # MIT
|
testtools>=1.4.0 # MIT
|
||||||
|
PyYAML>=3.1.0 # MIT
|
||||||
|
Loading…
Reference in New Issue
Block a user