def test_internal_matching(database, gpx4_patients):
    """Testing the combined matching algorithm"""

    # load 2 test patients in mock database
    for patient in gpx4_patients:
        mme_pat = mme_patient(patient, True)  # convert gene symbol to ensembl
        database['patients'].insert_one(mme_pat).inserted_id

    # 2 patients should be inserted
    results = database['patients'].find(
        {'genomicFeatures.gene.id': 'ENSG00000167468'})
    assert len(list(results)) == 2

    # test matching of one of the 2 patients against both patients in database
    proband_patient = mme_patient(gpx4_patients[0], True)

    match = internal_matcher(database, proband_patient, 0.5, 0.5)
    match_patients = match['results'][0]['patients']
    assert len(match_patients) == 2

    higest_scored_patient = match_patients[
        0]  # first returned patient has higher score
    lowest_scored_patient = match_patients[
        -1]  # last returned patient has lower score

    assert higest_scored_patient['score']['patient'] > lowest_scored_patient[
        'score']['patient']
示例#2
0
def test_internal_matching_with_threshold(database, gpx4_patients):
    # load 2 test patients in mock database
    for patient in gpx4_patients:
        mme_pat = mme_patient(patient, True)  # convert gene symbol to ensembl
        database["patients"].insert_one(mme_pat).inserted_id

    # 2 patients should be inserted
    results = database["patients"].find(
        {"genomicFeatures.gene.id": "ENSG00000167468"})
    assert len(list(results)) == 2

    # test matching of one of the 2 patients against both patients in database
    proband_patient = mme_patient(gpx4_patients[0], True)

    match = internal_matcher(
        database=database,
        patient_obj=proband_patient,
        max_pheno_score=0.5,
        max_geno_score=0.5,
        max_results=5,
        score_threshold=0.5,
    )
    match_patients = match["results"][0]["patients"]
    assert len(
        match_patients) == 1  # one patient is filtered out by search threshold
def test_match_ensembl_patient(mock_app, test_client, gpx4_patients, database):
    """Test matching patient with ensembl gene against patientMatcher database (internal matching)"""

    # add an authorized client to database
    ok_token = test_client["auth_token"]
    add_node(mongo_db=mock_app.db, obj=test_client, is_client=True)

    query_patient = {"patient": mme_patient(gpx4_patients[0], True)}
    assert query_patient["patient"]["genomicFeatures"][0]["gene"][
        "id"].startswith("ENSG")

    # load 2 test patient in mock database
    assert len(gpx4_patients) == 2
    inserted_ids = []
    for pat in gpx4_patients:
        # convert patient in mme patient type (convert also gene to ensembl)
        mme_pat = mme_patient(pat, True)
        inserted_ids.append(backend_add_patient(database, mme_pat))

    assert len(inserted_ids) == 2

    # make sure that there are no patient matches in the 'matches collection'
    assert database["matches"].find_one() is None

    # send a POST request to match patient with patients in database
    response = mock_app.test_client().post("/match",
                                           data=json.dumps(query_patient),
                                           headers=auth_headers(ok_token))
    assert response.status_code == 200  # POST request should be successful
    data = json.loads(response.data)
    # data should contain results and the max number of results is as defined in the config file
    assert len(data["results"]) == 2
    assert type(data["results"]) == list  # which is a list
    assert "patient" in data["results"][0]  # of patients
    assert "score" in data["results"][0]  # with matching scores
    assert "contact" in data["results"][0][
        "patient"]  # contact info should be available as well

    # make sure that a match object is created in db for this internal matching
    match = database["matches"].find_one()
    for res in match["results"]:
        for pat in res["patients"]:
            assert pat["patient"][
                "contact"]  # each result should have a contact person
            assert pat["score"]["patient"] > 0
    # and query patient should have hgnc gene symbol saved as non-standard _geneName field
    assert match["data"]["patient"]["genomicFeatures"][0]["gene"][
        "_geneName"] == "GPX4"
示例#4
0
def check_request(database, request):
    """Check if request is valid, if it is return MME formatted patient
       Otherwise return error code.
    """
    check_result = None

    # check that request is using a valid auth token
    if not authorize(database, request):
        LOG.info("Request is not authorized")
        return 401

    try: # make sure request has valid json data
        request_json = request.get_json(force=True)
    except Exception as err:
        LOG.info("Json data in request is not valid:{}".format(err))
        return 400

    try: # validate json data against MME API
        validate_api(json_obj=request_json, is_request=True)
    except Exception as err:
        LOG.info("Patient data does not conform to API:{}".format(err))
        return 422

    formatted_patient = mme_patient(json_patient=request_json['patient'],
        convert_to_ensembl = True)
    return formatted_patient
示例#5
0
def load_demo(path_to_json_data, mongo_db, convert_to_ensembl=False):
    """Inserts demo patient data into database
        Demo data consists of a set of 50 patients from this paper: http://onlinelibrary.wiley.com/doi/10.1002/humu.22850

        Args:
            path_to_demo_data(str): absolute path to json file containing the demo patients.
            mongo_db(pymongo.database.Database)

        Returns:
            inserted_ids(list): the database ID of the inserted patients
    """
    patients = [] # a list of dictionaries
    inserted_ids = []

    #open json file and try to insert one patient at the time
    try:
        LOG.info('reading patients file')
        with open(path_to_json_data) as json_data:
            patients = json.load(json_data)
            # create a progress bar
            pbar = enlighten.Counter(total=len(patients), desc='', unit='patients')
            for json_patient in patients:

                #parse patient into format accepted by database
                patient = mme_patient(json_patient, convert_to_ensembl)

                inserted_id = backend_add_patient(mongo_db=mongo_db, patient=patient)[1]
                if inserted_id:
                    inserted_ids.append(inserted_id)
                pbar.update()

    except Exception as err:
        LOG.fatal("An error occurred while importing benchmarking patients: {}".format(err))

    return inserted_ids
示例#6
0
def test_backend_remove_patient(gpx4_patients, database):
    """ Test adding 2 test patients and then removing them using label or ID """

    # test conversion to format required for the database:
    test_mme_patients = [ mme_patient(json_patient=patient) for patient in gpx4_patients]

    # make sure 2 json patient are correctly parsed
    assert len(test_mme_patients) == 2

    # insert the 2 patients into the database
    inserted_ids = [ backend_add_patient(mongo_db=database, patient=mme_patient, match_external=False) for mme_patient in test_mme_patients ]
    assert len(inserted_ids) == 2

    # make sure that inserted patients contain computed phenotypes from Monarch
    a_patient = database['patients'].find_one()
    assert a_patient

    # test removing a patient by ID:
    remove_query = {'_id' : 'P0001058'}
    deleted = delete_by_query(remove_query, database, 'patients')
    db_patients = database['patients'].find()
    assert db_patients.count() == 1

    # test removing a patient by label:
    remove_query = {'label' : '350_2-test'}
    deleted = delete_by_query(remove_query, database, 'patients')
    db_patients = database['patients'].find()
    assert db_patients.count() == 0
def test_delete_patient(mock_app, database, gpx4_patients, test_client,
                        match_objs):
    """Test deleting a patient from database by sending a DELETE request"""

    # load 2 patients from demo data in mock database
    assert len(gpx4_patients) == 2
    inserted_ids = []
    for pat in gpx4_patients:
        # convert patient in mme patient type (convert also gene to ensembl)
        mme_pat = mme_patient(pat, True)
        inserted_ids.append(backend_add_patient(database, mme_pat))

    assert len(inserted_ids) == 2

    # 50 cases present on patients collection
    delete_id = "P0001058"

    # try to delete patient without auth token:
    response = mock_app.test_client().delete("".join(
        ["patient/delete/", delete_id]))
    assert response.status_code == 401

    # Add a valid client node
    ok_token = test_client["auth_token"]
    add_node(mongo_db=mock_app.db, obj=test_client, is_client=True)

    # Send delete request providing a valid token but a non valid id
    response = mock_app.test_client().delete("".join(
        ["patient/delete/", "not_a_valid_ID"]),
                                             headers=auth_headers(ok_token))
    assert response.status_code == 200
    data = json.loads(response.data)
    # but server returns error
    assert (
        data["message"] ==
        "ERROR. Could not delete a patient with ID not_a_valid_ID from database"
    )

    assert database["matches"].find_one() is None  # no matches in database
    # insert into database some mock matching objects
    database["matches"].insert_many(match_objs)

    # patient "delete_id" should have two associated matches in database
    results = database["matches"].find({"data.patient.id": delete_id})
    assert len(list(results)) == 2

    # Send valid patient ID and valid token
    response = mock_app.test_client().delete("".join(
        ["patient/delete/", delete_id]),
                                             headers=auth_headers(ok_token))
    assert response.status_code == 200

    # make sure that the patient was removed from database
    results = database["patients"].find()
    assert len(list(results)) == 1

    # make sure that patient matches are also gone
    results = database["matches"].find()
    assert len(list(results)) == 1
示例#8
0
def test_genotype_matching(database, gpx4_patients):
    """Testing the genotyping matching algorithm"""

    # load 2 test patients in mock database
    for patient in gpx4_patients:
        mme_pat = mme_patient(patient, True)  # convert gene symbol to ensembl
        database['patients'].insert_one(mme_pat).inserted_id

    # 2 patients should be inserted
    assert database['patients'].find({
        'genomicFeatures.gene.id':
        'ENSG00000167468'
    }).count() == 2

    # test matching of a patient (with variants in genes) against the demo patients in database
    proband_patient = mme_patient(gpx4_patients[0], True)

    # assert patient has genomic features
    gt_features = proband_patient['genomicFeatures']
    assert len(gt_features) == 2  # should have 2 feature to match
    assert gt_features[0]['gene']['id'] == 'ENSG00000167468'

    # match features against database with 2 patients
    matches = match(database, gt_features, 0.5)
    assert len(matches.keys()) == 2  # 2 matching patients are returned

    for key, value in matches.items():
        # patient object should also be returned
        assert 'patient_obj' in value

        # genotype score for each patient should be higher than 0
        assert value['geno_score'] > 0

    # make sure that the algorithm works even if a gene or a variant object is missing:
    # remove gene ID from first gt feature
    gt_features[0]['gene']['id'] = ''
    matches = match(database, gt_features, 0.5)
    # same patient should be returned, because of variant matching instead
    assert len(matches) == 2

    # Remove variant object from second gt feature
    gt_features[1]['variant'] = None
    matches = match(database, gt_features, 0.5)
    # same patients should be returned, because of gene matching instead
    assert len(matches.keys()) == 2
示例#9
0
def test_phenotype_matching(gpx4_patients, database):
    """test the algorithm that compares the phenotype of a query patient against the database"""

    # insert 2 test patients into test database
    for patient in gpx4_patients:
        database["patients"].insert_one(patient)
    assert len(list(database["patients"].find())) == 2

    query_patient = gpx4_patients[0]
    assert query_patient

    # this patient has HPO terms and OMIM diagnosis
    formatted_patient = mme_patient(query_patient)
    assert len(formatted_patient["features"]) > 0
    assert len(formatted_patient["disorders"]) > 0

    matches_HPO_OMIM = match(database, 0.75, formatted_patient["features"],
                             formatted_patient["disorders"])
    assert len(matches_HPO_OMIM.keys()) == 2
    for key, value in matches_HPO_OMIM.items():
        assert "patient_obj" in value
        assert value["pheno_score"] > 0

    features = formatted_patient["features"]
    disorders = formatted_patient["disorders"]
    # remove HPO terms from the query patient, test that the algorithm works anyway
    # because matching will use OMIM disorders
    formatted_patient["features"] = []
    matches_OMIM = match(database, 0.75, formatted_patient["features"],
                         formatted_patient["disorders"])
    assert len(matches_OMIM.keys()) > 0 and len(matches_OMIM.keys()) < 50
    for key, value in matches_OMIM.items():
        assert "patient_obj" in value
        assert value["pheno_score"] > 0

    # remove the OMIM diagnosis from patient object. The algorithm should work
    # but it shouldn't return any match
    formatted_patient["disorders"] = []
    matches_no_phenotypes = match(database, 0.75,
                                  formatted_patient["features"],
                                  formatted_patient["disorders"])
    assert len(matches_no_phenotypes.keys()) == 0

    # Add again features. The algorithm works again because HPO terms will be used
    formatted_patient["features"] = features
    matches_HPO = match(database, 0.75, formatted_patient["features"],
                        formatted_patient["disorders"])
    assert len(matches_HPO.keys()) == 2
    for key, value in matches_HPO.items():
        assert "patient_obj" in value
        assert value["pheno_score"] > 0

    # make sure that matches obtained when OMIM and HPO terms are present are more or equal than
    # when either of these phenotype terms is present by itself
    assert len(matches_HPO_OMIM.keys()) >= len(matches_OMIM.keys())
    assert len(matches_HPO_OMIM.keys()) >= len(matches_HPO.keys())
示例#10
0
def test_mme_patient_entrez_gene(entrez_gene_patient, database):
    #Test format a patient with entrez gene

    # Before conversion patient's gene id is an entrez gene ID
    assert entrez_gene_patient['genomicFeatures'][0]['gene']['id'] == "3735"
    mme_formatted_patient = mme_patient(entrez_gene_patient,
                                        True)  # convert genes to Ensembl
    # After conversion formatted patient's gene id should be an Ensembl id
    assert mme_formatted_patient['genomicFeatures'][0]['gene'][
        'id'].startswith('ENSG')
示例#11
0
def test_mme_patient_entrez_gene(entrez_gene_patient, database):
    # Test format a patient with entrez gene
    # Before conversion patient's gene id is an entrez gene ID
    assert entrez_gene_patient["genomicFeatures"][0]["gene"]["id"] == "3735"
    mme_formatted_patient = mme_patient(entrez_gene_patient,
                                        True)  # convert genes to Ensembl
    # After conversion formatted patient's gene id should be an Ensembl id
    assert mme_formatted_patient["genomicFeatures"][0]["gene"][
        "id"].startswith("ENSG")
    assert mme_formatted_patient["genomicFeatures"][0]["gene"][
        "_geneName"]  # it's "KARS"
示例#12
0
def test_mme_patient_gene_symbol(gpx4_patients, database):
    # Test format a patient with gene symbol

    test_patient = gpx4_patients[0]
    # Before conversion patient's gene id is a gene symbol
    assert test_patient['genomicFeatures'][0]['gene']['id'].startswith(
        'ENSG') is False
    mme_formatted_patient = mme_patient(test_patient,
                                        True)  # Convert gene symbol to Ensembl
    # After conversion formatted patient's gene id should be an Ensembl id
    assert mme_formatted_patient['genomicFeatures'][0]['gene'][
        'id'].startswith('ENSG')
示例#13
0
def test_cli_remove_patient(mock_app, database, gpx4_patients, match_objs):

    runner = mock_app.test_cli_runner()

    # add a test patient to database
    test_patient = mme_patient(
        gpx4_patients[0], True)  # True --> convert gene symbols to ensembl
    inserted_id = mock_app.db["patients"].insert_one(test_patient).inserted_id
    assert inserted_id == gpx4_patients[0]["id"]

    # there is now 1 patient in database
    assert database["patients"].find_one()

    # test that without a valid id or label no patient is removed
    result = runner.invoke(cli, ["remove", "patient", "-id", "", "-label", ""])
    assert "Error" in result.output

    # Add mock patient matches objects to database
    database["matches"].insert_many(match_objs)
    # There should be 2 matches in database for this patient:
    results = database["matches"].find({"data.patient.id": inserted_id})
    assert len(list(results)) == 2

    # involke cli command to remove the patient by id and label
    result = runner.invoke(cli, [
        "remove", "patient", "-id", inserted_id, "-label", "350_1-test",
        "-leave_matches"
    ])
    assert result.exit_code == 0

    # check that the patient was removed from database
    assert database["patients"].find_one() is None

    # But matches are still there
    results = database["matches"].find({"data.patient.id": inserted_id})
    assert len(list(results)) == 2

    # Run remove patient command with option to remove matches but without patient ID
    result = runner.invoke(
        cli, ["remove", "patient", "-label", "350_1-test", "-remove_matches"])
    # And make sure that it doesn't work
    assert "Please provide patient ID and not label to remove all its matches." in result.output

    # Test now the proper command to remove patient matches:
    result = runner.invoke(
        cli, ["remove", "patient", "-id", inserted_id, "-remove_matches"])
    assert result.exit_code == 0

    # And make sure that patient removal removed its matchings
    assert database["matches"].find_one({"data.patient.id": inserted_id
                                         }) is None
示例#14
0
def test_mme_patient_gene_symbol(gpx4_patients, database):
    # Test format a patient with HGNC gene symbol

    test_patient = gpx4_patients[0]
    gene_name = test_patient["genomicFeatures"][0]["gene"]["id"]  # "GPX4"
    # Before conversion patient's gene id is a gene symbol
    assert gene_name.startswith("ENSG") is False
    mme_formatted_patient = mme_patient(test_patient,
                                        True)  # Convert gene symbol to Ensembl
    # After conversion formatted patient's gene id should be an Ensembl id
    assert mme_formatted_patient["genomicFeatures"][0]["gene"][
        "id"].startswith("ENSG")
    assert mme_formatted_patient["genomicFeatures"][0]["gene"][
        "_geneName"] == gene_name
示例#15
0
def test_match_hgnc_symbol_patient(mock_app, gpx4_patients, test_client,
                                   database):
    """Testing matching patient with gene symbl against patientMatcher database (internal matching)"""

    # add an authorized client to database
    ok_token = test_client['auth_token']
    add_node(mongo_db=mock_app.db, obj=test_client, is_client=True)

    query_patient = {'patient': gpx4_patients[0]}
    assert query_patient['patient']['genomicFeatures'][0]['gene'][
        'id'] == 'GPX4'

    # load 2 test patient in mock database
    assert len(gpx4_patients) == 2
    inserted_ids = []
    for pat in gpx4_patients:
        # convert patient in mme patient type (convert also gene to ensembl)
        mme_pat = mme_patient(pat, True)
        inserted_ids.append(backend_add_patient(database, mme_pat))

    assert len(inserted_ids) == 2

    # test the API response validator with non valid patient data:
    malformed_match_results = {'results': 'fakey_results'}
    assert validate_response(malformed_match_results) == 422

    # make sure that there are no patient matches in the 'matches collection'
    assert database['matches'].find().count() == 0

    # send a POST request to match patient with patients in database
    response = mock_app.test_client().post('/match',
                                           data=json.dumps(query_patient),
                                           headers=auth_headers(ok_token))
    assert response.status_code == 200  # POST request should be successful
    data = json.loads(response.data)
    # data should contain results and the max number of results is as defined in the config file
    assert len(data['results']) == 2
    assert type(data['results']) == list  # which is a list
    assert 'patient' in data['results'][0]  # of patients
    assert 'score' in data['results'][0]  # with matching scores
    assert 'contact' in data['results'][0][
        'patient']  # contact info should be available as well

    # make sure that there are match object is created in db for this internal matching
    match = database['matches'].find_one()
    for res in match['results']:
        for pat in res['patients']:
            assert pat['patient'][
                'contact']  # each result should have a contact person
            assert pat['score']['patient'] > 0
def test_match_entrez_patient(mock_app, test_client, gpx4_patients, database):
    """Test matching patient with ensembl gene against patientMatcher database (internal matching)"""

    # add an authorized client to database
    ok_token = test_client['auth_token']
    add_node(mongo_db=mock_app.db, obj=test_client, is_client=True)

    query_patient = gpx4_patients[0]
    query_patient = {'patient': gpx4_patients[0]}
    for feat in query_patient['patient']['genomicFeatures']:
        feat['gene']['id'] == "2879"  # entrez id for GPX4

    # load 2 test patient in mock database
    assert len(gpx4_patients) == 2
    inserted_ids = []
    for pat in gpx4_patients:
        # convert patient in mme patient type (convert also gene to ensembl)
        mme_pat = mme_patient(pat, True)
        inserted_ids.append(backend_add_patient(database, mme_pat))

    assert len(inserted_ids) == 2

    # make sure that there are no patient matches in the 'matches collection'
    assert database['matches'].find_one() is None

    # send a POST request to match patient with patients in database
    response = mock_app.test_client().post('/match',
                                           data=json.dumps(query_patient),
                                           headers=auth_headers(ok_token))
    assert response.status_code == 200  # POST request should be successful
    data = json.loads(response.data)
    # data should contain results and the max number of results is as defined in the config file
    assert len(data['results']) == 2
    assert type(data['results']) == list  # which is a list
    assert 'patient' in data['results'][0]  # of patients
    assert 'score' in data['results'][0]  # with matching scores
    assert 'contact' in data['results'][0][
        'patient']  # contact info should be available as well

    # make sure that a match object is created in db for this internal matching
    match = database['matches'].find_one()
    for res in match['results']:
        for pat in res['patients']:
            assert pat['patient'][
                'contact']  # each result should have a contact person
            assert pat['score']['patient'] > 0
    # and query patient should have hgnc gene symbol saved as non-standard _geneName field
    assert match['data']['patient']['genomicFeatures'][0]['gene'][
        '_geneName'] == 'GPX4'
示例#17
0
def test_cli_remove_patient(mock_app, database, gpx4_patients, match_objs):

    runner = mock_app.test_cli_runner()

    # add a test patient to database
    test_patient  = mme_patient(gpx4_patients[0], True) # True --> convert gene symbols to ensembl
    inserted_id = mock_app.db['patients'].insert_one(test_patient).inserted_id
    assert inserted_id == gpx4_patients[0]['id']

    # there is now 1 patient in database
    assert database['patients'].find().count() == 1

    # test that without a valid id or label no patient is removed
    result =  runner.invoke(cli, ['remove', 'patient', '-id', '', '-label', ''])
    assert 'Error' in result.output

    # Add mock patient matches objects to database
    database['matches'].insert_many(match_objs)
    # There should be 2 matches in database for this patient:
    assert database['matches'].find( {'data.patient.id' : inserted_id }).count() == 2

    # involke cli command to remove the patient by id and label
    result =  runner.invoke(cli, ['remove', 'patient', '-id', inserted_id, '-label', '350_1-test', '-leave_matches'])
    assert result.exit_code == 0

    # check that the patient was removed from database
    assert database['patients'].find().count() == 0

    # But matches are still there
    assert database['matches'].find( {'data.patient.id' : inserted_id }).count() == 2

    # Run remove patient command with option to remove matches but without patient ID
    result =  runner.invoke(cli, ['remove', 'patient', '-label', '350_1-test', '-remove_matches'])
    # And make sure that it doesn't work
    assert 'Please provide patient ID and not label to remove all its matches.' in result.output

    # Test now the proper command to remove patient matches:
    result =  runner.invoke(cli, ['remove', 'patient', '-id', inserted_id, '-remove_matches'])
    assert result.exit_code == 0

    # And make sure that patient removal removed its matchings
    assert database['matches'].find( {'data.patient.id' : inserted_id }).count() == 0
示例#18
0
def test_match_async_request(mock_app, database, async_response_obj,
                             json_patients, test_node):
    """This function tests the situation when this server is receiving a request containing
    results from an asynchronous server"""

    # send a POST request with no data to the async endpoint
    response = mock_app.test_client().post('/async_response',
                                           headers=unauth_headers())
    # server should return a request data is not valid code == 400
    assert response.status_code == 400

    # provide data object not containing a query id key
    data = {'key1': 'value1'}
    response = mock_app.test_client().post('/async_response',
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should return a not authorized response (401)
    assert response.status_code == 401

    # provide data object with query id not previously saved in database
    data = {
        'query_id': async_response_obj['query_id'],
        'source': 'fakey node',
        'response': {
            "results": [{
                "score": {
                    "patient": 0.8
                },
                "patient": json_patients[1],
            }]
        }
    }

    response = mock_app.test_client().post('/async_response',
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should again return a not authorized response (401)
    assert response.status_code == 401

    # save async_response_obj into database
    mock_app.db['async_responses'].insert_one(async_response_obj)
    assert mock_app.db['async_responses'].find().count() == 1

    # send a response with valid data but query patient is not in database
    response = mock_app.test_client().post('/async_response',
                                           data=json.dumps(data),
                                           headers=unauth_headers())

    # server should return ok code and an error message
    assert response.status_code == 200
    resp_data = json.loads(response.data)
    assert resp_data[
        'message'] == 'Error: could not create a valid match object from request data'

    # convert json test patient in mongodb patient object
    test_patient = mme_patient(json_patients[0])

    # save test patient in database
    mock_app.db['patients'].insert_one(test_patient)
    assert mock_app.db['patients'].find().count() == 1

    # There should be no match object in database
    assert mock_app.db['matches'].find().count() == 0

    # send a response with valid data
    # patient object is in database
    response = mock_app.test_client().post('/async_response',
                                           data=json.dumps(data),
                                           headers=unauth_headers())

    assert response.status_code == 200
    resp_data = json.loads(response.data)
    assert resp_data['message'] == 'results received, many thanks!'

    # make sure the async response entry was removed from database
    assert mock_app.db['async_responses'].find({
        'query_id':
        async_response_obj['query_id']
    }).count() == 0

    # and that match result was saved to server
    assert mock_app.db['matches'].find().count() == 1

    # re-introduce async response in database for further testing
    mock_app.db['async_responses'].insert_one(async_response_obj)
    # test the enpoint by providing a request with no 'response' key
    data = {
        'query_id': async_response_obj['query_id'],
        'source': 'fakey node',
    }
    response = mock_app.test_client().post('/async_response',
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should return code 400 (data is not valid)
    assert response.status_code == 400

    # test the enpoint by providing a request with 'response' key containing
    # results not conforming to the API
    data = {
        'query_id': async_response_obj['query_id'],
        'source': 'fakey node',
        'response': {
            'results': ['malformed_result1', 'malformed_result2']
        }
    }
    response = mock_app.test_client().post('/async_response',
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should return status 422 (Patient data does not conform to API)
    assert response.status_code == 422
def test_match_external(mock_app, test_client, test_node, database,
                        json_patients):
    """Testing the view that is sending post request to trigger matches on external nodes"""

    # add an authorized client to database
    ok_token = test_client["auth_token"]
    add_node(mongo_db=mock_app.db, obj=test_client,
             is_client=True)  # required to trigger external matches

    a_patient = json_patients[0]
    parsed_patient = mme_patient(a_patient)

    # insert patient into mock database:
    assert database["patients"].find_one() is None
    inserted_id = database["patients"].insert_one(parsed_patient).inserted_id
    assert database["patients"].find_one()

    # send an un-authorized match request to server
    response = mock_app.test_client().post("".join(
        ["/match/external/", inserted_id]))
    # server should return 401 (not authorized)
    assert response.status_code == 401

    # send an authorized request with a patient ID that doesn't exist on server:
    response = mock_app.test_client().post("".join(
        ["/match/external/", "not_a_valid_ID"]),
                                           headers=auth_headers(ok_token))
    # Response is valid
    assert response.status_code == 200
    data = json.loads(response.data)
    # but server returns error
    assert data[
        "message"] == "ERROR. Could not find any patient with ID not_a_valid_ID in database"

    # there are no matches in mock database
    assert database["matches"].find_one() is None
    # after sending an authorized request with a patient ID that exists on database

    # Check that external matching doesn't work if there are no connected nodes:
    response = mock_app.test_client().post("".join(
        ["/match/external/", inserted_id]),
                                           headers=auth_headers(ok_token))
    assert response.status_code == 200
    data = json.loads(response.data)
    assert data[
        "message"] == "Could not find any other node connected to this MatchMaker server"

    # Try to send a request for a match on a node that does not exist
    response = mock_app.test_client().post("".join(
        ["/match/external/", inserted_id, "?node=meh"]),
                                           headers=auth_headers(ok_token))
    assert response.status_code == 200
    data = json.loads(response.data)
    # And check that node not found is in response message
    assert data[
        "message"] == "ERROR. Could not find any connected node with id meh in database"

    # insert a connected node
    add_node(mongo_db=mock_app.db, obj=test_node,
             is_client=False)  # required for external matches
    # send a request to match patients against all nodes
    response = mock_app.test_client().post("".join(
        ["/match/external/", inserted_id]),
                                           headers=auth_headers(ok_token))

    # Response should be valid
    assert response.status_code == 200
    # And a new match should be created in matches collection
    assert database["matches"].find_one()

    # send a request to match patients against the specific existing node:
    response = mock_app.test_client().post(
        "".join(["/match/external/", inserted_id, "?node=", test_node["_id"]]),
        headers=auth_headers(ok_token),
    )
    # Response should be valid
    assert response.status_code == 200

    # And a new match should be created in matches collection. So total matches are 2
    results = database["matches"].find()
    assert len(list(results)) == 2
def test_match_async_request(mock_app, database, async_response_obj,
                             json_patients, test_node):
    """This function tests the situation when this server is receiving a request containing
    results from an asynchronous server"""

    # send a POST request with no data to the async endpoint
    response = mock_app.test_client().post("/async_response",
                                           headers=unauth_headers())
    # server should return a request data is not valid code == 400
    assert response.status_code == 400

    # provide data object not containing a query id key
    data = {"key1": "value1"}
    response = mock_app.test_client().post("/async_response",
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should return a not authorized response (401)
    assert response.status_code == 401

    # provide data object with query id not previously saved in database
    data = {
        "query_id": async_response_obj["query_id"],
        "source": "fakey node",
        "response": {
            "results": [{
                "score": {
                    "patient": 0.8
                },
                "patient": json_patients[1],
            }]
        },
    }

    response = mock_app.test_client().post("/async_response",
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should again return a not authorized response (401)
    assert response.status_code == 401

    # save async_response_obj into database
    assert mock_app.db["async_responses"].insert_one(
        async_response_obj).inserted_id

    # send a response with valid data but query patient is not in database
    response = mock_app.test_client().post("/async_response",
                                           data=json.dumps(data),
                                           headers=unauth_headers())

    # server should return ok code and an error message
    assert response.status_code == 200
    resp_data = json.loads(response.data)
    assert resp_data[
        "message"] == "Error: could not create a valid match object from request data"

    # convert json test patient in mongodb patient object
    test_patient = mme_patient(json_patients[0])

    # save test patient in database
    assert mock_app.db["patients"].insert_one(test_patient).inserted_id

    # There should be no match object in database
    assert mock_app.db["matches"].find_one() is None

    # send a response with valid data
    # patient object is in database
    response = mock_app.test_client().post("/async_response",
                                           data=json.dumps(data),
                                           headers=unauth_headers())

    assert response.status_code == 200
    resp_data = json.loads(response.data)
    assert resp_data["message"] == "results received, many thanks!"

    # make sure the async response entry was removed from database
    results = mock_app.db["async_responses"].find(
        {"query_id": async_response_obj["query_id"]})
    assert len(list(results)) == 0

    # and that match result was saved to server
    assert mock_app.db["matches"].find_one()

    # re-introduce async response in database for further testing
    mock_app.db["async_responses"].insert_one(async_response_obj)
    # test the enpoint by providing a request with no 'response' key
    data = {
        "query_id": async_response_obj["query_id"],
        "source": "fakey node",
    }
    response = mock_app.test_client().post("/async_response",
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should return code 400 (data is not valid)
    assert response.status_code == 400

    # test the enpoint by providing a request with 'response' key containing
    # results not conforming to the API
    data = {
        "query_id": async_response_obj["query_id"],
        "source": "fakey node",
        "response": {
            "results": ["malformed_result1", "malformed_result2"]
        },
    }
    response = mock_app.test_client().post("/async_response",
                                           data=json.dumps(data),
                                           headers=unauth_headers())
    # server should return status 422 (Patient data does not conform to API)
    assert response.status_code == 422