<gh_stars>1-10
import pytest
import json
import uuid
from graphql_relay import to_global_id, from_global_id
from graphql import GraphQLError

from creator.studies.factories import StudyFactory
from creator.organizations.factories import OrganizationFactory
from creator.data_templates.models import TemplateVersion
from creator.data_templates.factories import (
    DataTemplateFactory,
    TemplateVersionFactory,
)
from creator.data_templates.mutations.template_version import check_studies

FIELDS = {"fields": [
    {
        "key": "person.id",
        "label": "Person ID",
        "description": "Person identifiers",
        "required": True,
        "data_type": "string",
        "instructions": f"Populate person id properly",
    }
]}

CREATE_TEMPLATE_VERSION = """
mutation ($input: CreateTemplateVersionInput!) {
    createTemplateVersion(input: $input) {
        templateVersion {
            id
            description
            fieldDefinitions
            dataTemplate { id }
            studies { edges { node { id kfId } } }
        }
    }
}
"""

UPDATE_TEMPLATE_VERSION = """
mutation ($id: ID!, $input: UpdateTemplateVersionInput!) {
    updateTemplateVersion(id: $id, input: $input) {
        templateVersion {
            id
            description
            fieldDefinitions
            dataTemplate { id }
            studies { edges { node { id kfId } } }
        }
    }
}
"""

DELETE_TEMPLATE_VERSION = """
  mutation DeleteTemplateVersion($id: ID!) {
    deleteTemplateVersion(id: $id) {
      id
      success
    }
  }
"""


@pytest.mark.parametrize(
    "permissions,allowed",
    [
        (["add_datatemplate", "change_study"], True),
        (["add_datatemplate"], False),
        ([], False),
    ],
)
def test_create_template_version(db, permission_client, permissions, allowed):
    """
    Test the create mutation

    Users without the add_datatemplate permission should not be allowed
    to create template versions
    """
    user, client = permission_client(permissions)
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.organizations.add(org)
    user.studies.set(studies)
    user.save()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            "studies": [to_global_id("StudyNode", s.pk) for s in studies],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    if allowed:
        resp_dt = resp.json()["data"]["createTemplateVersion"][
            "templateVersion"
        ]
        assert resp_dt is not None

        # Check creation
        _, node_id = from_global_id(resp_dt["id"])
        template_version = TemplateVersion.objects.get(pk=node_id)

        # Check attributes
        assert template_version.description == resp_dt["description"]
        fdefs = json.loads(resp_dt["fieldDefinitions"])
        assert template_version.field_definitions == fdefs
        _, dt_node = from_global_id(resp_dt["dataTemplate"]["id"])
        assert template_version.data_template.pk == dt_node

    else:
        assert "Not allowed" in resp.json()["errors"][0]["message"]


def test_create_template_version_invalid(db, permission_client):
    """
    Test the create mutation with invalid field definitions
    """
    user, client = permission_client(["add_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.organizations.add(org)
    user.studies.set(studies)
    user.save()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps({"fields": "invalid fields"}),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            "studies": [to_global_id("StudyNode", s.pk) for s in studies],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )
    assert resp.json()["errors"][0]["message"]


def test_create_and_apply_all(db, permission_client):
    """
    Test create and add template to all studies in org
    """
    user, client = permission_client(["add_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(3, organization=org)
    user.organizations.add(org)
    user.studies.set(studies)
    user.save()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            # User accidentally supplied both these inputs
            # Should result in apply all behavior
            "applyToAll": True,
            "studies": [to_global_id("StudyNode", studies[0].pk)],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    # Check creation
    resp_dt = resp.json()["data"]["createTemplateVersion"]["templateVersion"]
    assert resp_dt is not None
    _, node_id = from_global_id(resp_dt["id"])
    tv = TemplateVersion.objects.get(pk=node_id)
    assert set(s.pk for s in tv.studies.all()) == set(
        s.pk for s in org.studies.all()
    )


def test_create_version_missing_data_template(db, permission_client):
    """
    Test the create mutation when the data template does not exist
    """
    user, client = permission_client(["add_datatemplate"])
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    dt_id = str(uuid.uuid4())

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt_id),
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    assert f"{dt_id} does not exist" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_create_template_version_not_my_org(db, permission_client):
    """
    Test the create template version for an org the user is not a member of
    """
    user, client = permission_client(["add_datatemplate"])
    org = OrganizationFactory()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    assert "Not allowed" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_create_template_version_missing_studies(db, permission_client):
    """
    Test the create template version with a study that doesn't exist
    """
    user, client = permission_client(["add_datatemplate", "change_study"])
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    dt = DataTemplateFactory(organization=org)
    studies = [str(uuid.uuid4())]

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            "studies": [to_global_id("StudyNode", s) for s in studies],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    assert "Failed to create/update" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_check_studies(db):
    """
    Test helper function used in template version mutations
    """

    class MockUser:
        def __init__(self, perms=[]):
            self.perms = perms

        def has_perm(self, perm):
            return perm in set(self.perms)

    studies = StudyFactory.create_batch(4)

    # User doesn't have change study permission
    user = MockUser()
    node_ids = [to_global_id("StudyNode", s.pk) for s in studies]
    with pytest.raises(GraphQLError) as e:
        check_studies(node_ids, user)
    assert "Not allowed" in str(e)

    # Study that doesn't exist should error
    user = MockUser(perms=["studies.change_study"])
    node_ids = [to_global_id("StudyNode", str(uuid.uuid4()))]
    with pytest.raises(GraphQLError) as e:
        check_studies(node_ids, user)
    assert "Failed to create/update" in str(e)

    # If all studies' pk exist and user allowed to modify, return studies
    study_ids = [s.pk for s in studies]
    out = check_studies(study_ids, user, primary_keys=True)
    assert set(s.pk for s in out) == set(study_ids)


def test_template_released(db):
    """
    Test TemplateVersion.released property
    """
    # TemplateVersions that have studies should have released = True
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    dt = DataTemplateFactory(organization=org)
    tv = TemplateVersionFactory(studies=studies)
    tv.refresh_from_db()
    assert tv.released == True  # noqa

    # TemplateVersions that have 0 studies should have released = False
    tv.studies.set([])
    tv.save()
    assert tv.released == False  # noqa


@pytest.mark.parametrize(
    "permissions,allowed",
    [
        (["change_datatemplate", "change_study"], True),
        (["change_datatemplate"], False),
        ([], False),
    ],
)
def test_update_template_version(db, permission_client, permissions, allowed):
    """
    Test the update mutation

    Users without the change_datatemplate permission should not be allowed
    to update template versions
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(permissions)
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added sex col to Participant Details",
        "studies": [to_global_id("StudyNode", s.pk) for s in studies],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()

    if allowed:
        resp_dt = resp.json()["data"]["updateTemplateVersion"][
            "templateVersion"
        ]
        assert resp_dt is not None

        # Check attributes
        assert template_version.description == resp_dt["description"]
        fdefs = json.loads(resp_dt["fieldDefinitions"])
        assert template_version.field_definitions == fdefs
        assert set([s.pk for s in studies]) == set(
            s["node"]["kfId"] for s in resp_dt["studies"]["edges"]
        )

    else:
        assert "Not allowed" in resp.json()["errors"][0]["message"]


def test_update_template_version_invalid(db, permission_client):
    """
    Test the update mutation with invalid field definitions
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(["change_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps({"fields": "bad fields"}),
        "description": "Added sex col to Participant Details",
        "studies": [to_global_id("StudyNode", s.pk) for s in studies],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()
    assert resp.json()["errors"][0]["message"]


def test_update_and_apply_all(db, permission_client):
    """
    Test update and add template to all studies in org
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(["change_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    tv = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added gender col to Participant Details",
        # User accidentally supplied both these inputs
        # Should result in apply all behavior
        "applyToAll": True,
        "studies": [to_global_id("StudyNode", studies[0].pk)],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", tv.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    tv.refresh_from_db()

    # Check update
    resp_dt = resp.json()["data"]["updateTemplateVersion"]["templateVersion"]
    assert resp_dt is not None
    assert set(s.pk for s in tv.studies.all()) == set(
        s.pk for s in org.studies.all()
    )


def test_update_template_version_does_not_exist(db, permission_client):
    """
    Test the update mutation when the template version does not exist
    """
    user, client = permission_client(["change_datatemplate"])
    tv_id = str(uuid.uuid4())

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added sex col to Participant Details",
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", tv_id),
                "input": input_,
            },
        },
        content_type="application/json",
    )

    assert f"{tv_id} does not exist" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_update_template_version_not_my_org(db, permission_client):
    """
    Test the update template version for an org user is not a member of
    """
    user, client = permission_client(["change_datatemplate"])
    # Add user to an org
    org1 = OrganizationFactory()
    user.organizations.add(org1)
    user.save()
    # Create template in diff org
    org2 = OrganizationFactory()
    dt = DataTemplateFactory(organization=org2)
    template_version = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps({"fields": []}),
        "description": "Added sex col to Participant Details",
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()

    assert "Not allowed" in resp.json()["errors"][0]["message"]


@pytest.mark.parametrize(
    "permissions,allowed",
    [
        (["delete_datatemplate"], True),
        ([], False),
    ],
)
def test_delete_template_version(db, permission_client, permissions, allowed):
    """
    Test the delete mutation

    Users without the delete_datatemplate permission should not be allowed
    to delete template versions
    """
    user, client = permission_client(permissions)
    # Add user to an organization
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    assert TemplateVersion.objects.count() == 1

    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
            },
        },
        content_type="application/json",
    )

    if allowed:
        resp_dt = resp.json()["data"]["deleteTemplateVersion"]["id"]
        assert resp_dt is not None
        with pytest.raises(TemplateVersion.DoesNotExist):
            TemplateVersion.objects.get(id=template_version.id)

    else:
        assert resp.json()["errors"][0]["message"] == "Not allowed"


def test_delete_template_version_does_not_exist(db, permission_client):
    """
    Test the delete mutation when the template version does not exist
    """
    user, client = permission_client(["delete_datatemplate"])
    org = OrganizationFactory()
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    tv_id = str(uuid.uuid4())

    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", tv_id),
            },
        },
        content_type="application/json",
    )

    assert f"{tv_id} does not exist" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 1


def test_delete_template_version_not_my_org(db, permission_client):
    """
    Test the delete template version for an org user is not a member of
    """
    user, client = permission_client(["delete_datatemplate"])
    # Add user to an org
    org1 = OrganizationFactory()
    user.organizations.add(org1)
    user.save()
    # Create template in diff org
    org2 = OrganizationFactory()
    dt = DataTemplateFactory(organization=org2)
    template_version = TemplateVersionFactory(data_template=dt)

    assert TemplateVersion.objects.count() == 1
    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
            },
        },
        content_type="application/json",
    )

    assert "Not allowed" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 1


def test_delete_released_template_version(db, permission_client):
    """
    Test the delete template version after it's already being used by studies
    """
    user, client = permission_client(["delete_datatemplate"])
    # Add user to an organization
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of and assign
    # template version to some studies
    dt = DataTemplateFactory(organization=org)
    studies = StudyFactory.create_batch(2, organization=org)
    template_version = TemplateVersionFactory(
        data_template=dt, studies=studies
    )

    assert TemplateVersion.objects.count() == 1
    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
            },
        },
        content_type="application/json",
    )

    assert "used by any studies" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 1


def test_unset_studies(db, permission_client):
    """
    Test the update mutation when setting studies to empty list
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(["change_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(
        data_template=dt, studies=studies
    )

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added sex col to Participant Details",
        "studies": [],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()

    resp_dt = resp.json()["data"]["updateTemplateVersion"][
        "templateVersion"
    ]
    assert template_version.studies.count() == 0
    assert len(resp_dt["studies"]["edges"]) == 0

import pytest
import json
import uuid
from graphql_relay import to_global_id, from_global_id
from graphql import GraphQLError

from creator.studies.factories import StudyFactory
from creator.organizations.factories import OrganizationFactory
from creator.data_templates.models import TemplateVersion
from creator.data_templates.factories import (
    DataTemplateFactory,
    TemplateVersionFactory,
)
from creator.data_templates.mutations.template_version import check_studies

FIELDS = {"fields": [
    {
        "key": "person.id",
        "label": "Person ID",
        "description": "Person identifiers",
        "required": True,
        "data_type": "string",
        "instructions": f"Populate person id properly",
    }
]}

CREATE_TEMPLATE_VERSION = """
mutation ($input: CreateTemplateVersionInput!) {
    createTemplateVersion(input: $input) {
        templateVersion {
            id
            description
            fieldDefinitions
            dataTemplate { id }
            studies { edges { node { id kfId } } }
        }
    }
}
"""

UPDATE_TEMPLATE_VERSION = """
mutation ($id: ID!, $input: UpdateTemplateVersionInput!) {
    updateTemplateVersion(id: $id, input: $input) {
        templateVersion {
            id
            description
            fieldDefinitions
            dataTemplate { id }
            studies { edges { node { id kfId } } }
        }
    }
}
"""

DELETE_TEMPLATE_VERSION = """
  mutation DeleteTemplateVersion($id: ID!) {
    deleteTemplateVersion(id: $id) {
      id
      success
    }
  }
"""


@pytest.mark.parametrize(
    "permissions,allowed",
    [
        (["add_datatemplate", "change_study"], True),
        (["add_datatemplate"], False),
        ([], False),
    ],
)
def test_create_template_version(db, permission_client, permissions, allowed):
    """
    Test the create mutation

    Users without the add_datatemplate permission should not be allowed
    to create template versions
    """
    user, client = permission_client(permissions)
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.organizations.add(org)
    user.studies.set(studies)
    user.save()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            "studies": [to_global_id("StudyNode", s.pk) for s in studies],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    if allowed:
        resp_dt = resp.json()["data"]["createTemplateVersion"][
            "templateVersion"
        ]
        assert resp_dt is not None

        # Check creation
        _, node_id = from_global_id(resp_dt["id"])
        template_version = TemplateVersion.objects.get(pk=node_id)

        # Check attributes
        assert template_version.description == resp_dt["description"]
        fdefs = json.loads(resp_dt["fieldDefinitions"])
        assert template_version.field_definitions == fdefs
        _, dt_node = from_global_id(resp_dt["dataTemplate"]["id"])
        assert template_version.data_template.pk == dt_node

    else:
        assert "Not allowed" in resp.json()["errors"][0]["message"]


def test_create_template_version_invalid(db, permission_client):
    """
    Test the create mutation with invalid field definitions
    """
    user, client = permission_client(["add_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.organizations.add(org)
    user.studies.set(studies)
    user.save()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps({"fields": "invalid fields"}),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            "studies": [to_global_id("StudyNode", s.pk) for s in studies],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )
    assert resp.json()["errors"][0]["message"]


def test_create_and_apply_all(db, permission_client):
    """
    Test create and add template to all studies in org
    """
    user, client = permission_client(["add_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(3, organization=org)
    user.organizations.add(org)
    user.studies.set(studies)
    user.save()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            # User accidentally supplied both these inputs
            # Should result in apply all behavior
            "applyToAll": True,
            "studies": [to_global_id("StudyNode", studies[0].pk)],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    # Check creation
    resp_dt = resp.json()["data"]["createTemplateVersion"]["templateVersion"]
    assert resp_dt is not None
    _, node_id = from_global_id(resp_dt["id"])
    tv = TemplateVersion.objects.get(pk=node_id)
    assert set(s.pk for s in tv.studies.all()) == set(
        s.pk for s in org.studies.all()
    )


def test_create_version_missing_data_template(db, permission_client):
    """
    Test the create mutation when the data template does not exist
    """
    user, client = permission_client(["add_datatemplate"])
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    dt_id = str(uuid.uuid4())

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt_id),
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    assert f"{dt_id} does not exist" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_create_template_version_not_my_org(db, permission_client):
    """
    Test the create template version for an org the user is not a member of
    """
    user, client = permission_client(["add_datatemplate"])
    org = OrganizationFactory()
    dt = DataTemplateFactory(organization=org)

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    assert "Not allowed" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_create_template_version_missing_studies(db, permission_client):
    """
    Test the create template version with a study that doesn't exist
    """
    user, client = permission_client(["add_datatemplate", "change_study"])
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    dt = DataTemplateFactory(organization=org)
    studies = [str(uuid.uuid4())]

    variables = {
        "input": {
            "fieldDefinitions": json.dumps(FIELDS),
            "description": "Added gender col to Participant Details",
            "dataTemplate": to_global_id("DataTemplateNode", dt.pk),
            "studies": [to_global_id("StudyNode", s) for s in studies],
        }
    }
    resp = client.post(
        "/graphql",
        data={"query": CREATE_TEMPLATE_VERSION, "variables": variables},
        content_type="application/json",
    )

    assert "Failed to create/update" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_check_studies(db):
    """
    Test helper function used in template version mutations
    """

    class MockUser:
        def __init__(self, perms=[]):
            self.perms = perms

        def has_perm(self, perm):
            return perm in set(self.perms)

    studies = StudyFactory.create_batch(4)

    # User doesn't have change study permission
    user = MockUser()
    node_ids = [to_global_id("StudyNode", s.pk) for s in studies]
    with pytest.raises(GraphQLError) as e:
        check_studies(node_ids, user)
    assert "Not allowed" in str(e)

    # Study that doesn't exist should error
    user = MockUser(perms=["studies.change_study"])
    node_ids = [to_global_id("StudyNode", str(uuid.uuid4()))]
    with pytest.raises(GraphQLError) as e:
        check_studies(node_ids, user)
    assert "Failed to create/update" in str(e)

    # If all studies' pk exist and user allowed to modify, return studies
    study_ids = [s.pk for s in studies]
    out = check_studies(study_ids, user, primary_keys=True)
    assert set(s.pk for s in out) == set(study_ids)


def test_template_released(db):
    """
    Test TemplateVersion.released property
    """
    # TemplateVersions that have studies should have released = True
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    dt = DataTemplateFactory(organization=org)
    tv = TemplateVersionFactory(studies=studies)
    tv.refresh_from_db()
    assert tv.released == True  # noqa

    # TemplateVersions that have 0 studies should have released = False
    tv.studies.set([])
    tv.save()
    assert tv.released == False  # noqa


@pytest.mark.parametrize(
    "permissions,allowed",
    [
        (["change_datatemplate", "change_study"], True),
        (["change_datatemplate"], False),
        ([], False),
    ],
)
def test_update_template_version(db, permission_client, permissions, allowed):
    """
    Test the update mutation

    Users without the change_datatemplate permission should not be allowed
    to update template versions
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(permissions)
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added sex col to Participant Details",
        "studies": [to_global_id("StudyNode", s.pk) for s in studies],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()

    if allowed:
        resp_dt = resp.json()["data"]["updateTemplateVersion"][
            "templateVersion"
        ]
        assert resp_dt is not None

        # Check attributes
        assert template_version.description == resp_dt["description"]
        fdefs = json.loads(resp_dt["fieldDefinitions"])
        assert template_version.field_definitions == fdefs
        assert set([s.pk for s in studies]) == set(
            s["node"]["kfId"] for s in resp_dt["studies"]["edges"]
        )

    else:
        assert "Not allowed" in resp.json()["errors"][0]["message"]


def test_update_template_version_invalid(db, permission_client):
    """
    Test the update mutation with invalid field definitions
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(["change_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps({"fields": "bad fields"}),
        "description": "Added sex col to Participant Details",
        "studies": [to_global_id("StudyNode", s.pk) for s in studies],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()
    assert resp.json()["errors"][0]["message"]


def test_update_and_apply_all(db, permission_client):
    """
    Test update and add template to all studies in org
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(["change_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    tv = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added gender col to Participant Details",
        # User accidentally supplied both these inputs
        # Should result in apply all behavior
        "applyToAll": True,
        "studies": [to_global_id("StudyNode", studies[0].pk)],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", tv.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    tv.refresh_from_db()

    # Check update
    resp_dt = resp.json()["data"]["updateTemplateVersion"]["templateVersion"]
    assert resp_dt is not None
    assert set(s.pk for s in tv.studies.all()) == set(
        s.pk for s in org.studies.all()
    )


def test_update_template_version_does_not_exist(db, permission_client):
    """
    Test the update mutation when the template version does not exist
    """
    user, client = permission_client(["change_datatemplate"])
    tv_id = str(uuid.uuid4())

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added sex col to Participant Details",
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", tv_id),
                "input": input_,
            },
        },
        content_type="application/json",
    )

    assert f"{tv_id} does not exist" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 0


def test_update_template_version_not_my_org(db, permission_client):
    """
    Test the update template version for an org user is not a member of
    """
    user, client = permission_client(["change_datatemplate"])
    # Add user to an org
    org1 = OrganizationFactory()
    user.organizations.add(org1)
    user.save()
    # Create template in diff org
    org2 = OrganizationFactory()
    dt = DataTemplateFactory(organization=org2)
    template_version = TemplateVersionFactory(data_template=dt)

    input_ = {
        "fieldDefinitions": json.dumps({"fields": []}),
        "description": "Added sex col to Participant Details",
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()

    assert "Not allowed" in resp.json()["errors"][0]["message"]


@pytest.mark.parametrize(
    "permissions,allowed",
    [
        (["delete_datatemplate"], True),
        ([], False),
    ],
)
def test_delete_template_version(db, permission_client, permissions, allowed):
    """
    Test the delete mutation

    Users without the delete_datatemplate permission should not be allowed
    to delete template versions
    """
    user, client = permission_client(permissions)
    # Add user to an organization
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    assert TemplateVersion.objects.count() == 1

    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
            },
        },
        content_type="application/json",
    )

    if allowed:
        resp_dt = resp.json()["data"]["deleteTemplateVersion"]["id"]
        assert resp_dt is not None
        with pytest.raises(TemplateVersion.DoesNotExist):
            TemplateVersion.objects.get(id=template_version.id)

    else:
        assert resp.json()["errors"][0]["message"] == "Not allowed"


def test_delete_template_version_does_not_exist(db, permission_client):
    """
    Test the delete mutation when the template version does not exist
    """
    user, client = permission_client(["delete_datatemplate"])
    org = OrganizationFactory()
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(data_template=dt)

    tv_id = str(uuid.uuid4())

    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", tv_id),
            },
        },
        content_type="application/json",
    )

    assert f"{tv_id} does not exist" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 1


def test_delete_template_version_not_my_org(db, permission_client):
    """
    Test the delete template version for an org user is not a member of
    """
    user, client = permission_client(["delete_datatemplate"])
    # Add user to an org
    org1 = OrganizationFactory()
    user.organizations.add(org1)
    user.save()
    # Create template in diff org
    org2 = OrganizationFactory()
    dt = DataTemplateFactory(organization=org2)
    template_version = TemplateVersionFactory(data_template=dt)

    assert TemplateVersion.objects.count() == 1
    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
            },
        },
        content_type="application/json",
    )

    assert "Not allowed" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 1


def test_delete_released_template_version(db, permission_client):
    """
    Test the delete template version after it's already being used by studies
    """
    user, client = permission_client(["delete_datatemplate"])
    # Add user to an organization
    org = OrganizationFactory()
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of and assign
    # template version to some studies
    dt = DataTemplateFactory(organization=org)
    studies = StudyFactory.create_batch(2, organization=org)
    template_version = TemplateVersionFactory(
        data_template=dt, studies=studies
    )

    assert TemplateVersion.objects.count() == 1
    resp = client.post(
        "/graphql",
        data={
            "query": DELETE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
            },
        },
        content_type="application/json",
    )

    assert "used by any studies" in resp.json()["errors"][0]["message"]
    assert TemplateVersion.objects.count() == 1


def test_unset_studies(db, permission_client):
    """
    Test the update mutation when setting studies to empty list
    """
    # Create a user that is a member of an org and some studies
    user, client = permission_client(["change_datatemplate", "change_study"])
    org = OrganizationFactory()
    studies = StudyFactory.create_batch(2, organization=org)
    user.studies.set(studies)
    user.organizations.add(org)
    user.save()
    # Create template in organization that user is member of
    dt = DataTemplateFactory(organization=org)
    template_version = TemplateVersionFactory(
        data_template=dt, studies=studies
    )

    input_ = {
        "fieldDefinitions": json.dumps(FIELDS),
        "description": "Added sex col to Participant Details",
        "studies": [],
    }
    resp = client.post(
        "/graphql",
        data={
            "query": UPDATE_TEMPLATE_VERSION,
            "variables": {
                "id": to_global_id("TemplateVersionNode", template_version.id),
                "input": input_,
            },
        },
        content_type="application/json",
    )
    template_version.refresh_from_db()

    resp_dt = resp.json()["data"]["updateTemplateVersion"][
        "templateVersion"
    ]
    assert template_version.studies.count() == 0
    assert len(resp_dt["studies"]["edges"]) == 0
mutation ($input: CreateTemplateVersionInput!) {     createTemplateVersion(input: $input) {         templateVersion {             id             description             fieldDefinitions             dataTemplate { id }             studies { edges { node { id kfId } } }         }     } } mutation ($id: ID!, $input: UpdateTemplateVersionInput!) {     updateTemplateVersion(id: $id, input: $input) {         templateVersion {             id             description             fieldDefinitions             dataTemplate { id }             studies { edges { node { id kfId } } }         }     } } mutation DeleteTemplateVersion($id: ID!) {     deleteTemplateVersion(id: $id) {       id       success     }   } Test the create mutation      Users without the add_datatemplate permission should not be allowed     to create template versions # Check creation # Check attributes Test the create mutation with invalid field definitions Test create and add template to all studies in org # User accidentally supplied both these inputs # Should result in apply all behavior # Check creation Test the create mutation when the data template does not exist Test the create template version for an org the user is not a member of Test the create template version with a study that doesn't exist Test helper function used in template version mutations # User doesn't have change study permission # Study that doesn't exist should error # If all studies' pk exist and user allowed to modify, return studies Test TemplateVersion.released property # TemplateVersions that have studies should have released = True # noqa # TemplateVersions that have 0 studies should have released = False # noqa Test the update mutation      Users without the change_datatemplate permission should not be allowed     to update template versions # Create a user that is a member of an org and some studies # Create template in organization that user is member of # Check attributes Test the update mutation with invalid field definitions # Create a user that is a member of an org and some studies # Create template in organization that user is member of Test update and add template to all studies in org # Create a user that is a member of an org and some studies # Create template in organization that user is member of # User accidentally supplied both these inputs # Should result in apply all behavior # Check update Test the update mutation when the template version does not exist Test the update template version for an org user is not a member of # Add user to an org # Create template in diff org Test the delete mutation      Users without the delete_datatemplate permission should not be allowed     to delete template versions # Add user to an organization # Create template in organization that user is member of Test the delete mutation when the template version does not exist Test the delete template version for an org user is not a member of # Add user to an org # Create template in diff org Test the delete template version after it's already being used by studies # Add user to an organization # Create template in organization that user is member of and assign # template version to some studies Test the update mutation when setting studies to empty list # Create a user that is a member of an org and some studies # Create template in organization that user is member of