from graphqlclient import GraphQLClient
import unittest
from os import environ
from app import app
from flask_testing import LiveServerTestCase
from json import loads
from database.database import db_session
from database.models import User, Role

class GraphQlTests(LiveServerTestCase):

    def create_app(self):
        app.config["TESTING"] = True
        return app

    def setUp(self):
        self.client = GraphQLClient(self.get_server_url()+"/graphql")

    def tearDown(self):
        # clean up tables
        for username in ["graphqluser", "graphqluser2"]:
            user = db_session.query(User).filter_by(username=username).first()
            if user is not None:
                user.roles = []
                user.applications = []
                db_session.delete(user)
                db_session.commit()
        role = db_session.query(Role).filter_by(name="testusers").first()
        if role is not None:
            role.users = []
            db_session.delete(role)
            db_session.commit()


    def test_query_create_and_get_user(self):
        username = "graphqluser"
        password = "randompassword"
        email = "user@example.com"
        result = self._create_user(username, password, email)
        self.assertNotIn("error", result, "Creating user failed due to an error")
        self.assertIn("data", result)
        self.assertIn("createUser", result["data"])
        self.assertIn("user", result["data"]["createUser"])
        user = result["data"]["createUser"]["user"]
        self.assertEqual(user["username"], username)
        self.assertEqual(user["email"], email)
        querystring = '''
        {{
          getUser(username: "{0}"){{
            username,
            email
         }}
        }}
        '''.format(username).strip()
        result = loads(self.client.execute(querystring))
        self.assertIn("data", result)
        self.assertIn("getUser", result["data"])
        user = result["data"]["getUser"]
        self.assertEqual(user["username"], username)
        self.assertEqual(user["email"], email)

    def _create_user(self, username, password, email):
        querystring = '''
        mutation{{
          createUser(
            username: "{0}",
              password: "{1}",
              email: "{2}"){{
              user{{
                username,
                email}}}}}}
        '''.format(username, password, email).strip()
        return loads(self.client.execute(querystring))

    def test_create_and_assign_role(self):
        role_name = "testusers"
        role_description = "A role assigned to users during unittests"
        result = self._create_role(role_name, role_description)
        self.assertIn("data", result)
        self.assertIn("createRole", result["data"])
        self.assertIn("role", result["data"]["createRole"])
        role = result["data"]["createRole"]["role"]
        self.assertEqual(role["name"], role_name)
        self.assertEqual(role["description"], role_description)
        username = "graphqluser2"
        password = "randompassword"
        email = "user2@example.com"
        self._create_user(username, password, email)
        querystring = '''
        mutation{{
          addRoleToUser(
            username: "{0}",
            role: "{1}"){{
              user{{
                username,
                roles{{
                  edges{{
                    node{{
                      name
                    }}
                  }}
                }}
              }}
              role{{
                name,
                description
                users{{
                  edges{{
                    node{{
                      username
                    }}
                  }}
                }}
              }}
           }}
        }}
        '''.format(username, role_name).strip()
        result = loads(self.client.execute(querystring))
        self.assertIn("data", result)
        self.assertIn("addRoleToUser", result["data"])
        user = result["data"]["addRoleToUser"]["user"]
        self.assertEqual(username, user["username"])
        self.assertIn("edges", user["roles"])
        roles = user["roles"]["edges"]
        self.assertEqual(role_name, roles[0]["node"]["name"])
        role = result["data"]["addRoleToUser"]["role"]
        self.assertEqual(role_name, role["name"])
        self.assertIn("edges", role["users"])
        users = role["users"]["edges"]
        self.assertEqual(username, users[0]["node"]["username"])

    def _create_role(self, name, description):
        querystring = '''
        mutation{{
          createRole(
            name: "{0}",
            description: "{1}"){{
              role{{
                name,
                description}}}}}}
        '''.format(name, description).strip()
        return loads(self.client.execute(querystring))