예제 #1
0
    def test_api_stats(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=True)
        # create a dataset for group A
        dataset_A = {"name": faker.pystr(), "description": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/datasets",
            headers=user_A1_headers,
            data=dataset_A,
        )
        assert r.status_code == 200
        dataset_A_uuid = self.get_content(r)
        assert isinstance(dataset_A_uuid, str)
        # create a dataset for group B
        dataset_B = {"name": faker.pystr(), "description": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset_B,
        )
        assert r.status_code == 200
        dataset_B_uuid = self.get_content(r)
        assert isinstance(dataset_B_uuid, str)

        # init an upload in dataset A
        fake_filename = f"{faker.pystr()}_R1"
        fake_file = {
            "name": f"{fake_filename}.fastq.gz",
            "mimeType": "application/gzip",
            "size": faker.pyint(),
            "lastModified": faker.pyint(),
        }
        r = client.post(
            f"{API_URI}/dataset/{dataset_A_uuid}/files/upload",
            headers=user_A1_headers,
            data=fake_file,
        )
        assert r.status_code == 201
        # init an upload in dataset B

        r = client.post(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload",
            headers=user_B1_headers,
            data=fake_file,
        )
        assert r.status_code == 201

        # test without group filter
        # public stats
        NIGEndpoint.GROUPS_TO_FILTER = []
        r = client.get(f"{API_URI}/stats/public", )
        assert r.status_code == 200
        full_public_stats = self.get_content(r)
        assert isinstance(full_public_stats, dict)
        assert full_public_stats["num_users"] > 0
        assert full_public_stats["num_studies"] > 0
        assert full_public_stats["num_datasets"] > 0
        assert full_public_stats["num_files"] > 0

        # private stats
        r = client.get(
            f"{API_URI}/stats/private",
            headers=user_B1_headers,
        )
        private_stats = self.get_content(r)
        assert isinstance(private_stats, dict)
        assert private_stats["num_users"] > 0
        assert private_stats["num_studies"] > 0
        assert private_stats["num_datasets"] > 0
        assert private_stats["num_files"] > 0

        # exclude test group
        NIGEndpoint.GROUPS_TO_FILTER = ["Default group"]
        # test public stats
        r = client.get(f"{API_URI}/stats/public", )
        assert r.status_code == 200
        public_stats = self.get_content(r)
        assert isinstance(public_stats, dict)
        assert public_stats["num_users"] == 3
        assert public_stats["num_studies"] == 2
        assert public_stats["num_datasets"] == 2
        assert public_stats["num_files"] == 2

        # test authentication for private stats
        r = client.get(f"{API_URI}/stats/private", )
        assert r.status_code == 401

        # test private stats
        r = client.get(
            f"{API_URI}/stats/private",
            headers=user_B1_headers,
        )
        private_stats = self.get_content(r)
        assert isinstance(private_stats, dict)
        assert private_stats["num_users"] == 3
        assert private_stats["num_studies"] == 2
        assert private_stats["num_datasets"] == 2
        assert private_stats["num_files"] == 2
        # get group fullnames
        graph = neo4j.get_instance()
        group_A = graph.Group.nodes.get_or_none(uuid=uuid_group_A)
        group_A_fullname = group_A.fullname
        group_B = graph.Group.nodes.get_or_none(uuid=uuid_group_B)
        group_B_fullname = group_B.fullname
        assert private_stats["num_datasets_per_group"][group_A_fullname] == 1
        assert private_stats["num_datasets_per_group"][group_B_fullname] == 1
        # check the excluded group not in responses
        assert "Default group" not in private_stats["num_datasets_per_group"]

        # test empty stats
        NIGEndpoint.GROUPS_TO_FILTER.append(group_A_fullname)
        NIGEndpoint.GROUPS_TO_FILTER.append(group_B_fullname)
        # public
        r = client.get(f"{API_URI}/stats/public", )
        assert r.status_code == 200
        public_stats = self.get_content(r)
        assert isinstance(public_stats, dict)
        assert public_stats["num_users"] == 0
        assert public_stats["num_studies"] == 0
        assert public_stats["num_datasets"] == 0
        assert public_stats["num_files"] == 0
        # private
        r = client.get(
            f"{API_URI}/stats/private",
            headers=user_B1_headers,
        )
        private_stats = self.get_content(r)
        assert isinstance(private_stats, dict)
        assert private_stats["num_users"] == 0
        assert private_stats["num_studies"] == 0
        assert private_stats["num_datasets"] == 0
        assert private_stats["num_files"] == 0
        assert group_A_fullname not in private_stats["num_datasets_per_group"]
        assert group_B_fullname not in private_stats["num_datasets_per_group"]

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
            study1_uuid=study1_uuid,
            study2_uuid=study2_uuid,
        )
예제 #2
0
    def test_api_phenotype(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=True)

        # create a new phenotype with wrong age
        phenotype1 = {
            "name": faker.pystr(),
            "age": -2,
            "sex": "male",
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data=phenotype1,
        )
        assert r.status_code == 400

        # create a new phenotype
        phenotype1["age"] = faker.pyint(0, 100)
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data=phenotype1,
        )
        assert r.status_code == 200
        phenotype1_uuid = self.get_content(r)
        assert isinstance(phenotype1_uuid, str)

        # create a new phenotype in a study of an other group
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/phenotypes",
            headers=user_B1_headers,
            data=phenotype1,
        )
        assert r.status_code == 404

        # create a new phenotype as admin not belonging to study group
        phenotype2 = {
            "name": faker.pystr(),
            "age": faker.pyint(0, 100),
            "sex": "female",
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=admin_headers,
            data=phenotype2,
        )
        assert r.status_code == 404

        # create a phenotype with a geodata and a list of hpo
        graph = neo4j.get_instance()
        geodata_nodes = graph.GeoData.nodes
        geodata_uuid = geodata_nodes[0].uuid
        hpo_nodes = graph.HPO.nodes
        hpo1_id = hpo_nodes[0].hpo_id
        hpo2_id = hpo_nodes[1].hpo_id
        phenotype2["birth_place"] = geodata_uuid
        phenotype2["hpo"] = [hpo1_id, hpo2_id]
        phenotype2["hpo"] = json.dumps(phenotype2["hpo"])

        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data=phenotype2,
        )
        assert r.status_code == 200
        phenotype2_uuid = self.get_content(r)
        assert isinstance(phenotype2_uuid, str)

        # test phenotype access
        # test phenotype list response
        r = client.get(f"{API_URI}/study/{study1_uuid}/phenotypes",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test phenotype list response for a study you don't have access
        r = client.get(f"{API_URI}/study/{study2_uuid}/phenotypes",
                       headers=user_B1_headers)
        assert r.status_code == 404

        # test phenotype list response for admin
        r = client.get(f"{API_URI}/study/{study1_uuid}/phenotypes",
                       headers=admin_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test empty list of phenotypes in a study
        r = client.get(f"{API_URI}/study/{study2_uuid}/phenotypes",
                       headers=user_A1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert not response

        # study owner
        r = client.get(f"{API_URI}/phenotype/{phenotype1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        # same group of the study owner
        r = client.get(f"{API_URI}/phenotype/{phenotype2_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        # check hpo and geodata were correctly linked
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert response["birth_place"]["uuid"] == geodata_uuid
        assert len(response["hpo"]) == 2
        hpo_list = []
        for el in response["hpo"]:
            hpo_list.append(el["hpo_id"])
        assert hpo1_id in hpo_list
        assert hpo2_id in hpo_list

        # phenotype owned by an other group
        r = client.get(f"{API_URI}/phenotype/{phenotype1_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        not_authorized_message = self.get_content(r)
        assert isinstance(not_authorized_message, str)

        # admin access
        r = client.get(f"{API_URI}/phenotype/{phenotype1_uuid}",
                       headers=admin_headers)
        assert r.status_code == 200

        # test phenotype modification

        # modify a non existent phenotype
        random_phenotype = faker.pystr()
        r = client.put(
            f"{API_URI}/phenotype/{random_phenotype}",
            headers=user_A1_headers,
            data={
                "name": faker.pystr(),
                "sex": "female"
            },
        )
        assert r.status_code == 404
        # modify a phenotype you do not own
        r = client.put(
            f"{API_URI}/phenotype/{phenotype1_uuid}",
            headers=user_A1_headers,
            data={
                "name": faker.pystr(),
                "sex": "female"
            },
        )
        assert r.status_code == 404

        # modify a phenotype using a wrong age
        phenotype1["age"] = -8
        r = client.put(
            f"{API_URI}/phenotype/{phenotype1_uuid}",
            headers=user_B1_headers,
            data=phenotype1,
        )
        assert r.status_code == 400

        # modify a phenotype you own
        phenotype1["age"] = faker.pyint(0, 100)
        r = client.put(
            f"{API_URI}/phenotype/{phenotype1_uuid}",
            headers=user_B1_headers,
            data=phenotype1,
        )
        assert r.status_code == 204

        # admin modify a phenotype of a group he don't belongs
        r = client.put(
            f"{API_URI}/phenotype/{phenotype1_uuid}",
            headers=admin_headers,
            data={
                "name": faker.pystr(),
                "sex": "female"
            },
        )
        assert r.status_code == 404

        # add a new hpo and change the previous geodata
        hpo3_id = hpo_nodes[2].hpo_id
        geodata2_uuid = geodata_nodes[1].uuid
        phenotype2["name"] = faker.pystr()
        phenotype2["sex"] = "male"
        phenotype2["birth_place"] = geodata2_uuid
        phenotype2["hpo"] = [hpo1_id, hpo2_id, hpo3_id]
        phenotype2["hpo"] = json.dumps(phenotype2["hpo"])
        r = client.put(
            f"{API_URI}/phenotype/{phenotype2_uuid}",
            headers=user_B1_headers,
            data=phenotype2,
        )
        assert r.status_code == 204
        r = client.get(f"{API_URI}/phenotype/{phenotype2_uuid}",
                       headers=user_B2_headers)
        res = self.get_content(r)
        assert isinstance(res, dict)
        assert res["birth_place"]["uuid"] == geodata2_uuid
        assert len(res["hpo"]) == 3

        # delete all hpo and geodata
        data: Dict[str, Any] = {**phenotype2}
        data.pop("birth_place", None)
        data.pop("hpo", None)
        r = client.put(f"{API_URI}/phenotype/{phenotype2_uuid}",
                       headers=user_B1_headers,
                       data=data)
        assert r.status_code == 204
        r = client.get(f"{API_URI}/phenotype/{phenotype2_uuid}",
                       headers=user_B2_headers)
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "birth_place" not in response
        assert not response["hpo"]

        # add a no existing geodata
        phenotype2["birth_place"] = faker.pystr()
        r = client.put(
            f"{API_URI}/phenotype/{phenotype2_uuid}",
            headers=user_B1_headers,
            data=phenotype2,
        )
        assert r.status_code == 400

        # delete a phenotype
        # delete a phenotype that does not exists
        r = client.delete(f"{API_URI}/phenotype/{random_phenotype}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # delete a phenotype in a study you do not own
        r = client.delete(f"{API_URI}/phenotype/{phenotype1_uuid}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # admin delete a phenotype of a group he don't belong
        r = client.delete(f"{API_URI}/phenotype/{phenotype1_uuid}",
                          headers=admin_headers)
        assert r.status_code == 404
        # delete a phenotype in a study you own
        r = client.delete(f"{API_URI}/phenotype/{phenotype1_uuid}",
                          headers=user_B1_headers)
        assert r.status_code == 204
        # delete a phenotype in a study own by your group
        r = client.delete(f"{API_URI}/phenotype/{phenotype2_uuid}",
                          headers=user_B2_headers)
        assert r.status_code == 204
        # check phenotype deletion
        r = client.get(f"{API_URI}/phenotype/{phenotype1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 404
        not_existent_message = self.get_content(r)
        assert isinstance(not_existent_message, str)
        assert not_existent_message == not_authorized_message

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
            study1_uuid=study1_uuid,
            study2_uuid=study2_uuid,
        )
예제 #3
0
    def test_api_family(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=True)

        # create new phenotypes
        phenotype_father = {
            "name": faker.pystr(),
            "age": faker.pyint(0, 100),
            "sex": "male",
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data=phenotype_father,
        )
        assert r.status_code == 200
        phenotype_father_uuid = self.get_content(r)
        assert isinstance(phenotype_father_uuid, str)

        phenotype_mother = {
            "name": faker.pystr(),
            "age": faker.pyint(0, 100),
            "sex": "female",
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data=phenotype_mother,
        )
        assert r.status_code == 200
        phenotype_mother_uuid = self.get_content(r)
        assert isinstance(phenotype_mother_uuid, str)

        phenotype_son_B = {
            "name": faker.pystr(),
            "age": faker.pyint(0, 100),
            "sex": "female",
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data=phenotype_son_B,
        )
        assert r.status_code == 200
        phenotype_son_B_uuid = self.get_content(r)
        assert isinstance(phenotype_son_B_uuid, str)

        phenotype_son_A = {
            "name": faker.pystr(),
            "age": faker.pyint(0, 100),
            "sex": "female",
        }
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/phenotypes",
            headers=user_A1_headers,
            data=phenotype_son_A,
        )
        assert r.status_code == 200
        phenotype_son_A_uuid = self.get_content(r)
        assert isinstance(phenotype_son_A_uuid, str)

        # create a relationship
        # father case
        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 200
        graph = neo4j.get_instance()
        phenotype_father_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_father_uuid)
        phenotype_son_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_son_B_uuid)
        assert phenotype_father_node.son.is_connected(phenotype_son_node)
        assert phenotype_son_node.father.is_connected(phenotype_father_node)

        # test relationships in get phenotype list response
        r = client.get(f"{API_URI}/study/{study1_uuid}/phenotypes",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        for el in response:
            if el["uuid"] == phenotype_son_B_uuid:
                assert el["relationships"]["father"][
                    "uuid"] == phenotype_father_uuid
            if el["uuid"] == phenotype_father_uuid:
                assert el["relationships"]["sons"][0][
                    "uuid"] == phenotype_son_B_uuid

        # test relationships in get single phenotype response
        r = client.get(f"{API_URI}/phenotype/{phenotype_son_B_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert response["relationships"]["father"][
            "uuid"] == phenotype_father_uuid

        r = client.get(f"{API_URI}/phenotype/{phenotype_father_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert response["relationships"]["sons"][0][
            "uuid"] == phenotype_son_B_uuid

        # create a relationship for two phenotypes in an other study
        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_mother_uuid}",
            headers=user_A1_headers,
        )
        assert r.status_code == 404

        # admin creates a relationship
        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_mother_uuid}",
            headers=admin_headers,
        )
        assert r.status_code == 404

        # a user of the same group of the owner create a relationship
        # mother case
        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_mother_uuid}",
            headers=user_B2_headers,
        )
        assert r.status_code == 200
        graph = neo4j.get_instance()
        phenotype_mother_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_mother_uuid)
        phenotype_son_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_son_B_uuid)
        assert phenotype_mother_node.son.is_connected(phenotype_son_node)
        assert phenotype_son_node.mother.is_connected(phenotype_mother_node)

        # test relationships in get phenotype list response
        r = client.get(f"{API_URI}/study/{study1_uuid}/phenotypes",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        for el in response:
            if el["uuid"] == phenotype_son_B_uuid:
                assert el["relationships"]["mother"][
                    "uuid"] == phenotype_mother_uuid
            if el["uuid"] == phenotype_mother_uuid:
                assert el["relationships"]["sons"][0][
                    "uuid"] == phenotype_son_B_uuid

        # test relationships in get single phenotype response
        r = client.get(f"{API_URI}/phenotype/{phenotype_son_B_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert response["relationships"]["mother"][
            "uuid"] == phenotype_mother_uuid

        # relationship between phenotype from different studies
        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_A_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 404

        # relationship with a random phenotype as son
        random_phenotype_uuid = faker.pystr()
        r = client.post(
            f"{API_URI}/phenotype/{random_phenotype_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 404

        # relationship with a random phenotype as father
        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{random_phenotype_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 404

        # relationship with itself
        r = client.post(
            f"{API_URI}/phenotype/{phenotype_father_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 400

        # delete a relationship
        # father case
        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 204
        graph = neo4j.get_instance()
        phenotype_father_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_father_uuid)
        phenotype_son_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_son_B_uuid)
        assert not phenotype_father_node.son.single()
        assert not phenotype_son_node.father.single()

        # delete a relationship for two phenotypes in an other study
        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_mother_uuid}",
            headers=user_A1_headers,
        )
        assert r.status_code == 404

        # admin delete a relationship
        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_mother_uuid}",
            headers=admin_headers,
        )
        assert r.status_code == 404

        # a user of the same group of the owner delete a relationship
        # mother case
        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_mother_uuid}",
            headers=user_B2_headers,
        )
        assert r.status_code == 204
        graph = neo4j.get_instance()
        phenotype_mother_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_mother_uuid)
        phenotype_son_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_son_B_uuid)
        assert not phenotype_mother_node.son.single()
        assert not phenotype_son_node.mother.single()

        # delete relationship between phenotype from different studies
        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_son_A_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 404

        #  delete relationship with a random phenotype as son
        r = client.delete(
            f"{API_URI}/phenotype/{random_phenotype_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 404

        # delete relationship with a random phenotype as father
        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{random_phenotype_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 404

        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_father_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 200

        r = client.post(
            f"{API_URI}/phenotype/{phenotype_son_B_uuid}/relationships/{phenotype_mother_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 200

        # delete a son relationship
        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_mother_uuid}/relationships/{phenotype_son_B_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 204
        graph = neo4j.get_instance()
        phenotype_mother_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_mother_uuid)
        phenotype_son_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_son_B_uuid)
        assert not phenotype_mother_node.son.single()
        assert not phenotype_son_node.mother.single()

        r = client.delete(
            f"{API_URI}/phenotype/{phenotype_father_uuid}/relationships/{phenotype_son_B_uuid}",
            headers=user_B1_headers,
        )
        assert r.status_code == 204
        graph = neo4j.get_instance()
        phenotype_father_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_father_uuid)
        phenotype_son_node = graph.Phenotype.nodes.get_or_none(
            uuid=phenotype_son_B_uuid)
        assert not phenotype_father_node.son.single()
        assert not phenotype_son_node.father.single()

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
            study1_uuid=study1_uuid,
            study2_uuid=study2_uuid,
        )
예제 #4
0
    def test_api_study(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=False)

        # create a new study for the group B
        random_name = faker.pystr()
        study1 = {"name": random_name, "description": faker.pystr()}
        r = client.post(f"{API_URI}/study",
                        headers=user_B1_headers,
                        data=study1)
        assert r.status_code == 200
        study1_uuid = self.get_content(r)
        assert isinstance(study1_uuid, str)

        # create a new study for the group A
        random_name2 = faker.pystr()
        study2 = {"name": random_name2, "description": faker.pystr()}
        r = client.post(f"{API_URI}/study",
                        headers=user_A1_headers,
                        data=study2)
        assert r.status_code == 200
        study2_uuid = self.get_content(r)
        assert isinstance(study2_uuid, str)

        # check the directory was created
        dir_path = INPUT_ROOT.joinpath(uuid_group_A, study2_uuid)
        assert dir_path.is_dir()

        # test study access
        # test study list response
        r = client.get(f"{API_URI}/study", headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 1

        # test admin access
        r = client.get(f"{API_URI}/study/{study1_uuid}", headers=admin_headers)
        assert r.status_code == 200

        # study owner
        r = client.get(f"{API_URI}/study/{study1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        # other component of the group
        r = client.get(f"{API_URI}/study/{study1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        # study own by an other group
        r = client.get(f"{API_URI}/study/{study1_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        not_authorized_message = self.get_content(r)
        assert isinstance(not_authorized_message, str)

        # test study modification
        # modify a study you do not own
        r = client.put(
            f"{API_URI}/study/{study1_uuid}",
            headers=user_A1_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 404
        # modify a study you own
        r = client.put(
            f"{API_URI}/study/{study1_uuid}",
            headers=user_B1_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 204

        # delete a study
        # delete a study you do not own
        r = client.delete(f"{API_URI}/study/{study1_uuid}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # delete a study you own
        # create a new dataset to test if it's deleted with the study
        dataset = {"name": faker.pystr(), "description": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/datasets",
            headers=user_A1_headers,
            data=dataset,
        )
        assert r.status_code == 200
        dataset_uuid = self.get_content(r)
        assert isinstance(dataset_uuid, str)
        dataset_path = dir_path.joinpath(dataset_uuid)
        assert dataset_path.is_dir()
        # create a new file to test if it's deleted with the study
        filename = f"{faker.pystr()}_R1"
        file_data = {
            "name": f"{filename}.fastq.gz",
            "mimeType": "application/gzip",
            "size": faker.pyint(),
            "lastModified": faker.pyint(),
        }
        r = client.post(
            f"{API_URI}/dataset/{dataset_uuid}/files/upload",
            headers=user_A1_headers,
            data=file_data,
        )
        assert r.status_code == 201
        # get the file uuid
        r = client.get(
            f"{API_URI}/dataset/{dataset_uuid}/files",
            headers=user_A1_headers,
        )
        assert r.status_code == 200
        file_list = self.get_content(r)
        assert isinstance(file_list, list)
        file_uuid = file_list[0]["uuid"]

        # create a new technical to test if it's deleted with the study
        techmeta = {"name": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/technicals",
            headers=user_A1_headers,
            data=techmeta,
        )
        assert r.status_code == 200
        techmeta_uuid = self.get_content(r)
        assert isinstance(techmeta_uuid, str)
        # create a new phenotype to test if it's deleted with the study
        phenotype = {"name": faker.pystr(), "sex": "male"}
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/phenotypes",
            headers=user_A1_headers,
            data=phenotype,
        )
        assert r.status_code == 200
        phenotype_uuid = self.get_content(r)
        assert isinstance(phenotype_uuid, str)
        # simulate the study has an output directory
        # create the output directory in the same way is created in launch pipeline task
        output_path = OUTPUT_ROOT.joinpath(
            dataset_path.relative_to(INPUT_ROOT))
        output_path.mkdir(parents=True)
        assert output_path.is_dir()

        # delete the study
        r = client.delete(f"{API_URI}/study/{study2_uuid}",
                          headers=user_A1_headers)
        assert r.status_code == 204
        assert not dir_path.is_dir()
        assert not dataset_path.is_dir()
        # check the dataset was deleted
        r = client.get(f"{API_URI}/dataset/{dataset_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        # check the file was deleted
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_A1_headers)
        assert r.status_code == 404
        # check the technical was deleted
        r = client.get(f"{API_URI}/technical/{techmeta_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        # check the phenotype was deleted
        r = client.get(f"{API_URI}/phenotype/{phenotype_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        # check the output dir was deleted
        assert not output_path.is_dir()

        # delete a study own by your group
        r = client.delete(f"{API_URI}/study/{study1_uuid}",
                          headers=user_B2_headers)
        assert r.status_code == 204
        # check study deletion
        r = client.get(f"{API_URI}/study/{study1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 404
        not_existent_message = self.get_content(r)
        assert isinstance(not_existent_message, str)
        assert not_existent_message == not_authorized_message

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
        )
    def test_api_techmeta(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=True)

        # create a new techmeta
        techmeta1 = {
            "name": faker.pystr(),
            "sequencing_date": faker.date(),
            "platform": "Other",
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/technicals",
            headers=user_B1_headers,
            data=techmeta1,
        )
        assert r.status_code == 200
        techmeta1_uuid = self.get_content(r)
        assert isinstance(techmeta1_uuid, str)

        # create a new techmeta in a study of an other group
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/technicals",
            headers=user_B1_headers,
            data=techmeta1,
        )
        assert r.status_code == 404

        # create a new technical as admin not belonging to study group
        techmeta2 = {
            "name": faker.pystr(),
            "sequencing_date": faker.date(),
            "platform": "Other",
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/technicals",
            headers=admin_headers,
            data=techmeta2,
        )
        assert r.status_code == 404

        r = client.post(
            f"{API_URI}/study/{study1_uuid}/technicals",
            headers=user_B1_headers,
            data=techmeta2,
        )
        assert r.status_code == 200
        techmeta2_uuid = self.get_content(r)
        assert isinstance(techmeta2_uuid, str)

        # test technical access
        # test technical list response
        r = client.get(f"{API_URI}/study/{study1_uuid}/technicals",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test technical list response for a study you don't have access
        r = client.get(f"{API_URI}/study/{study2_uuid}/technicals",
                       headers=user_B1_headers)
        assert r.status_code == 404

        # test technical list response for admin
        r = client.get(f"{API_URI}/study/{study1_uuid}/technicals",
                       headers=admin_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test empty list of technicals in a study
        r = client.get(f"{API_URI}/study/{study2_uuid}/technicals",
                       headers=user_A1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert not response

        # study owner
        r = client.get(f"{API_URI}/technical/{techmeta1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        # same group of the study owner
        r = client.get(f"{API_URI}/technical/{techmeta1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        # technical owned by an other group
        r = client.get(f"{API_URI}/technical/{techmeta1_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        not_authorized_message = self.get_content(r)
        assert isinstance(not_authorized_message, str)

        # admin access
        r = client.get(f"{API_URI}/technical/{techmeta1_uuid}",
                       headers=admin_headers)
        assert r.status_code == 200

        # test technical modification

        # modify a non existent technical
        random_technical = faker.pystr()
        r = client.put(
            f"{API_URI}/technical/{random_technical}",
            headers=user_A1_headers,
            data={"name": faker.pystr()},
        )
        assert r.status_code == 404
        # modify a technical you do not own
        r = client.put(
            f"{API_URI}/technical/{techmeta1_uuid}",
            headers=user_A1_headers,
            data={"name": faker.pystr()},
        )
        assert r.status_code == 404
        # modify a technical you own
        r = client.put(
            f"{API_URI}/technical/{techmeta1_uuid}",
            headers=user_B1_headers,
            data={
                "name": faker.pystr(),
                "sequencing_date": faker.date()
            },
        )
        assert r.status_code == 204

        # admin modify a technical of a group he don't belongs
        r = client.put(
            f"{API_URI}/technical/{techmeta1_uuid}",
            headers=admin_headers,
            data={"name": faker.pystr()},
        )
        assert r.status_code == 404

        # delete a technical
        # delete a technical that does not exists
        r = client.delete(f"{API_URI}/technical/{random_technical}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # delete a technical in a study you do not own
        r = client.delete(f"{API_URI}/technical/{techmeta1_uuid}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # admin delete a technical of a group he don't belong
        r = client.delete(f"{API_URI}/technical/{techmeta1_uuid}",
                          headers=admin_headers)
        assert r.status_code == 404
        # delete a technical in a study you own
        r = client.delete(f"{API_URI}/technical/{techmeta1_uuid}",
                          headers=user_B1_headers)
        assert r.status_code == 204
        # delete a technical in a study own by your group
        r = client.delete(f"{API_URI}/technical/{techmeta2_uuid}",
                          headers=user_B2_headers)
        assert r.status_code == 204
        # check technical deletion
        r = client.get(f"{API_URI}/technical/{techmeta1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 404
        not_existent_message = self.get_content(r)
        assert isinstance(not_existent_message, str)
        assert not_existent_message == not_authorized_message

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
            study1_uuid=study1_uuid,
            study2_uuid=study2_uuid,
        )
예제 #6
0
    def test_api_file(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=True)
        # create a new dataset
        dataset_B = {"name": faker.pystr(), "description": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset_B,
        )
        assert r.status_code == 200
        dataset_B_uuid = self.get_content(r)
        assert isinstance(dataset_B_uuid, str)

        # check accesses for post request
        # upload a new file in a dataset of an other group
        fake_file = {
            "name": f"{faker.pystr()}_R1.fastq.gz",
            "mimeType": "application/gzip",
            "size": faker.pyint(),
            "lastModified": faker.pyint(),
        }
        r = client.post(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload",
            headers=user_A1_headers,
            data=fake_file,
        )
        assert r.status_code == 404

        # upload a new file as admin not belonging to study group
        r = client.post(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload",
            headers=admin_headers,
            data=fake_file,
        )
        assert r.status_code == 404

        # try to upload a file with a non allowed format
        fake_format = {
            "name": f"{faker.pystr()}_R1.txt",
            "mimeType": "text/plain",
            "size": faker.pyint(),
            "lastModified": faker.pyint(),
        }
        r = client.post(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload",
            headers=user_B1_headers,
            data=fake_format,
        )
        assert r.status_code == 400

        # try to upload a file with a wrong nomenclature
        fake_nomencl_file = {
            "name":
            f"{faker.pystr()}.{faker.pystr()}_R1.fastq.gz.{faker.pystr()}",
            "mimeType": "text/plain",
            "size": faker.pyint(),
            "lastModified": faker.pyint(),
        }
        r = client.post(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload",
            headers=user_B1_headers,
            data=fake_nomencl_file,
        )
        assert r.status_code == 400

        fake_nomencl_file2 = {
            "name": f"{faker.pystr()}.{faker.pystr()}_R1.fastq.gz",
            "mimeType": "text/plain",
            "size": faker.pyint(),
            "lastModified": faker.pyint(),
        }
        r = client.post(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload",
            headers=user_B1_headers,
            data=fake_nomencl_file2,
        )
        assert r.status_code == 400

        valid_fcontent = f"@SEQ_ID\n{faker.pystr(max_chars=12)}\n+{faker.pystr()}\n{faker.pystr(max_chars=12)}"
        # invalid header, @ is missing
        invalid_header = f"SEQ_ID\n{faker.pystr(max_chars=12)}\n+{faker.pystr()}\n{faker.pystr(max_chars=12)}"
        # CASE invalid separator
        invalid_separator = f"@SEQ_ID\n{faker.pystr(max_chars=12)}\n{faker.pystr()}\n{faker.pystr(max_chars=12)}"
        # len of sequence != len of quality
        invalid_sequence = f"@SEQ_ID\n{faker.pystr(max_chars=12)}\n+{faker.pystr()}\n{faker.pystr(max_chars=8)}"
        # invalid second header
        invalid_header2 = f"@SEQ_ID\n{faker.pystr(max_chars=12)}\n+{faker.pystr()}\n{faker.pystr(max_chars=12)}\n{faker.pystr()}"

        # create a file to upload
        fastq = self.create_fastq_gz(faker, valid_fcontent)

        # upload a file
        response = self.upload_file(client,
                                    user_B1_headers,
                                    fastq,
                                    dataset_B_uuid,
                                    stream=True)
        assert response.status_code == 200
        # check the file exists and have the expected size
        filename = fastq.name
        filesize = fastq.stat().st_size
        filepath = INPUT_ROOT.joinpath(uuid_group_B, study1_uuid,
                                       dataset_B_uuid, filename)
        assert filepath.is_file()
        assert filepath.stat().st_size == filesize

        # upload the same file twice
        response = self.upload_file(client,
                                    user_B2_headers,
                                    fastq,
                                    dataset_B_uuid,
                                    stream=True)
        assert response.status_code == 409

        # upload the same file in a different dataset
        # create a new dataset
        dataset_B2 = {"name": faker.pystr(), "description": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset_B2,
        )
        assert r.status_code == 200
        dataset_B2_uuid = self.get_content(r)
        assert isinstance(dataset_B2_uuid, str)
        response = self.upload_file(client,
                                    user_B2_headers,
                                    fastq,
                                    dataset_B2_uuid,
                                    stream=True)
        assert response.status_code == 200

        # check error if final file size is different from the expected
        # rename the file to upload
        fastq2 = fastq.parent.joinpath(f"{faker.pystr()}_R1.fastq.gz")
        fastq.rename(fastq2)
        # upload without streaming
        response = self.upload_file(
            client,
            user_B1_headers,
            fastq2,
            dataset_B_uuid,
            stream=False,
        )
        assert response.status_code == 500
        error_message = self.get_content(response)
        assert isinstance(error_message, str)
        assert (
            error_message ==
            "File has not been uploaded correctly: final size does not correspond to total size. Please try a new upload"
        )
        # check uncomplete file has been removed
        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            fastq2.name,
        )
        assert not check_filepath.is_file()

        # check file validation
        # upload an empty file
        empty_file = self.create_fastq_gz(faker, "")
        response = self.upload_file(
            client,
            user_B1_headers,
            empty_file,
            dataset_B_uuid,
            stream=True,
        )
        assert response.status_code == 400
        # check the empty file has been removed
        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            empty_file.name,
        )
        assert not check_filepath.is_file()
        empty_file.unlink()

        # upload a file with not valid content
        # CASE wrong gzip file
        wrong_gzip = Path(tempfile.gettempdir(),
                          f"{faker.pystr()}_R1.fastq.gz")

        # Directly write the gz => it is an ascii file and not a valid gz
        with open(wrong_gzip, "w") as f:
            f.write(valid_fcontent)

        response = self.upload_file(
            client,
            user_B1_headers,
            wrong_gzip,
            dataset_B_uuid,
            stream=True,
        )
        assert response.status_code == 400
        error_message = self.get_content(response)
        assert isinstance(error_message, str)
        assert "gzipped" in error_message

        # check the empty file has been removed
        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            wrong_gzip.name,
        )
        assert not check_filepath.is_file()
        wrong_gzip.unlink()

        # CASE binary file instead of a text file
        binary_file = self.create_fastq_gz(faker, faker.binary(), mode="wb")
        response = self.upload_file(
            client,
            user_B1_headers,
            binary_file,
            dataset_B_uuid,
            stream=True,
        )
        assert response.status_code == 400
        error_message = self.get_content(response)
        assert isinstance(error_message, str)
        assert "binary" in error_message

        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            binary_file.name,
        )
        assert not check_filepath.is_file()
        binary_file.unlink()

        # CASE invalid header
        invalid_fastq = self.create_fastq_gz(faker, invalid_header)
        response = self.upload_file(
            client,
            user_B1_headers,
            invalid_fastq,
            dataset_B_uuid,
            stream=True,
        )
        assert response.status_code == 400
        error_message = self.get_content(response)
        assert isinstance(error_message, str)
        assert "header" in error_message

        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            invalid_fastq.name,
        )
        assert not check_filepath.is_file()
        invalid_fastq.unlink()

        # CASE invalid separator
        invalid_fastq = self.create_fastq_gz(faker, invalid_separator)
        response = self.upload_file(
            client,
            user_B1_headers,
            invalid_fastq,
            dataset_B_uuid,
            stream=True,
        )
        assert response.status_code == 400
        error_message = self.get_content(response)
        assert isinstance(error_message, str)
        assert "separator" in error_message

        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            invalid_fastq.name,
        )
        assert not check_filepath.is_file()
        invalid_fastq.unlink()

        # CASE invalid sequence line
        invalid_fastq = self.create_fastq_gz(faker, invalid_sequence)
        response = self.upload_file(
            client,
            user_B1_headers,
            invalid_fastq,
            dataset_B_uuid,
            stream=True,
        )
        assert response.status_code == 400
        error_message = self.get_content(response)
        assert isinstance(error_message, str)
        assert "lines lengths differ" in error_message

        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            invalid_fastq.name,
        )
        assert not check_filepath.is_file()
        invalid_fastq.unlink()

        # CASE invalid header for the second read
        invalid_fastq = self.create_fastq_gz(faker, invalid_header2)
        response = self.upload_file(
            client,
            user_B1_headers,
            invalid_fastq,
            dataset_B_uuid,
            stream=True,
        )
        assert response.status_code == 400
        error_message = self.get_content(response)
        assert isinstance(error_message, str)
        assert "header" in error_message

        check_filepath = INPUT_ROOT.joinpath(
            uuid_group_B,
            study1_uuid,
            dataset_B_uuid,
            invalid_fastq.name,
        )
        assert not check_filepath.is_file()
        invalid_fastq.unlink()

        # check accesses on put endpoint
        # put on a file in a dataset of an other group
        r = client.put(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload/{filename}",
            headers=user_A1_headers,
        )
        assert r.status_code == 404
        # put a file as admin not belonging to study group
        r = client.put(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload/{filename}",
            headers=admin_headers,
        )
        assert r.status_code == 404

        # put of a ton existent file
        r = client.put(
            f"{API_URI}/dataset/{dataset_B_uuid}/files/upload/{fastq2}.txt.gz",
            headers=user_B1_headers,
        )
        assert r.status_code == 404

        # test file access
        # test file list response
        r = client.get(f"{API_URI}/dataset/{dataset_B_uuid}/files",
                       headers=user_B1_headers)
        assert r.status_code == 200
        file_list = self.get_content(r)
        assert isinstance(file_list, list)
        assert len(file_list) == 1
        file_uuid = file_list[0]["uuid"]

        # test file list response for a dataset you don't have access
        r = client.get(f"{API_URI}/dataset/{dataset_B_uuid}/files",
                       headers=user_A1_headers)
        assert r.status_code == 404

        # test file list response for admin
        r = client.get(f"{API_URI}/dataset/{dataset_B_uuid}/files",
                       headers=admin_headers)
        assert r.status_code == 200
        file_list = self.get_content(r)
        assert isinstance(file_list, list)
        assert len(file_list) == 1

        # check use case of file not in the folder
        # rename the file in the folder as it will not be found
        temporary_filepath = filepath.with_suffix(".fastq.tmp")
        filepath.rename(temporary_filepath)
        r = client.get(f"{API_URI}/dataset/{dataset_B_uuid}/files",
                       headers=user_B1_headers)
        assert r.status_code == 200
        file_list = self.get_content(r)
        assert isinstance(file_list, list)
        assert file_list[0]["status"] == "unknown"
        # create an empty file with the original name
        # test status from unknown to importing
        filepath.touch()

        r = client.get(f"{API_URI}/dataset/{dataset_B_uuid}/files",
                       headers=user_B1_headers)
        assert r.status_code == 200
        file_list = self.get_content(r)
        assert isinstance(file_list, list)
        assert file_list[0]["status"] == "importing"

        # restore the original file
        filepath.unlink()
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B1_headers)
        assert r.status_code == 200
        file_response = self.get_content(r)
        assert isinstance(file_response, dict)
        assert file_response["status"] == "unknown"
        temporary_filepath.rename(filepath)

        r = client.get(f"{API_URI}/dataset/{dataset_B_uuid}/files",
                       headers=user_B1_headers)
        assert r.status_code == 200
        file_list = self.get_content(r)
        assert isinstance(file_list, list)
        assert file_list[0]["status"] == "uploaded"

        # dataset owner
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B1_headers)
        assert r.status_code == 200
        # same group of the dataset owner
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B2_headers)
        assert r.status_code == 200
        # file owned by an other group
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_A1_headers)
        assert r.status_code == 404
        not_authorized_message = self.get_content(r)
        assert isinstance(not_authorized_message, str)

        # admin access
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=admin_headers)
        assert r.status_code == 200

        # check use case of file not in the folder
        # rename the file in the folder as it will not be found
        filepath.rename(temporary_filepath)
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B1_headers)
        assert r.status_code == 200

        # create an empty file with the original name
        # test status from unknown to importing
        filepath.touch()
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B1_headers)
        assert r.status_code == 200
        file_res = self.get_content(r)
        assert isinstance(file_res, dict)
        assert file_res["status"] == "importing"

        # restore the original file
        filepath.unlink()
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B1_headers)
        assert r.status_code == 200

        temporary_filepath.rename(filepath)

        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B1_headers)
        assert r.status_code == 200
        file_res = self.get_content(r)
        assert isinstance(file_res, dict)
        assert file_res["status"] == "uploaded"

        # delete a file
        # delete a file that does not exists
        fake_filename = f"{faker.pystr()}_R1"
        r = client.delete(f"{API_URI}/file/{fake_filename}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # delete a file in a dataset you do not own
        r = client.delete(f"{API_URI}/file/{file_uuid}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # admin delete a file of a dataset he don't belong
        r = client.delete(f"{API_URI}/file/{file_uuid}", headers=admin_headers)
        assert r.status_code == 404
        # delete a file in a dataset you own
        r = client.delete(f"{API_URI}/file/{file_uuid}",
                          headers=user_B1_headers)
        assert r.status_code == 204
        # delete a file in a dataset own by your group
        r = client.get(f"{API_URI}/dataset/{dataset_B2_uuid}/files",
                       headers=user_B2_headers)
        file_list = self.get_content(r)
        assert isinstance(file_list, list)
        file2_uuid = file_list[0]["uuid"]

        r = client.delete(f"{API_URI}/file/{file2_uuid}",
                          headers=user_B2_headers)
        assert r.status_code == 204
        # check file deletion
        r = client.get(f"{API_URI}/file/{file_uuid}", headers=user_B1_headers)
        assert r.status_code == 404
        not_existent_message = self.get_content(r)
        assert isinstance(not_existent_message, str)
        assert not_existent_message == not_authorized_message
        # check physical deletion from the folder
        assert not filepath.is_file()

        if fastq.exists():
            fastq.unlink()

        if fastq2.exists():
            fastq2.unlink()

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
            study1_uuid=study1_uuid,
            study2_uuid=study2_uuid,
        )
    def test_api_dataset(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=True)

        # create a new dataset
        dataset1 = {"name": faker.pystr(), "description": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset1,
        )
        assert r.status_code == 200
        dataset1_uuid = self.get_content(r)
        assert isinstance(dataset1_uuid, str)
        # check the directory exists
        dir_path = INPUT_ROOT.joinpath(uuid_group_B, study1_uuid,
                                       dataset1_uuid)
        assert dir_path.is_dir()

        # create a new dataset in a study of an other group
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset1,
        )
        assert r.status_code == 404

        # create a technical
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/technicals",
            headers=user_B1_headers,
            data={"name": faker.pystr()},
        )
        assert r.status_code == 200
        technical_uuid = self.get_content(r)
        assert isinstance(technical_uuid, str)
        # create a phenotype
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data={
                "name": faker.pystr(),
                "sex": "male"
            },
        )
        assert r.status_code == 200
        phenotype_uuid = self.get_content(r)
        assert isinstance(phenotype_uuid, str)

        # create a new dataset as admin not belonging to study group
        dataset2 = {
            "name": faker.pystr(),
            "description": faker.pystr(),
            "phenotype": phenotype_uuid,
            "technical": technical_uuid,
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=admin_headers,
            data=dataset2,
        )
        assert r.status_code == 404

        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset2,
        )
        assert r.status_code == 200
        dataset2_uuid = self.get_content(r)
        assert isinstance(dataset2_uuid, str)

        # test dataset access
        # test dataset list response
        r = client.get(f"{API_URI}/study/{study1_uuid}/datasets",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test dataset list response for a study you don't have access
        r = client.get(f"{API_URI}/study/{study2_uuid}/datasets",
                       headers=user_B1_headers)
        assert r.status_code == 404

        # test dataset list response for admin
        r = client.get(f"{API_URI}/study/{study1_uuid}/datasets",
                       headers=admin_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test empty list of datasets in a study
        r = client.get(f"{API_URI}/study/{study2_uuid}/datasets",
                       headers=user_A1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert not response

        # dataset owner
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        # same group of the owner
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        # dataset owned by an other group
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        not_authorized_message = self.get_content(r)
        assert isinstance(not_authorized_message, str)

        # admin access
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=admin_headers)
        assert r.status_code == 200

        # test technical and phenoype assignation when a new dataset is created
        r = client.get(f"{API_URI}/dataset/{dataset2_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "technical" in response
        assert "phenotype" in response
        assert response["technical"]["uuid"] == technical_uuid
        # check phenotype was correctly assigned
        assert response["phenotype"]["uuid"] == phenotype_uuid

        # test dataset changing status
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"status": "UPLOAD COMPLETED"},
        )
        assert r.status_code == 204
        # check new status in get response
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "status" in response

        # delete status
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"status": "-1"},
        )
        assert r.status_code == 204
        # check status has been removed
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert not response["status"]

        # try to modify a status when the dataset is running
        graph = neo4j.get_instance()
        dataset = graph.Dataset.nodes.get_or_none(uuid=dataset1_uuid)
        dataset.status = "RUNNING"
        dataset.save()
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"status": "UPLOAD COMPLETED"},
        )
        assert r.status_code == 400

        # admin tries to modify a status when the dataset is running
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=admin_headers,
            data={"status": "UPLOAD COMPLETED"},
        )
        assert r.status_code == 204

        # test dataset modification

        # modify a dataset you do not own
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_A1_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 404
        # modify a dataset you own
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 204
        # modify a dataset of your group assigning a technical and a phenotype
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B2_headers,
            data={
                "name": faker.pystr(),
                "technical": technical_uuid,
                "phenotype": phenotype_uuid,
            },
        )
        assert r.status_code == 204
        # check technical was correctly assigned
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "technical" in response
        assert "phenotype" in response
        assert response["technical"]["uuid"] == technical_uuid
        # check phenotype was correctly assigned
        assert response["phenotype"]["uuid"] == phenotype_uuid

        # modify a dataset of your group removing a technical and a phenotype
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B2_headers,
            data={
                "technical": "-1",
                "phenotype": "-1"
            },
        )
        assert r.status_code == 204
        # check technical was correctly removed
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert response["technical"] is None
        # check phenotype was correctly removed
        assert response["phenotype"] is None

        # admin modify a dataset of a group he don't belongs
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=admin_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 404
        # simulate the dataset has an output directory
        # create the output directory in the same way is created in launch pipeline task
        output_path = OUTPUT_ROOT.joinpath(dir_path.relative_to(INPUT_ROOT))
        output_path.mkdir(parents=True)
        assert output_path.is_dir()

        # delete a dataset
        # delete a dataset you do not own
        r = client.delete(f"{API_URI}/dataset/{dataset1_uuid}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # admin delete a dataset of a group he don't belong
        r = client.delete(f"{API_URI}/dataset/{dataset1_uuid}",
                          headers=admin_headers)
        assert r.status_code == 404
        # delete a dataset you own
        r = client.delete(f"{API_URI}/dataset/{dataset1_uuid}",
                          headers=user_B1_headers)
        assert r.status_code == 204
        assert not dir_path.is_dir()
        # delete a study own by your group
        r = client.delete(f"{API_URI}/dataset/{dataset2_uuid}",
                          headers=user_B2_headers)
        assert r.status_code == 204
        # check dataset deletion
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 404
        not_existent_message = self.get_content(r)
        assert isinstance(not_existent_message, str)
        assert not_existent_message == not_authorized_message
        # check directory deletion
        assert not dir_path.is_dir()
        assert not output_path.is_dir()

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
            study1_uuid=study1_uuid,
            study2_uuid=study2_uuid,
        )