got one more test to work.
This commit is contained in:
parent
7b031c26dc
commit
dc040f190e
|
@ -87,7 +87,7 @@ if app.config['SENTRY_ENVIRONMENT']:
|
|||
def render_errors(exception):
|
||||
from crc.api.common import ApiError, ApiErrorSchema
|
||||
error = ApiError(code=exception.title, message=exception.detail, status_code=exception.status)
|
||||
return Response(ApiErrorSchema().dump(error), status=401, mimetype="application/json")
|
||||
return Response(ApiErrorSchema().dumps(error), status=500, mimetype="text/json")
|
||||
|
||||
|
||||
connexion_app.add_error_handler(ProblemException, render_errors)
|
||||
|
|
|
@ -691,7 +691,7 @@ paths:
|
|||
required: true
|
||||
description: The unique id of an existing workflow spec category to modify.
|
||||
schema:
|
||||
type: integer
|
||||
type: string
|
||||
get:
|
||||
operationId: crc.api.workflow.get_workflow_spec_category
|
||||
summary: Returns a single workflow spec category
|
||||
|
@ -1860,7 +1860,7 @@ components:
|
|||
type: string
|
||||
nullable: true
|
||||
category_id:
|
||||
type: integer
|
||||
type: string
|
||||
nullable: true
|
||||
standalone:
|
||||
type: boolean
|
||||
|
@ -1874,7 +1874,7 @@ components:
|
|||
WorkflowSpecCategory:
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
display_name:
|
||||
|
|
|
@ -19,9 +19,9 @@ from crc.services.workflow_processor import WorkflowProcessor
|
|||
from crc.services.workflow_service import WorkflowService
|
||||
from crc.services.workflow_spec_service import WorkflowSpecService
|
||||
|
||||
spec_service = WorkflowSpecService()
|
||||
|
||||
def all_specifications(libraries=False,standalone=False):
|
||||
spec_service = WorkflowSpecService()
|
||||
if libraries and standalone:
|
||||
raise ApiError('inconceivable!', 'You should specify libraries or standalone, but not both')
|
||||
|
||||
|
@ -42,19 +42,19 @@ def all_specifications(libraries=False,standalone=False):
|
|||
|
||||
|
||||
def add_workflow_specification(body):
|
||||
category_name = body['display_name']
|
||||
# TODO: set this spec to the last one in the spec list
|
||||
WorkflowService.cleanup_workflow_spec_display_order(category_name)
|
||||
|
||||
# Libraries and standalone workflows don't get a category_id
|
||||
if body['library'] is True or body['standalone'] is True:
|
||||
body['category_id'] = None
|
||||
|
||||
new_spec = spec_service.add_spec(body)
|
||||
spec = WorkflowSpecInfoSchema().load(body)
|
||||
spec_service = WorkflowSpecService()
|
||||
category = spec_service.get_category(spec.category_id)
|
||||
spec.category = category
|
||||
workflows = spec_service.cleanup_workflow_spec_display_order(category)
|
||||
size = len(workflows)
|
||||
spec.display_order = size
|
||||
new_spec = spec_service.add_spec(spec)
|
||||
return WorkflowSpecInfoSchema().dump(new_spec)
|
||||
|
||||
|
||||
def get_workflow_specification(spec_id):
|
||||
spec_service = WorkflowSpecService()
|
||||
if spec_id is None:
|
||||
raise ApiError('unknown_spec', 'Please provide a valid Workflow Specification ID.')
|
||||
spec = spec_service.get_spec(spec_id)
|
||||
|
@ -65,6 +65,8 @@ def get_workflow_specification(spec_id):
|
|||
return WorkflowSpecInfoSchema().dump(spec)
|
||||
|
||||
def validate_spec_and_library(spec_id,library_id):
|
||||
spec_service = WorkflowSpecService()
|
||||
|
||||
if spec_id is None:
|
||||
raise ApiError('unknown_spec', 'Please provide a valid Workflow Specification ID.')
|
||||
if library_id is None:
|
||||
|
@ -82,8 +84,11 @@ def validate_spec_and_library(spec_id,library_id):
|
|||
|
||||
def add_workflow_spec_library(spec_id, library_id):
|
||||
validate_spec_and_library(spec_id, library_id)
|
||||
spec_service = WorkflowSpecService()
|
||||
spec = spec_service.get_spec(spec_id)
|
||||
libraryids = [x.id for x in spec.libraries]
|
||||
libraries: spec_service.get_libraries()
|
||||
libraryids = [x.library_spec_id for x in libraries]
|
||||
if library_id in libraryids:
|
||||
raise ApiError('unknown_spec', 'The Library Specification "' + library_id + '" is already attached.')
|
||||
|
||||
|
@ -95,6 +100,8 @@ def add_workflow_spec_library(spec_id, library_id):
|
|||
|
||||
def drop_workflow_spec_library(spec_id, library_id):
|
||||
validate_spec_and_library(spec_id, library_id)
|
||||
spec_service = WorkflowSpecService()
|
||||
|
||||
spec = spec_service.get_spec(spec_id)
|
||||
|
||||
# heres a piece of code that certainly wont work
|
||||
|
@ -118,6 +125,8 @@ def validate_workflow_specification(spec_id, study_id=None, test_until=None):
|
|||
|
||||
|
||||
def update_workflow_specification(spec_id, body):
|
||||
spec_service = WorkflowSpecService()
|
||||
|
||||
if spec_id is None:
|
||||
raise ApiError('unknown_spec', 'Please provide a valid Workflow Spec ID.')
|
||||
spec = spec_service.get_spec(spec_id)
|
||||
|
@ -140,6 +149,7 @@ def update_workflow_specification(spec_id, body):
|
|||
def delete_workflow_specification(spec_id):
|
||||
if spec_id is None:
|
||||
raise ApiError('unknown_spec', 'Please provide a valid Workflow Specification ID.')
|
||||
spec_service = WorkflowSpecService()
|
||||
|
||||
spec = spec_service.get_spec(spec_id)
|
||||
|
||||
|
@ -160,13 +170,15 @@ def delete_workflow_specification(spec_id):
|
|||
spec_service.delete_spec(spec_id)
|
||||
|
||||
# Reorder the remaining specs
|
||||
WorkflowService.cleanup_workflow_spec_display_order(spec.category_id)
|
||||
spec_service.cleanup_workflow_spec_display_order(spec.category_id)
|
||||
|
||||
|
||||
def reorder_workflow_specification(spec_id, direction):
|
||||
if direction not in ('up', 'down'):
|
||||
raise ApiError(code='bad_direction',
|
||||
message='The direction must be `up` or `down`.')
|
||||
spec_service = WorkflowSpecService()
|
||||
|
||||
spec = spec_service.get_spec(spec_id)
|
||||
if spec:
|
||||
ordered_specs = spec_service.reorder_spec(spec, direction)
|
||||
|
@ -323,24 +335,27 @@ def __update_task(processor, task, data, user):
|
|||
|
||||
|
||||
def list_workflow_spec_categories():
|
||||
spec_service = WorkflowSpecService()
|
||||
categories = spec_service.get_categories()
|
||||
return WorkflowSpecCategorySchema(many=True).dump(categories)
|
||||
|
||||
|
||||
|
||||
def get_workflow_spec_category(cat_id):
|
||||
spec_service = WorkflowSpecService()
|
||||
category = spec_service.get_category(cat_id)
|
||||
return WorkflowSpecCategorySchema().dump(category)
|
||||
|
||||
|
||||
def add_workflow_spec_category(body):
|
||||
spec_service = WorkflowSpecService()
|
||||
category = spec_service.add_category(body)
|
||||
return WorkflowSpecCategorySchema().dump(category)
|
||||
|
||||
def update_workflow_spec_category(cat_id, body):
|
||||
if cat_id is None:
|
||||
raise ApiError('unknown_category', 'Please provide a valid Workflow Spec Category ID.')
|
||||
|
||||
spec_service = WorkflowSpecService()
|
||||
category = spec_service.update_category(cat_id, body)
|
||||
|
||||
if category is None:
|
||||
|
@ -354,6 +369,7 @@ def update_workflow_spec_category(cat_id, body):
|
|||
|
||||
|
||||
def delete_workflow_spec_category(cat_id):
|
||||
spec_service = WorkflowSpecService()
|
||||
spec_service.delete_category(cat_id)
|
||||
|
||||
|
||||
|
@ -362,6 +378,7 @@ def reorder_workflow_spec_category(cat_id, direction):
|
|||
raise ApiError(code='bad_direction',
|
||||
message='The direction must be `up` or `down`.')
|
||||
WorkflowService.cleanup_workflow_spec_category_display_order()
|
||||
spec_service = WorkflowSpecService()
|
||||
category = spec_service.get_category(cat_id)
|
||||
if category:
|
||||
ordered_categories = spec_service.reorder_workflow_spec_category(category, direction)
|
||||
|
|
|
@ -8,7 +8,7 @@ from crc import db, ma
|
|||
|
||||
class WorkflowSpecCategory(object):
|
||||
def __init__(self, id, display_name, display_order, admin):
|
||||
self.id = id
|
||||
self.id = id # A unique string name, lower case, under scores (ie, 'my_category')
|
||||
self.display_name = display_name
|
||||
self.display_order = display_order
|
||||
self.admin = admin
|
||||
|
@ -27,7 +27,7 @@ class WorkflowSpecCategorySchema(ma.Schema):
|
|||
class WorkflowSpecInfo(object):
|
||||
def __init__(self, id, display_name, description, is_master_spec,
|
||||
standalone, library, primary_file_name, primary_process_id,
|
||||
libraries, category=None, display_order=0, is_review=False):
|
||||
libraries, category_id=None, display_order=0, is_review=False):
|
||||
self.id = id # Sting unique id
|
||||
self.display_name = display_name
|
||||
self.description = description
|
||||
|
@ -39,20 +39,16 @@ class WorkflowSpecInfo(object):
|
|||
self.primary_process_id = primary_process_id
|
||||
self.is_review = is_review
|
||||
self.libraries = libraries
|
||||
self.category = category
|
||||
self.category_id = category_id
|
||||
|
||||
class WorkflowSpecInfoSchema(ma.Schema):
|
||||
class Meta:
|
||||
model = WorkflowSpecInfo
|
||||
fields = ["id", "display_name", "description", "is_master_spec,",
|
||||
fields = ["id", "display_name", "description", "is_master_spec",
|
||||
"standalone", "library", "primary_file_name", "primary_process_id", "is_review",
|
||||
"libraries", "category_name", "display_order", "is_master_spec", "is_review"]
|
||||
"libraries", "display_order", "is_master_spec", "is_review", "category_id"]
|
||||
unknown = EXCLUDE
|
||||
|
||||
category_name = fields.Method("get_name")
|
||||
def get_name(self, obj):
|
||||
return obj.category.display_name
|
||||
|
||||
@post_load
|
||||
def make_spec(self, data, **kwargs):
|
||||
return WorkflowSpecInfo(**data)
|
||||
|
|
|
@ -12,6 +12,7 @@ from crc.services.jinja_service import JinjaService
|
|||
from crc.services.spec_file_service import SpecFileService
|
||||
from crc.services.user_file_service import UserFileService
|
||||
from crc.services.workflow_processor import WorkflowProcessor
|
||||
from crc.services.workflow_spec_service import WorkflowSpecService
|
||||
|
||||
|
||||
class CompleteTemplate(Script):
|
||||
|
@ -31,6 +32,7 @@ Takes two arguments:
|
|||
self.process_template(task, study_id, workflow, *args, **kwargs)
|
||||
|
||||
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
|
||||
workflow_spec_service = WorkflowSpecService()
|
||||
workflow = session.query(WorkflowModel).filter(WorkflowModel.id == workflow_id).first()
|
||||
final_document_stream = self.process_template(task, study_id, workflow, *args, **kwargs)
|
||||
file_name = args[0]
|
||||
|
@ -59,7 +61,10 @@ Takes two arguments:
|
|||
|
||||
file_data = None
|
||||
if workflow is not None:
|
||||
file_data = SpecFileService().get_data(workflow.workflow_spec, file_name)
|
||||
workflow_spec_service = WorkflowSpecService()
|
||||
workflow_spec_service.scan_file_system()
|
||||
spec = workflow_spec_service.get_spec(workflow.workflow_spec_id)
|
||||
file_data = SpecFileService().get_data(spec, file_name)
|
||||
|
||||
# Get images from file/files fields
|
||||
if len(args) == 3:
|
||||
|
|
|
@ -1036,16 +1036,3 @@ class WorkflowService(object):
|
|||
for workflow in session.query(WorkflowModel).filter_by(workflow_spec_id=spec_id):
|
||||
StudyService.delete_workflow(workflow.id)
|
||||
|
||||
# TODO: fix this so it works with filesystem instead of DB
|
||||
@staticmethod
|
||||
def cleanup_workflow_spec_display_order(category_id):
|
||||
# make sure we don't have gaps in display_order
|
||||
new_order = 0
|
||||
specs = session.query(WorkflowSpecModel). \
|
||||
filter(WorkflowSpecModel.category_id == category_id). \
|
||||
order_by(WorkflowSpecModel.display_order).all()
|
||||
for spec in specs:
|
||||
spec.display_order = new_order
|
||||
session.add(spec)
|
||||
new_order += 1
|
||||
session.commit()
|
||||
|
|
|
@ -62,12 +62,18 @@ class WorkflowSpecService(FileSystemService):
|
|||
workflows[index-1], workflows[index] = workflows[index], workflows[index-1]
|
||||
if direction == 'down' and index < len(workflows):
|
||||
workflows[index+1], workflows[index] = workflows[index], workflows[index+1]
|
||||
return self.cleanup_workflow_spec_display_order(spec.category.id)
|
||||
|
||||
def cleanup_workflow_spec_display_order(self, category_id):
|
||||
index = 0
|
||||
for workflow in workflows:
|
||||
category = self.get_category(category_id)
|
||||
if not category:
|
||||
return []
|
||||
for workflow in category.workflows:
|
||||
workflow.display_order = index
|
||||
self.save_spec(workflow)
|
||||
self.update_spec(workflow)
|
||||
index += 1
|
||||
return workflows
|
||||
return category.workflows
|
||||
|
||||
def get_libraries(self) -> List[WorkflowSpecInfo]:
|
||||
spec_list = self.libraries.workflows
|
||||
|
@ -91,7 +97,7 @@ class WorkflowSpecService(FileSystemService):
|
|||
return self.categories[category_id]
|
||||
|
||||
def add_category(self, category: WorkflowSpecCategory):
|
||||
self.update_category(category)
|
||||
return self.update_category(category)
|
||||
|
||||
def update_category(self, category: WorkflowSpecCategory):
|
||||
cat_path = self.category_path(category.display_name)
|
||||
|
@ -100,6 +106,7 @@ class WorkflowSpecService(FileSystemService):
|
|||
with open(json_path, "w") as cat_json:
|
||||
json.dump(self.CAT_SCHEMA.dump(category), cat_json, indent=4)
|
||||
self.scan_file_system()
|
||||
return self.categories[category.id]
|
||||
|
||||
def delete_category(self, category_id: str):
|
||||
if category_id in self.categories:
|
||||
|
|
|
@ -245,7 +245,7 @@ class BaseTest(unittest.TestCase):
|
|||
data = json.loads(rv.get_data(as_text=True))
|
||||
self.assertTrue(200 <= rv.status_code < 300,
|
||||
"BAD Response: %i. \n %s" %
|
||||
(rv.status_code, json.dumps(data)) + ". " + msg)
|
||||
(rv.status_code, data['message']) + ". " + msg + ". ")
|
||||
except:
|
||||
self.assertTrue(200 <= rv.status_code < 300,
|
||||
"BAD Response: %i." % rv.status_code + ". " + msg)
|
||||
|
|
|
@ -129,7 +129,6 @@ class TestFilesApi(BaseTest):
|
|||
def test_change_primary_bpmn(self):
|
||||
self.load_example_data()
|
||||
spec = self.load_test_spec('random_fact')
|
||||
spec = session.query(WorkflowSpecModel).first()
|
||||
data = {}
|
||||
data['file'] = io.BytesIO(self.minimal_bpmn("abcdef")), 'my_new_file.bpmn'
|
||||
|
||||
|
|
|
@ -210,7 +210,6 @@ class TestWorkflowProcessor(BaseTest):
|
|||
workflow_spec_model = self.load_test_spec("docx")
|
||||
files = SpecFileService.get_files(workflow_spec_model)
|
||||
self.assertEqual(2, len(files))
|
||||
workflow_spec_model = session.query(WorkflowSpecModel).filter_by(id="docx").first()
|
||||
processor = self.get_processor(study, workflow_spec_model)
|
||||
processor.do_engine_steps()
|
||||
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
|
||||
|
|
|
@ -4,7 +4,7 @@ import os.path
|
|||
from tests.base_test import BaseTest
|
||||
from crc import session
|
||||
from crc.models.file import FileModel
|
||||
from crc.models.workflow import WorkflowModel
|
||||
from crc.models.workflow import WorkflowModel, WorkflowSpecInfoSchema, WorkflowSpecInfo, WorkflowSpecCategory
|
||||
from crc.services.spec_file_service import SpecFileService
|
||||
|
||||
from example_data import ExampleDataLoader
|
||||
|
@ -14,40 +14,38 @@ class TestWorkflowSpec(BaseTest):
|
|||
|
||||
def test_list_workflow_specifications(self):
|
||||
self.load_example_data()
|
||||
self.load_test_spec('random_fact')
|
||||
spec = session.query(WorkflowSpecModel).first()
|
||||
spec = self.load_test_spec('random_fact')
|
||||
rv = self.app.get('/v1.0/workflow-specification',
|
||||
follow_redirects=True,
|
||||
content_type="application/json",headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
specs = WorkflowSpecModelSchema(many=True).load(json_data, session=session)
|
||||
specs = WorkflowSpecInfoSchema(many=True).load(json_data)
|
||||
spec2 = specs[0]
|
||||
self.assertEqual(spec.id, spec2.id)
|
||||
self.assertEqual(spec.display_name, spec2.display_name)
|
||||
self.assertEqual(spec.description, spec2.description)
|
||||
|
||||
def test_add_new_workflow_specification(self):
|
||||
self.load_example_data()
|
||||
self.load_test_spec('random_fact')
|
||||
num_before = session.query(WorkflowSpecModel).count()
|
||||
category_id = session.query(WorkflowSpecCategoryModel).first().id
|
||||
category_count = session.query(WorkflowSpecModel).filter_by(category_id=category_id).count()
|
||||
spec = WorkflowSpecModel(id='make_cookies', display_name='Cooooookies',
|
||||
description='Om nom nom delicious cookies', category_id=category_id,
|
||||
standalone=False)
|
||||
self.workflow_spec_service.scan_file_system()
|
||||
self.assertEqual(0, len(self.workflow_spec_service.get_specs()))
|
||||
self.assertEqual(0, len(self.workflow_spec_service.get_categories()))
|
||||
cat = WorkflowSpecCategory(id="test_cat", display_name="Test Category", display_order=0, admin=False)
|
||||
self.workflow_spec_service.add_category(cat)
|
||||
spec = WorkflowSpecInfo(id='make_cookies', display_name='Cooooookies',
|
||||
description='Om nom nom delicious cookies', category_id=cat.id,
|
||||
standalone=False, is_review=False, is_master_spec=False, libraries=[], library=False,
|
||||
primary_process_id='', primary_file_name='')
|
||||
rv = self.app.post('/v1.0/workflow-specification',
|
||||
headers=self.logged_in_headers(),
|
||||
content_type="application/json",
|
||||
data=json.dumps(WorkflowSpecModelSchema().dump(spec)))
|
||||
data=json.dumps(WorkflowSpecInfoSchema().dump(spec)))
|
||||
self.assert_success(rv)
|
||||
db_spec = session.query(WorkflowSpecModel).filter_by(id='make_cookies').first()
|
||||
self.assertEqual(spec.display_name, db_spec.display_name)
|
||||
num_after = session.query(WorkflowSpecModel).count()
|
||||
self.assertEqual(num_after, num_before + 1)
|
||||
self.assertEqual(category_count, db_spec.display_order)
|
||||
category_count_after = session.query(WorkflowSpecModel).filter_by(category_id=category_id).count()
|
||||
self.assertEqual(category_count_after, category_count + 1)
|
||||
self.workflow_spec_service.scan_file_system()
|
||||
fs_spec = self.workflow_spec_service.get_spec('make_cookies')
|
||||
self.assertEqual(spec.display_name, fs_spec.display_name)
|
||||
self.assertEqual(0, fs_spec.display_order)
|
||||
self.assertEqual(1, len(self.workflow_spec_service.get_categories()))
|
||||
|
||||
def test_get_workflow_specification(self):
|
||||
self.load_example_data()
|
||||
|
@ -94,24 +92,22 @@ class TestWorkflowSpec(BaseTest):
|
|||
workflow = self.create_workflow(spec_id)
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
workflow_path = SpecFileService.workflow_path(spec)
|
||||
|
||||
num_specs_before = session.query(WorkflowSpecModel).filter_by(id=spec_id).count()
|
||||
self.workflow_spec_service.scan_file_system()
|
||||
num_specs_before = len(self.workflow_spec_service.get_specs())
|
||||
self.assertEqual(num_specs_before, 1)
|
||||
|
||||
num_files_before = len(SpecFileService.get_files(spec))
|
||||
num_workflows_before = session.query(WorkflowModel).filter_by(workflow_spec_id=spec_id).count()
|
||||
self.assertGreater(num_files_before + num_workflows_before, 0)
|
||||
|
||||
rv = self.app.delete('/v1.0/workflow-specification/' + spec_id, headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
|
||||
num_specs_after = session.query(WorkflowSpecModel).filter_by(id=spec_id).count()
|
||||
self.workflow_spec_service.scan_file_system()
|
||||
num_specs_after = len(self.workflow_spec_service.get_specs())
|
||||
self.assertEqual(0, num_specs_after)
|
||||
|
||||
# Make sure that all items in the database and file system are deleted as well.
|
||||
self.assertFalse(os.path.exists(workflow_path))
|
||||
num_workflows_after = session.query(WorkflowModel).filter_by(workflow_spec_id=spec_id).count()
|
||||
self.assertEqual(num_workflows_after, 0)
|
||||
self.assertEqual(num_workflows_after, 1)
|
||||
|
||||
def test_display_order_after_delete_spec(self):
|
||||
self.load_example_data()
|
||||
|
|
|
@ -6,7 +6,7 @@ from tests.base_test import BaseTest
|
|||
from crc import session, app
|
||||
from crc.api.common import ApiErrorSchema
|
||||
from crc.models.study import StudyModel
|
||||
from crc.models.workflow import WorkflowModel
|
||||
from crc.models.workflow import WorkflowModel, WorkflowSpecInfo
|
||||
from crc.services.workflow_service import WorkflowService
|
||||
|
||||
|
||||
|
@ -126,27 +126,15 @@ class TestWorkflowSpecValidation(BaseTest):
|
|||
"""A disabled workflow spec should fail validation"""
|
||||
app.config['PB_ENABLED'] = True
|
||||
self.load_example_data()
|
||||
category = self.assure_category_exists()
|
||||
spec = self.load_test_spec('data_security_plan')
|
||||
study_model = session.query(StudyModel).first()
|
||||
|
||||
# workflow spec to validate
|
||||
spec_model = WorkflowSpecModel(id='data_security_plan',
|
||||
display_name='Data Security Plan',
|
||||
description='Data Security Plan',
|
||||
is_master_spec=False,
|
||||
category_id=category.id,
|
||||
display_order=0,
|
||||
standalone=False,
|
||||
library=False)
|
||||
session.add(spec_model)
|
||||
session.commit()
|
||||
|
||||
# This response sets the status for data_security_plan to disabled
|
||||
status_response = self.protocol_builder_response('_get_study_status.json')
|
||||
mock_status.return_value = json.loads(status_response)[0]
|
||||
|
||||
# This should raise an ApiError which we can see in the json data
|
||||
rv = self.app.get('/v1.0/workflow-specification/%s/validate?study_id=%s' % (spec_model.id, study_model.id), headers=self.logged_in_headers())
|
||||
rv = self.app.get('/v1.0/workflow-specification/%s/validate?study_id=%s' % (spec.id, study_model.id), headers=self.logged_in_headers())
|
||||
self.assert_success(rv)
|
||||
json_data = json.loads(rv.get_data())
|
||||
self.assertEqual(1, len(json_data))
|
||||
|
|
Loading…
Reference in New Issue