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
|
||||
|
||||
from keystoneauth1.fixture import hooks
|
||||
from keystoneauth1.fixture import serializer as yaml_serializer
|
||||
from keystoneauth1 import session
|
||||
|
||||
|
||||
@ -29,11 +30,12 @@ class BetamaxFixture(fixtures.Fixture):
|
||||
serializer=None, record=False,
|
||||
pre_record_hook=hooks.pre_record_hook):
|
||||
self.cassette_library_dir = cassette_library_dir
|
||||
self.serializer = serializer
|
||||
self.record = record
|
||||
self.cassette_name = cassette_name
|
||||
if serializer:
|
||||
betamax.Betamax.register_serializer(serializer)
|
||||
if not serializer:
|
||||
serializer = yaml_serializer.YamlJsonSerializer
|
||||
self.serializer = serializer
|
||||
betamax.Betamax.register_serializer(serializer)
|
||||
self.pre_record_hook = pre_record_hook
|
||||
|
||||
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
|
||||
testresources>=0.2.4 # Apache-2.0/BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
PyYAML>=3.1.0 # MIT
|
||||
|
Loading…
Reference in New Issue
Block a user