using Capnp.Rpc;
using FabAccessAPI;
using FabAccessAPI.Schema;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using static FabAccessAPI.Schema.Machine;

namespace FabAccessAPI_Test
{
    public static class API_TestEnv_Test
    {
        public const string TESTSERVER = "bffh.lab.bln.kjknet.de";
        public const int TESTSERVER_PORT = 59666;
        public const string PASSWORD = "secret";

        public static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

        public static async Task<Connection> Connect(string username)
        {
            TcpRpcClient tcpRpcClient = new TcpRpcClient();
            tcpRpcClient.InjectMidlayer((tcpstream) =>
            {
                var sslStream = new SslStream(tcpstream, false, new RemoteCertificateValidationCallback(RemoteCertificateValidationCallback));
                try
                {
                    sslStream.AuthenticateAsClient("bffhd");
                    return sslStream;
                }
                catch (AuthenticationException)
                {
                    sslStream.Close();
                    throw;
                }
            });

            tcpRpcClient.Connect(TESTSERVER, TESTSERVER_PORT);
            await tcpRpcClient.WhenConnected;

            Connection connection = new Connection(tcpRpcClient);
            await connection.Auth("PLAIN", new Dictionary<string, object>(StringComparer.Ordinal) { { "Username", username }, { "Password", PASSWORD } });

            return connection;
        }

        public static void Disconnect(Connection connection)
        {
            connection.RpcClient.Dispose();
        }
    }

    [TestFixture, Parallelizable(ParallelScope.Children)]
    [Order(1)]
    public class MachineSystem_Test
    {
        [TestCase("Admin1", true)]
        [TestCase("Admin2", true)]
        [TestCase("ManagerA1", true)]
        [TestCase("ManagerA2", true)]
        [TestCase("ManagerB1", true)]
        [TestCase("ManagerB2", true)]
        [TestCase("ManagerC1", true)]
        [TestCase("ManagerC2", true)]
        [TestCase("ManagerABC1", true)]
        [TestCase("ManagerABC2", true)]
        [TestCase("MakerA1", true)]
        [TestCase("MakerA2", true)]
        [TestCase("MakerB1", true)]
        [TestCase("MakerB2", true)]
        [TestCase("MakerC1", true)]
        [TestCase("MakerC2", true)]
        [TestCase("MakerABC1", true)]
        [TestCase("MakerABC2", true)]
        [TestCase("GuestA1", true)]
        [TestCase("GuestA2", true)]
        [TestCase("GuestB1", true)]
        [TestCase("GuestB2", true)]
        [TestCase("GuestC1", true)]
        [TestCase("GuestC2", true)]
        [TestCase("GuestABC1", true)]
        [TestCase("GuestABC2", true)]
        [TestCase("MakerQRA", true)]
        [TestCase("MakerQRB", true)]
        [TestCase("MakerQRC", true)]

        [Order(1)]
        public async Task AccessMachineSystem(string username, bool expectAllow)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            bool result = session.MachineSystem != null;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectAllow, result);
        }

        //[TestCase("", true)]
        //[Order(2)]
        //public async Task AccessMachineSystem_Info(string username, bool expectAllow)
        //{
        //    Session session = await API_TestEnv_Test.Connect(username);

        //    Assert.AreEqual(expectAllow, session.MachineSystem.Info);
        //}

        [TestCase("Admin1", 15)]
        [TestCase("ManagerA1", 5)]
        [TestCase("ManagerB1", 5)]
        [TestCase("ManagerC1", 5)]
        [TestCase("ManagerABC1", 15)]
        [TestCase("MakerA1", 5)]
        [TestCase("MakerB1", 5)]
        [TestCase("MakerC1", 5)]
        [TestCase("MakerABC1", 15)]
        [TestCase("GuestA1", 5)]
        [TestCase("GuestB1", 5)]
        [TestCase("GuestC1", 5)]
        [TestCase("GuestABC1", 15)]
        [TestCase("MakerQRA", 0)]
        [TestCase("MakerQRB", 0)]
        [TestCase("MakerQRC", 0)]
        [Order(3)]
        public async Task ListMachines(string username, int expectedMachineCount)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            IReadOnlyList<Machine> machine_list = await session.MachineSystem.Info.GetMachineList().ConfigureAwait(false);

            int result = machine_list.Count;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectedMachineCount, result);
        }

        [TestCase("Admin1", "MachineA1", true)]
        [TestCase("Admin1", "MachineB1", true)]
        [TestCase("Admin1", "MachineC1", true)]
        [TestCase("ManagerA1", "MachineA1", true)]
        [TestCase("ManagerA1", "MachineB1", false)]
        [TestCase("ManagerA1", "MachineC1", false)]
        [TestCase("ManagerB1", "MachineA1", false)]
        [TestCase("ManagerB1", "MachineB1", true)]
        [TestCase("ManagerB1", "MachineC1", false)]
        [TestCase("ManagerC1", "MachineA1", false)]
        [TestCase("ManagerC1", "MachineB1", false)]
        [TestCase("ManagerC1", "MachineC1", true)]
        [TestCase("ManagerABC1", "MachineA1", true)]
        [TestCase("ManagerABC1", "MachineB1", true)]
        [TestCase("ManagerABC1", "MachineC1", true)]
        [TestCase("MakerA1", "MachineA1", true)]
        [TestCase("MakerB1", "MachineB1", true)]
        [TestCase("MakerC1", "MachineC1", true)]
        [TestCase("GuestA1", "MachineA1", true)]
        [TestCase("GuestB1", "MachineB1", true)]
        [TestCase("GuestC1", "MachineC1", true)]
        [TestCase("MakerQRA", "MachineA1", true)]
        [TestCase("MakerQRB", "MachineB1", true)]
        [TestCase("MakerQRC", "MachineC1", true)]

        [Order(4)]
        public async Task GetMachineByName(string username, string machineName, bool expectedAllow)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineName).ConfigureAwait(false);
            bool result = machine != null;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectedAllow, result);
        }

        [TestCase("Admin1", "MachineX")]
        [TestCase("Admin1", "urn:fabaccess:resource:MachineA1")]
        [Order(5)]
        public async Task GetMachineByName_WrongName(string username, string machineName)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineName).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Assert.IsNull(machine);
        }

        [TestCase("Admin1", "urn:fabaccess:resource:MachineA1", true)]
        [TestCase("Admin1", "urn:fabaccess:resource:MachineB1", true)]
        [TestCase("Admin1", "urn:fabaccess:resource:MachineC1", true)]
        [TestCase("ManagerA1", "urn:fabaccess:resource:MachineA1", true)]
        [TestCase("ManagerA1", "urn:fabaccess:resource:MachineB1", false)]
        [TestCase("ManagerA1", "urn:fabaccess:resource:MachineC1", false)]
        [TestCase("ManagerB1", "urn:fabaccess:resource:MachineA1", false)]
        [TestCase("ManagerB1", "urn:fabaccess:resource:MachineB1", true)]
        [TestCase("ManagerB1", "urn:fabaccess:resource:MachineC1", false)]
        [TestCase("ManagerC1", "urn:fabaccess:resource:MachineA1", false)]
        [TestCase("ManagerC1", "urn:fabaccess:resource:MachineB1", false)]
        [TestCase("ManagerC1", "urn:fabaccess:resource:MachineC1", true)]
        [TestCase("ManagerABC1", "urn:fabaccess:resource:MachineA1", true)]
        [TestCase("ManagerABC1", "urn:fabaccess:resource:MachineB1", true)]
        [TestCase("ManagerABC1", "urn:fabaccess:resource:MachineC1", true)]
        [TestCase("MakerA1", "urn:fabaccess:resource:MachineA1", true)]
        [TestCase("MakerB1", "urn:fabaccess:resource:MachineB1", true)]
        [TestCase("MakerC1", "urn:fabaccess:resource:MachineC1", true)]
        [TestCase("GuestA1", "urn:fabaccess:resource:MachineA1", true)]
        [TestCase("GuestB1", "urn:fabaccess:resource:MachineB1", true)]
        [TestCase("GuestC1", "urn:fabaccess:resource:MachineC1", true)]
        [TestCase("MakerQRA", "urn:fabaccess:resource:MachineA1", true)]
        [TestCase("MakerQRB", "urn:fabaccess:resource:MachineB1", true)]
        [TestCase("MakerQRC", "urn:fabaccess:resource:MachineC1", true)]

        [Order(6)]
        public async Task GetMachineByURN(string username, string urn, bool expectedAllow)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(urn).ConfigureAwait(false);
            bool result = machine != null;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectedAllow, result);
        }

        [TestCase("Admin1", "urn:fabaccess:resource:MachineX")]
        [TestCase("Admin1", "MachineA1")]
        [TestCase("Admin1", "something")]
        [Order(7)]
        public async Task GetMachineByURN_WrongURN(string username, string urn)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(urn).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Assert.IsNull(machine);
        }
    }

    [TestFixture, Parallelizable(ParallelScope.Children)]
    [Order(2)]
    public class Machine_Test_Stateless
    {
        [TestCase("Admin1", "MachineA1", true)]
        [TestCase("Admin1", "MachineB1", true)]
        [TestCase("Admin1", "MachineC1", true)]
        [TestCase("ManagerA1", "MachineA1", true)]
        [TestCase("ManagerA1", "MachineB1", false)]
        [TestCase("ManagerA1", "MachineC1", false)]
        [TestCase("ManagerB1", "MachineA1", false)]
        [TestCase("ManagerB1", "MachineB1", true)]
        [TestCase("ManagerB1", "MachineC1", false)]
        [TestCase("ManagerC1", "MachineA1", false)]
        [TestCase("ManagerC1", "MachineB1", false)]
        [TestCase("ManagerC1", "MachineC1", true)]
        [TestCase("ManagerABC1", "MachineA1", true)]
        [TestCase("ManagerABC1", "MachineB1", true)]
        [TestCase("ManagerABC1", "MachineC1", true)]
        [TestCase("MakerA1", "MachineA1", true)]
        [TestCase("MakerB1", "MachineB1", true)]
        [TestCase("MakerC1", "MachineC1", true)]
        [TestCase("GuestA1", "MachineA1", true)]
        [TestCase("GuestB1", "MachineB1", true)]
        [TestCase("GuestC1", "MachineC1", true)]
        [TestCase("MakerQRA", "MachineA1", true)]
        [TestCase("MakerQRB", "MachineB1", true)]
        [TestCase("MakerQRC", "MachineC1", true)]
        [Order(1)]
        public async Task InfoInterface(string username, string machineID, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            bool result = !((Machine.InfoInterface_Proxy)machine.Info).IsNull;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }

        [TestCase("Admin1", "MachineA1", true)]
        [TestCase("Admin1", "MachineB1", true)]
        [TestCase("Admin1", "MachineC1", true)]
        [TestCase("ManagerA1", "MachineA1", true)]
        [TestCase("ManagerA1", "MachineB1", false)]
        [TestCase("ManagerA1", "MachineC1", false)]
        [TestCase("ManagerB1", "MachineA1", false)]
        [TestCase("ManagerB1", "MachineB1", true)]
        [TestCase("ManagerB1", "MachineC1", false)]
        [TestCase("ManagerC1", "MachineA1", false)]
        [TestCase("ManagerC1", "MachineB1", false)]
        [TestCase("ManagerC1", "MachineC1", true)]
        [TestCase("ManagerABC1", "MachineA1", true)]
        [TestCase("ManagerABC1", "MachineB1", true)]
        [TestCase("ManagerABC1", "MachineC1", true)]
        [TestCase("MakerA1", "MachineA1", false)]
        [TestCase("MakerB1", "MachineB1", false)]
        [TestCase("MakerC1", "MachineC1", false)]
        [TestCase("GuestA1", "MachineA1", false)]
        [TestCase("GuestB1", "MachineB1", false)]
        [TestCase("GuestC1", "MachineC1", false)]
        [TestCase("MakerQRA", "MachineA1", false)]
        [TestCase("MakerQRB", "MachineB1", false)]
        [TestCase("MakerQRC", "MachineC1", false)]
        [Order(2)]
        public async Task ManageInterface(string username, string machineID, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            bool result = !((ManageInterface_Proxy)machine.Manage).IsNull;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }

        [TestCase("Admin1", "MachineA1", true)]
        [TestCase("Admin1", "MachineB1", true)]
        [TestCase("Admin1", "MachineC1", true)]
        [TestCase("ManagerA1", "MachineA1", false)]
        [TestCase("ManagerA1", "MachineB1", false)]
        [TestCase("ManagerA1", "MachineC1", false)]
        [TestCase("ManagerB1", "MachineA1", false)]
        [TestCase("ManagerB1", "MachineB1", false)]
        [TestCase("ManagerB1", "MachineC1", false)]
        [TestCase("ManagerC1", "MachineA1", false)]
        [TestCase("ManagerC1", "MachineB1", false)]
        [TestCase("ManagerC1", "MachineC1", false)]
        [TestCase("ManagerABC1", "MachineA1", false)]
        [TestCase("ManagerABC1", "MachineB1", false)]
        [TestCase("ManagerABC1", "MachineC1", false)]
        [TestCase("MakerA1", "MachineA1", false)]
        [TestCase("MakerB1", "MachineB1", false)]
        [TestCase("MakerC1", "MachineC1", false)]
        [TestCase("GuestA1", "MachineA1", false)]
        [TestCase("GuestB1", "MachineB1", false)]
        [TestCase("GuestC1", "MachineC1", false)]
        [TestCase("MakerQRA", "MachineA1", false)]
        [TestCase("MakerQRB", "MachineB1", false)]
        [TestCase("MakerQRC", "MachineC1", false)]
        [Order(3)]
        public async Task AdminInterface(string username, string machineID, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            bool result = !((AdminInterface_Proxy)machine.Admin).IsNull;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }

        [TestCase("Admin1", "MachineA1", "Description of MachineA1", @"https://fab-access.readthedocs.io", "CategoryA")]
        [TestCase("Admin1", "MachineB2", "Description of MachineB2", @"https://fab-access.readthedocs.io", "CategoryB")]
        [TestCase("Admin1", "MachineC3", "Description of MachineC3", @"https://fab-access.readthedocs.io", "CategoryC")]
        [Order(4)]
        public async Task ReadMachineData(string username, string machineID, string description, string wiki, string category)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Assert.Multiple(() =>
            {
                Assert.AreEqual(machineID, machine.Id);
                Assert.AreEqual(description, machine.Description);
                Assert.AreEqual(wiki, machine.Wiki);
                Assert.AreEqual(category, machine.Category);
            });
        }
    }

    [TestFixture]
    [Order(3)]
    public class Machine_Test
    {
        #region SetUp
        public async Task ForceFree(string username, string machineID)
        {
            Console.WriteLine("start: " + machineID);
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            await machine.Manage.ForceFree().ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Console.WriteLine("finished: " + machineID);
        }

        [OneTimeSetUp]
        public async Task OneTimeSetUp()
        {
            List<Task> tasks = new List<Task>()
            {
                ForceFree("Admin1", "MachineA1"),
                ForceFree("Admin1", "MachineA2"),
                ForceFree("Admin1", "MachineA3"),
                ForceFree("Admin1", "MachineA4"),
                ForceFree("Admin1", "MachineA5"),
                ForceFree("Admin1", "MachineB1"),
                ForceFree("Admin1", "MachineB2"),
                ForceFree("Admin1", "MachineB3"),
                ForceFree("Admin1", "MachineB4"),
                ForceFree("Admin1", "MachineB5"),
                ForceFree("Admin1", "MachineC1"),
                ForceFree("Admin1", "MachineC2"),
                ForceFree("Admin1", "MachineC3"),
                ForceFree("Admin1", "MachineC4"),
                ForceFree("Admin1", "MachineC5")
            };

            await Task.WhenAll(tasks);
        }
        #endregion

        [TestCase("Admin1", "MachineA1")]
        [TestCase("ManagerA1", "MachineA1")]
        [TestCase("MakerA1", "MachineA1")]
        [Order(1)]
        public async Task UseGiveBack(string username, string machineID)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            Machine machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            // Check State before run Test
            if (machine.State != MachineState.free)
            {
                API_TestEnv_Test.Disconnect(connection);
                Assert.Inconclusive("State is not 'free'");
            }

            await machine.Use.Use().ConfigureAwait(false);

            machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine.Inuse.GiveBack().ConfigureAwait(false);

            machine = await session.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(MachineState.free, machine.State);
        }

        [TestCase("ManagerA1", "MakerA1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [TestCase("ManagerA1", "GuestA1", "MachineA1")]
        [Order(2)]
        public async Task TransferMachine(string username1, string username2, string machineID)
        {
            Connection connection1 = await API_TestEnv_Test.Connect(username1);
            Session session1 = connection1.Session;

            Connection connection2 = await API_TestEnv_Test.Connect(username2);
            Session session2 = connection1.Session;

            Machine machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            // Check State before run Test
            if (machine1.State != MachineState.free)
            {
                API_TestEnv_Test.Disconnect(connection1);
                API_TestEnv_Test.Disconnect(connection2);
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine1.Inuse.Releasefortakeover().ConfigureAwait(false);

            Machine machine2 = await session2.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine2.Takeover.Accept().ConfigureAwait(false);

            machine2 = await session2.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine2.Inuse.GiveBack().ConfigureAwait(false);

            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection1);
            API_TestEnv_Test.Disconnect(connection2);

            Assert.AreEqual(MachineState.free, machine1.State);
        }

        [TestCase("ManagerA1", "MakerA1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [TestCase("ManagerA1", "GuestA1", "MachineA1")]
        [Order(3)]
        public async Task TransferMachine_Reject(string username1, string username2, string machineID)
        {
            Connection connection1 = await API_TestEnv_Test.Connect(username1);
            Session session1 = connection1.Session;

            Connection connection2 = await API_TestEnv_Test.Connect(username2);
            Session session2 = connection1.Session;

            Machine machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            // Check State before run Test
            if (machine1.State != MachineState.free)
            {
                API_TestEnv_Test.Disconnect(connection1);
                API_TestEnv_Test.Disconnect(connection2);
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine1.Inuse.Releasefortakeover().ConfigureAwait(false);

            Machine machine2 = await session2.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine2.Takeover.Reject().ConfigureAwait(false);

            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            machine2 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection1);
            API_TestEnv_Test.Disconnect(connection2);

            Assert.AreEqual(MachineState.free, machine2.State);
        }

        [TestCase("ManagerA1", "ManagerA1", "MachineA1")]
        [TestCase("ManagerA1", "Admin1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [Order(4)]
        public async Task CheckMachine(string username1, string username2, string machineID)
        {
            Connection connection1 = await API_TestEnv_Test.Connect(username1);
            Session session1 = connection1.Session;

            Connection connection2 = await API_TestEnv_Test.Connect(username2);
            Session session2 = connection1.Session;

            Machine machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            // Check State before run Test
            if (machine1.State != MachineState.free)
            {
                API_TestEnv_Test.Disconnect(connection1);
                API_TestEnv_Test.Disconnect(connection2);
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            Machine machine2 = await session2.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine2.Check.Check().ConfigureAwait(false);

            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection1);
            API_TestEnv_Test.Disconnect(connection2);

            Assert.AreEqual(MachineState.free, machine1.State);
        }

        [TestCase("ManagerA1", "ManagerA1", "MachineA1")]
        [TestCase("ManagerA1", "Admin1", "MachineA1")]
        [TestCase("MakerA1", "Admin1", "MachineA1")]
        [Order(5)]
        public async Task CheckMachine_Reject(string username1, string username2, string machineID)
        {
            Connection connection1 = await API_TestEnv_Test.Connect(username1);
            Session session1 = connection1.Session;

            Connection connection2 = await API_TestEnv_Test.Connect(username2);
            Session session2 = connection1.Session;


            Machine machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            // Check State before run Test
            if (machine1.State != MachineState.free)
            {
                API_TestEnv_Test.Disconnect(connection1);
                API_TestEnv_Test.Disconnect(connection2);
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            Machine machine2 = await session2.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine2.Check.Reject().ConfigureAwait(false);

            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            machine2 = await session2.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine2.Check.Check().ConfigureAwait(false);

            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection1);
            API_TestEnv_Test.Disconnect(connection2);

            Assert.AreEqual(MachineState.free, machine1.State);
        }

        [TestCase("MakerA1", "GuestA1", "ManagerA1", "MachineA1")]
        [Order(4)]
        public async Task CheckMachine_NoPermission(string username1, string username2, string username3, string machineID)
        {
            Connection connection1 = await API_TestEnv_Test.Connect(username1);
            Session session1 = connection1.Session;

            Connection connection2 = await API_TestEnv_Test.Connect(username2);
            Session session2 = connection1.Session;

            Connection connection3 = await API_TestEnv_Test.Connect(username3);
            Session session3 = connection1.Session;

            Machine machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);

            // Check State before run Test
            if (machine1.State != MachineState.free)
            {
                API_TestEnv_Test.Disconnect(connection1);
                API_TestEnv_Test.Disconnect(connection2);
                Assert.Inconclusive("State is not 'free'");
            }

            await machine1.Use.Use().ConfigureAwait(false);
            machine1 = await session1.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine1.Inuse.GiveBack().ConfigureAwait(false);

            Machine machine2 = await session2.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            bool result = ((CheckInterface_Proxy)machine2.Check).IsNull;

            Machine machine3 = await session3.MachineSystem.Info.GetMachine(machineID).ConfigureAwait(false);
            await machine3.Check.Check().ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection1);
            API_TestEnv_Test.Disconnect(connection2);

            Assert.IsTrue(result);
        }
    }

    [TestFixture, Parallelizable(ParallelScope.Children)]
    [Order(4)]
    public class PermissionSystem_Test
    {
        [TestCase("Admin1", true)]
        [TestCase("ManagerA1", true)]
        [TestCase("MakerA1", true)]
        [TestCase("GuestA1", true)]
        [Order(1)]
        public async Task AccessPermissionSystem(string username, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            bool result = session.PermissionSystem != null;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }

        //[TestCase("Admin1", true)]
        //[TestCase("ManagerA1", true)]
        //[TestCase("MakerA1", true)]
        //[TestCase("GuestA1", true)]
        //[Order(2)]
        //public async Task InfoInterface(string username, bool expectInterface)
        //{
        //    Connection connection = await API_TestEnv_Test.Connect(username);
        //    Session session = connection.Session;

        //    MachineSystem.IInfoInterface infoInterface = await session.PermissionSystem.Info().ConfigureAwait(false);

        //    bool result = !((Machine.InfoInterface_Proxy)machine.Info).IsNull;

        //    API_TestEnv_Test.Disconnect(connection);

        //    Assert.AreEqual(expectInterface, result);
        //}

        [TestCase("Admin1", 13)]
        [TestCase("ManagerA1", 13)]
        [TestCase("MakerA1", 13)]
        [TestCase("GuestA1", 13)]
        [Order(3)]
        public async Task ListRoles(string username, int expectRolesCount)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            IReadOnlyList<Role> roles_list = await session.PermissionSystem.Info.GetRoleList().ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectRolesCount, roles_list.Count);
        }
    }

    [TestFixture, Parallelizable(ParallelScope.Children)]
    [Order(5)]
    public class UserSystem_Test
    {
        [TestCase("Admin1", true)]
        [TestCase("ManagerA1", true)]
        [TestCase("MakerA1", true)]
        [TestCase("GuestA1", true)]
        [Order(1)]
        public async Task AccessUserSystem(string username, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            bool result = session.UserSystem != null;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }


        //[TestCase("Admin1", true)]
        //[TestCase("ManagerA1", true)]
        //[TestCase("MakerA1", true)]
        //[TestCase("GuestA1", true)]
        //[Order(2)]
        //public async Task InfoInterface(string username, bool expectInterface)
        //{
        //    Connection connection = await API_TestEnv_Test.Connect(username);
        //    Session session = connection.Session;

        //    UserSystem.IInfoInterface infoInterface = await session.UserSystem.Info().ConfigureAwait(false);

        //    bool result = !((UserSystem.InfoInterface_Proxy)infoInterface.Info).IsNull;

        //    API_TestEnv_Test.Disconnect(connection);

        //    Assert.AreEqual(expectInterface, result);
        //}

        //[TestCase("Admin1", true)]
        //[TestCase("ManagerA1", false)]
        //[TestCase("MakerA1", false)]
        //[TestCase("GuestA1", false)]
        //[Order(3)]
        //public async Task ManageInterface(string username, bool expectInterface)
        //{
        //    Connection connection = await API_TestEnv_Test.Connect(username);
        //    Session session = connection.Session;

        //    UserSystem.IInfoInterface infoInterface = await session.UserSystem.Info().ConfigureAwait(false);

        //    bool result = !((UserSystem.InfoInterface_Proxy)infoInterface.Info).IsNull;

        //    API_TestEnv_Test.Disconnect(connection);

        //    Assert.AreEqual(expectInterface, result);
        //}

        [TestCase("Admin1")]
        [TestCase("ManagerA1")]
        [TestCase("MakerA1")]
        [TestCase("GuestA1")]
        [Order(4)]
        public async Task GetUserSelf(string username)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            User user = await session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Assert.IsNotNull(user);
        }
    }

    [TestFixture, Parallelizable(ParallelScope.Children)]
    [Order(6)]
    public class User_Test_Stateless
    {
        [TestCase("Admin1", true)]
        [TestCase("ManagerA1", true)]
        [TestCase("MakerA1", true)]
        [TestCase("GuestA1", true)]
        [Order(1)]
        public async Task InfoInterface(string username, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            User user = await session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);

            bool result = !((User.InfoInterface_Proxy)user.Info).IsNull;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }

        [TestCase("Admin1", true)]
        [TestCase("ManagerA1", true)]
        [TestCase("MakerA1", true)]
        [TestCase("GuestA1", true)]
        [Order(2)]
        public async Task ManageInterface(string username, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            User user = await session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);

            bool result = !((User.ManageInterface_Proxy)user.Manage).IsNull;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }

        [TestCase("Admin1", true)]
        [TestCase("ManagerA1", false)]
        [TestCase("MakerA1", false)]
        [TestCase("GuestA1", false)]
        [Order(3)]
        public async Task AdminInterface(string username, bool expectInterface)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            User user = await session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);

            bool result = !((User.AdminInterface_Proxy)user.Admin).IsNull;

            API_TestEnv_Test.Disconnect(connection);

            Assert.AreEqual(expectInterface, result);
        }

        [TestCase("Admin1")]
        [TestCase("ManagerA1")]
        [TestCase("MakerA1")]
        [TestCase("GuestA1")]
        [Order(4)]
        public async Task ReadUserData(string username)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            User user = await session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);

            API_TestEnv_Test.Disconnect(connection);

            Assert.Multiple(() =>
            {
                Assert.AreEqual(username, user.Username);
            });
        }

        [TestCase("Admin1", "somerole")]
        [TestCase("ManagerA1")]
        [TestCase("MakerA1")]
        [TestCase("GuestA1")]
        [Order(5)]
        public async Task ListUserRoles(string username, params string[] expect_roles)
        {
            Connection connection = await API_TestEnv_Test.Connect(username);
            Session session = connection.Session;

            User user = await session.UserSystem.Info.GetUserSelf().ConfigureAwait(false);

            List<Role> roles_user = new List<Role>(await user.Info.ListRoles().ConfigureAwait(false));
            List<string> expect_roles_list = new List<string>(expect_roles);

            API_TestEnv_Test.Disconnect(connection);

            if (roles_user.Count != expect_roles_list.Count)
            {
                Assert.Fail("Roles Count is different");
            }

            foreach (Role role_user in roles_user)
            {
                if(!expect_roles_list.Exists(x => x == role_user.Name))
                {
                    Assert.Fail("Roles are different");
                }
            }
        }
    }

    [TestFixture]
    [Order(7)]
    public class User_Test
    {
        
    }
}