Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration tests #20

Merged
merged 3 commits into from
Feb 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added integration_tests/__init__.py
Empty file.
53 changes: 53 additions & 0 deletions integration_tests/integration_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import sys
from argparse import ArgumentParser

import os
import yaml
from pyclarity_lims.lims import Lims

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from integration_tests.test_entities import generate_entities_expected_output, test_all_entities


def main():
a = ArgumentParser()
a.add_argument('--url', type=str)
a.add_argument('--username', type=str)
a.add_argument('--password', type=str)
a.add_argument('--config', type=str, required=True)
a.add_argument('--generate_config', action='store_true')
args = a.parse_args()

config = {}
if os.path.isfile(args.config):
with open(args.config, 'r') as open_file:
config = yaml.load(open_file)

url = args.url or config.get('clarity', {}).get('url')
if not url:
print('Specify url on the command line')
a.print_help()
return 1
username = args.username or config.get('clarity', {}).get('username')
if not username:
print('Specify username on the command line')
a.print_help()
return 1
password = args.password or config.get('clarity', {}).get('password')
if not password:
print('Specify password on the command line')
a.print_help()
return 1

lims = Lims(baseuri=url, username=username, password=password)

if args.generate_config:
config['entities'] = generate_entities_expected_output(lims)
with open(args.config, 'w') as open_file:
yaml.dump(config, open_file, width=180, indent=4)
else:
test_all_entities(lims, config.get('entities', {}))


if __name__ == '__main__':
sys.exit(main())
243 changes: 243 additions & 0 deletions integration_tests/test_entities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import random

from pyclarity_lims.entities import *


def _str_representation(value):
if isinstance(value, list):
return [_str_representation(v) for v in value]
if isinstance(value, tuple):
return [_str_representation(v) for v in value]
elif isinstance(value, dict):
return dict([(k, _str_representation(v)) for k, v in value.items()])
elif value is None:
return value
else:
return repr(value)


def _stringify_and_order(value):
if isinstance(value, list):
return str(sorted([_stringify_and_order(v) for v in value]))
elif isinstance(value, dict):
return '{' + ', '.join(["%s: %s" % (k, _stringify_and_order(value[k])) for k in sorted(value)]) + '}'
else:
return str(value)


class TestEntity():
proprerties = None
functions = None
klass = None

def __init__(self, lims, entity_uri, value_dict):
self.entity = self.klass(lims, uri=entity_uri)
self.value_dict = value_dict

def test_properties(self):
tested_properties = []
for k in self.value_dict:
assert k in self.proprerties
tested_properties.append(k)
exp = self.value_dict.get(k)
obs = _str_representation(getattr(self.entity, k))
if _stringify_and_order(exp) != _stringify_and_order(obs):
print('In testing %s comparing %s expected and observed' % (self.klass.__name__, k))
print('exp: ' + str(exp))
print('obs: ' + str(obs))

non_tested_properties = set(self.proprerties).difference(tested_properties)
for k in non_tested_properties:
# only access to make sure nothing crash in the parsing
getattr(self.entity, k)

def get_expected_values(self):
expected_values = {}
for prop in self.proprerties:
value = getattr(self.entity, prop)
expected_values[prop] = _str_representation(value)
return expected_values


class TestArtifact(TestEntity):
proprerties = ['concentration', 'container', 'files', 'id', 'location', 'name', 'output_type', 'parent_process',
'qc_flag', 'reagent_labels', 'samples', 'type', 'udf', 'uri', 'volume',
'workflow_stages', 'workflow_stages_and_statuses', 'working_flag']
functions = ['create', 'input_artifact_list']
klass = Artifact

class TestSample(TestEntity):

proprerties = ['artifact', 'date_completed', 'date_received', 'externalids', 'files', 'id',
'name', 'notes', 'project', 'submitter', 'udf', 'udt']
functions = ['create']
klass = Sample


class TestProject(TestEntity):

proprerties = ['close_date', 'externalids', 'files', 'id', 'invoice_date', 'name', 'open_date', 'researcher',
'udf', 'udt']
functions = ['create']
klass = Project


class TestContainer(TestEntity):

proprerties = ['id', 'name', 'occupied_wells', 'placements', 'state', 'type', 'udf', 'udt']
functions = ['get_placements', 'create']
klass = Container


class TestProcess(TestEntity):

proprerties = ['date_run', 'files', 'id', 'input_output_maps', 'process_parameter', 'protocol_name', 'step',
'technician', 'type', 'udf', 'udt']
functions = ['create', 'output_containers', 'parent_processes','analytes','input_per_sample', 'all_inputs',
'all_outputs', 'result_files', 'shared_result_files', 'outputs_per_input']
klass = Process


class TestStep(TestEntity):
proprerties = ['actions', 'available_programs', 'configuration', 'current_state',
'date_completed', 'date_started', 'details', 'id', 'placements', 'pools',
'process', 'program_names', 'program_status', 'reagent_lots']
functions = ['create', 'advance', 'set_placements', 'trigger_program']
klass = Step


class TestWorkflow(TestEntity):
proprerties = ['name', 'protocols', 'stages', 'status']
functions = ['create']
klass = Workflow


class TestProtocol(TestEntity):
proprerties = ['id', 'properties', 'steps']
functions = ['create']
klass = Protocol


class TestLab(TestEntity):

proprerties = ['billing_address', 'externalids', 'id', 'name', 'shipping_address', 'udf', 'udt', 'website']
functions = ['create']
klass = Lab


class TestResearcher(TestEntity):

proprerties = ['email', 'externalids', 'fax', 'first_name', 'id', 'initials', 'lab', 'last_name', 'name', 'phone',
'udf', 'udt']
functions = ['create']
klass = Researcher


class TestReagentKit(TestEntity):

proprerties = ['archived', 'id', 'name', 'supplier', 'uri', 'website']
functions = ['create']
klass = ReagentKit


class TestReagentLot(TestEntity):

proprerties = ['created_by', 'created_date', 'expiry_date', 'id', 'last_modified_by', 'last_modified_date',
'lot_number', 'name', 'reagent_kit', 'status', 'usage_count']
functions = ['create']
klass = ReagentLot


class TestReagentType(TestEntity):

proprerties = ['category', 'id', 'sequence']
functions = ['create']
klass = ReagentType


class TestReagentType(TestEntity):
proprerties = ['category', 'id', 'sequence']
functions = ['create']
klass = ReagentType


class TestStage(TestEntity):
proprerties = ['id', 'index', 'name', 'protocol', 'step', 'workflow']
functions = ['create']
klass = Stage


class TestProtocolStep(TestEntity):
proprerties = ['epp_triggers', 'id', 'name', 'permitted_containers', 'queue_fields', 'sample_fields', 'step_fields',
'step_properties', 'type', 'uri']
functions = ['create']
klass = ProtocolStep


class TestQueue(TestEntity):
proprerties = ['artifacts', 'id', 'queued_artifacts', 'uri']
functions = ['create']
klass = Queue


test_classes = [TestArtifact, TestSample, TestProject, TestContainer, TestLab, TestResearcher, TestProcess, TestStep,
TestWorkflow, TestProtocol, TestReagentKit, TestReagentLot, TestReagentType, TestStage,
TestProtocolStep, TestQueue]
map_entity_to_test = dict([(test_class.klass, test_class) for test_class in test_classes])


def generate_entities_expected_output(lims):
"""
This function will extract random entities from the provided lims and dump a string representation in a dict
"""
def get_x_entities(list_entities, test_class, number, uri_getter=None):
dict_x_entities = {}
for ent in random.sample(list_entities, number):
if uri_getter:
uri = uri_getter(ent)
else:
uri = ent.uri
test_ent = test_class(lims, uri, None)
dict_x_entities[uri] = test_ent.get_expected_values()
return dict_x_entities

nb = 10 # Number of entities sampled
expected_values = {}

expected_values[TestArtifact.klass.__name__] = get_x_entities(lims.get_artifacts(), TestArtifact, nb)
expected_values[TestSample.klass.__name__] = get_x_entities(lims.get_samples(), TestSample, nb)
expected_values[TestProject.klass.__name__] = get_x_entities(lims.get_projects(), TestProject, nb)
expected_values[TestContainer.klass.__name__] = get_x_entities(lims.get_containers(), TestContainer, nb)
processes = lims.get_processes()
expected_values[TestProcess.klass.__name__] = get_x_entities(processes, TestProcess, nb)
# Steps and processes have the same ids
expected_values[TestStep.klass.__name__] = get_x_entities(processes, TestStep, nb, uri_getter=lambda x: x.step.uri)

expected_values[TestLab.klass.__name__] = get_x_entities(lims.get_labs(), TestLab, nb)
expected_values[TestResearcher.klass.__name__] = get_x_entities(lims.get_researchers(), TestResearcher, nb)
expected_values[TestReagentKit.klass.__name__] = get_x_entities(lims.get_reagent_kits(), TestReagentKit, nb)
expected_values[TestReagentLot.klass.__name__] = get_x_entities(lims.get_reagent_lots(), TestReagentLot, nb)
expected_values[TestReagentType.klass.__name__] = get_x_entities(lims.get_reagent_types(), TestReagentType, nb)
workflows = lims.get_workflows()
expected_values[TestWorkflow.klass.__name__] = get_x_entities(workflows, TestWorkflow, nb)
# get all stages from a random set of workflows
stages = [stage for sublist in random.sample(workflows, nb) for stage in sublist.stages]
expected_values[TestStage.klass.__name__] = get_x_entities(stages, TestStage, nb)

protocols = lims.get_protocols()
expected_values[TestProtocol.klass.__name__] = get_x_entities(protocols, TestProtocol, nb)
# get all steps from a random set of protocols
steps = [step for protocol in random.sample(protocols, nb) for step in protocol.steps]
expected_values[TestProtocolStep.klass.__name__] = get_x_entities(steps, TestProtocolStep, nb)
expected_values[TestQueue.klass.__name__] = get_x_entities(steps, TestQueue, nb, uri_getter=lambda x: x.queue.uri)

return expected_values

def test_all_entities(lims, entities_config):
for entity in map_entity_to_test:
entity_config = entities_config.get(entity.__name__)
test_class = map_entity_to_test.get(entity)
if entity_config:
for entity_uri in entity_config:
test = test_class(lims, entity_uri, entity_config.get(entity_uri))
test.test_properties()
7 changes: 5 additions & 2 deletions pyclarity_lims/descriptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,11 @@ def __get__(self, instance, cls):
from pyclarity_lims.entities import Container
instance.get()
node = self.rootnode(instance).find(self.tag)
uri = node.find('container').attrib['uri']
return Container(instance.lims, uri=uri), node.find('value').text
if node:
uri = node.find('container').attrib['uri']
return Container(instance.lims, uri=uri), node.find('value').text
else:
return None


class MutableDescriptor(BaseDescriptor):
Expand Down
Loading