context: // Copyright (c) The University of Dundee 2018-2019
  // This file is part of the Research Data Management Platform (RDMP).
  // RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  // RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  // You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  using System.Reflection;
  using System.Runtime.InteropServices;
  using FAnsi;
  using FAnsi.Implementation;
  using FAnsi.Implementations.MicrosoftSQL;
  using FAnsi.Implementations.MySql;
  using FAnsi.Implementations.Oracle;
  using FAnsi.Implementations.PostgreSql;
  using MapsDirectlyToDatabaseTable;
  using NUnit.Framework;
  using Rdmp.Core.CommandExecution;
  using Rdmp.Core.CommandLine.Interactive;
  using Rdmp.Core.Curation.Data;
  using Rdmp.Core.Curation.Data.Aggregation;
  using Rdmp.Core.Curation.Data.Cache;
  using Rdmp.Core.Curation.Data.Cohort;
  using Rdmp.Core.Curation.Data.Cohort.Joinables;
  using Rdmp.Core.Curation.Data.Dashboarding;
  using Rdmp.Core.Curation.Data.DataLoad;
  using Rdmp.Core.Curation.Data.Governance;
  using Rdmp.Core.Curation.Data.ImportExport;
  using Rdmp.Core.Curation.Data.Pipelines;
  using Rdmp.Core.Curation.Data.Remoting;
  using Rdmp.Core.Curation.Data.Spontaneous;
  using Rdmp.Core.Databases;
  using Rdmp.Core.DataExport.Data;
  using Rdmp.Core.DataExport.DataRelease;
  using Rdmp.Core.DataExport.DataRelease.Audit;
  using Rdmp.Core.DataExport.DataRelease.Potential;
  using Rdmp.Core.DataLoad.Modules.DataFlowOperations;
  using Rdmp.Core.Repositories;
  using Rdmp.Core.Validation;
  using ReusableLibraryCode.Checks;
  
  namespace Tests.Common
  {
      /// <summary>
      /// Base class for all tests that want to create objects only in memory (and not in database like <see cref="DatabaseTests"/>)
      /// </summary>
      [Category("Unit")]
      public class UnitTests
      {
          protected MemoryDataExportRepository Repository = new MemoryDataExportRepository();
          protected IRDMPPlatformRepositoryServiceLocator RepositoryLocator { get; private set; }
          
          //These types do not have to be supported by the method WhenIHaveA
          protected HashSet<string> SkipTheseTypes = new HashSet<string>(new string[]
          {
              "TestColumn",
              "ExtractableCohort",
              "DQEGraphAnnotation",
              "Evaluation",
              "WindowLayout",
          });
          
          
          public UnitTests()
          {
              RepositoryLocator = new RepositoryProvider(Repository);
          }
  
          /// <summary>
          /// Returns an <see cref="IBasicActivateItems"/> based on the <see cref="RepositoryLocator"/>
          /// (or <paramref name="locator"/> if specified) that throws if input is sought (e.g.
          /// <see cref="IBasicActivateItems.YesNo(DialogArgs)"/>)
          /// </summary>
          /// <returns></returns>
          protected IBasicActivateItems GetActivator(IRDMPPlatformRepositoryServiceLocator locator = null)
          {
              return new ConsoleInputManager(locator ?? RepositoryLocator, new ThrowImmediatelyCheckNotifier()) { DisallowInput = true};
          }
  
          /// <summary>
          /// Override to do stuff before your first instance is constructed
          /// </summary>
          [OneTimeSetUp]
          protected virtual void OneTimeSetUp()
          {
              ImplementationManager.Load<MicrosoftSQLImplementation>();
              ImplementationManager.Load<MySqlImplementation>();
              ImplementationManager.Load<OracleImplementation>();
              ImplementationManager.Load<PostgreSqlImplementation>();
          }
  
          /// <summary>
          /// Loads FAnsi implementations for all supported DBMS platforms into memory
          /// </summary>
          [SetUp]
          protected virtual void SetUp()
          {
          }
  
  
          /// <summary>
          /// Creates a minimum viable object of Type T.  This includes the object and any dependencies e.g. a 
          /// <see cref="ColumnInfo"/> cannot exist without a <see cref="TableInfo"/>.  
          /// </summary>
          /// <typeparam name="T">Type of object you want to create</typeparam>
          /// <returns></returns>
          /// <exception cref="NotSupportedException">If there is not yet an implementation for the given T.  Feel free to write one.</exception>
          protected T WhenIHaveA<T>() where T : DatabaseEntity
          {
              if (typeof(T) == typeof(Catalogue))
                  return (T)(object)Save(new Catalogue(Repository, "Mycata"));
  
              
              if (typeof(T) == typeof(ExtendedProperty))
              {
                  return (T)(object)new ExtendedProperty(Repository,Save(new Catalogue(Repository, "Mycata")),"TestProp",0);
              }
                  
  
              if (typeof(T) == typeof(CatalogueItem))
              {
                  var cata = new Catalogue(Repository, "Mycata");
                  return (T)(object)new CatalogueItem(Repository, cata, "MyCataItem");
              }
  
              if (typeof(T) == typeof(ExtractionInformation))
              {
                  var col = WhenIHaveA<ColumnInfo>();
  
                  var cata = new Catalogue(Repository, "Mycata");
                  var ci = new CatalogueItem(Repository, cata, "MyCataItem");
                  var ei = new ExtractionInformation(Repository, ci, col, "MyCataItem");
                  return (T)(object)Save(ei);
              }
  
              if (typeof(T) == typeof(TableInfo))
              {
                  var table = new TableInfo(Repository, "My_Table"){DatabaseType = DatabaseType.MicrosoftSQLServer};
                  return (T)(object)table;
              }
  
              if (typeof(T) == typeof(ColumnInfo))
              {
                  var ti = WhenIHaveA<TableInfo>();
                  var col = new ColumnInfo(Repository, "My_Col", "varchar(10)", ti);
                  return (T)(object)col;
              }
  
              if (typeof(T) == typeof(AggregateConfiguration))
              {
                  ExtractionInformation dateEi;
                  ExtractionInformation otherEi;
                  return (T)(object)WhenIHaveA<AggregateConfiguration>(out dateEi, out otherEi);
              }
  
              if (typeof(T) == typeof(ExternalDatabaseServer))
              {
                  return (T)(object)Save(new ExternalDatabaseServer(Repository, "My Server",null));
              }
  
              if (typeof(T) == typeof(ANOTable))
              {
                  ExternalDatabaseServer server;
                  return (T)(object)WhenIHaveA<ANOTable>(out server);
              }
  
              if (typeof(T) == typeof(LoadMetadata))
              {
                  //creates the table, column, catalogue, catalogue item and extraction information
                  var ei = WhenIHaveA<ExtractionInformation>();
                  var cata = ei.CatalogueItem.Catalogue;
  
                  var ti = ei.ColumnInfo.TableInfo;
                  ti.Server = "localhost";
                  ti.Database = "mydb";
                  ti.SaveToDatabase();
  
                  var lmd = new LoadMetadata(Repository, "MyLoad");
                  cata.LoadMetadata_ID = lmd.ID;
                  cata.SaveToDatabase();
                  return (T)(object)lmd;
              }
  
              if (typeof (T) == typeof (AggregateTopX))
              {
                  var agg = WhenIHaveA<AggregateConfiguration>();
                  return (T)(object) new AggregateTopX(Repository, agg, 10);
              }
  
              if (typeof (T) == typeof (ConnectionStringKeyword))
              {
                  return (T)(object)new ConnectionStringKeyword(Repository, DatabaseType.MicrosoftSQLServer, "MultipleActiveResultSets", "true");
              }
  
              if (typeof (T) == typeof (DashboardLayout))
                  return (T)(object)new DashboardLayout(Repository, "My Layout");
  
              if (typeof(T) == typeof(DashboardControl))
              {
                  var layout = WhenIHaveA<DashboardLayout>();
                  return (T)(object)Save(new DashboardControl(Repository, layout, typeof(int), 0, 0, 100, 100, "") { ControlType = "GoodBadCataloguePieChart" });
              }
  
              if (typeof(T) == typeof(DashboardObjectUse))
              {
                  var layout = WhenIHaveA<DashboardLayout>();
                  var control = Save(new DashboardControl(Repository, layout, typeof(int), 0, 0, 100, 100, "") { ControlType = "GoodBadCataloguePieChart" });
                  var use = new DashboardObjectUse(Repository, control, WhenIHaveA<Catalogue>());
                  return (T)(object)use;
              }
  
              if (typeof(T) == typeof(ExtractionFilter))
              {
                  var ei = WhenIHaveA<ExtractionInformation>();
                  return (T)(object)new ExtractionFilter(Repository, "My Filter", ei);
              }
  
              if (typeof(T) == typeof(ExtractionFilterParameter))
              {
                  var filter = WhenIHaveA<ExtractionFilter>();
                  filter.WhereSQL = "@myParam = 'T'";
  
                  return (T)(object)new ExtractionFilterParameter(Repository,"DECLARE @myParam varchar(10)",filter);
              }
  
              if (typeof(T) == typeof(ExtractionFilterParameterSetValue))
              {
                  var parameter = WhenIHaveA<ExtractionFilterParameter>();
                  var set = new ExtractionFilterParameterSet(Repository, parameter.ExtractionFilter, "Parameter Set");
                  return (T)(object)new ExtractionFilterParameterSetValue(Repository, set, parameter);
              }
  
              if (typeof (T) == typeof(ExtractionFilterParameterSet))
              {
                  return (T)(object)WhenIHaveA<ExtractionFilterParameterSetValue>().ExtractionFilterParameterSet;
              }
  
              if (typeof (T) == typeof (Favourite))
                  return (T) (object) new Favourite(Repository, WhenIHaveA<Catalogue>());
  
              if (typeof (T) == typeof(ObjectExport))
              {
                  ShareManager sm;
                  return (T)(object)WhenIHaveA<ObjectExport>(out sm);
              }
              
              if (typeof (T) == typeof(ObjectImport))
              {
                  ShareManager sm;
                  ObjectExport export = WhenIHaveA<ObjectExport>(out sm);
                  return (T)(object)sm.GetImportAs(export.SharingUID, WhenIHaveA<Catalogue>());
              }
              
              if (typeof (T) == typeof(WindowLayout))
                  return (T)(object)new WindowLayout(Repository,"My window arrangement","<html><body>ignore this</body></html>");
  
              if (typeof (T) == typeof(RemoteRDMP))
                  return (T)(object)new RemoteRDMP(Repository);
              
              if (typeof (T) == typeof(CohortIdentificationConfiguration))
                  return (T)(object)new CohortIdentificationConfiguration(Repository,"My cic");
  
              if (typeof (T) == typeof(JoinableCohortAggregateConfiguration))
              {
                  var config = WhenIHaveCohortAggregateConfiguration("PatientIndexTable");
                  var cic = WhenIHaveA<CohortIdentificationConfiguration>();
                  cic.EnsureNamingConvention(config);
                  return (T)(object)new JoinableCohortAggregateConfiguration(Repository,cic,config);
              }
              
              if (typeof (T) == typeof(JoinableCohortAggregateConfigurationUse))
              {
                  var joinable = WhenIHaveA<JoinableCohortAggregateConfiguration>();
                  var config = WhenIHaveCohortAggregateConfiguration("Aggregate");
                  return (T)(object)joinable.AddUser(config);
              }
              
              if (typeof (T) == typeof(Rdmp.Core.Curation.Data.Plugin))
                  return (T)(object)new Rdmp.Core.Curation.Data.Plugin(Repository,new FileInfo("bob.nupkg"),new Version(1,1,1),new Version(1,1,1));
              
              if (typeof (T) == typeof(LoadModuleAssembly))
              {
                  var dll = Path.Combine(TestContext.CurrentContext.TestDirectory,"a.nupkg");
                  File.WriteAllBytes(dll,new byte[] {0x11});
  
                  return (T)(object)new LoadModuleAssembly(Repository,new FileInfo(dll),WhenIHaveA<Rdmp.Core.Curation.Data.Plugin>());
              }
              
              if (typeof (T) == typeof(AggregateContinuousDateAxis))
              {
                  ExtractionInformation dateEi;
                  ExtractionInformation otherEi;
                  var config = WhenIHaveA<AggregateConfiguration>(out dateEi,out otherEi);
                  
                  //remove the other Ei
                  config.AggregateDimensions[0].DeleteInDatabase();
                  //add the date one
                  var dim = new AggregateDimension(Repository, dateEi, config);
  
                  return (T)(object)new AggregateContinuousDateAxis(Repository,dim);
              }
              
              if (typeof (T) == typeof(AggregateDimension))
                  return (T)(object) WhenIHaveA<AggregateConfiguration>().AggregateDimensions[0];
              
              if (typeof (T) == typeof(AggregateFilterContainer))
              {
                  var config = WhenIHaveA<AggregateConfiguration>();
                  var container = new AggregateFilterContainer(Repository,FilterContainerOperation.AND);
                  config.RootFilterContainer_ID = container.ID;
                  config.SaveToDatabase();
                  return (T) (object) container;
              }
              if (typeof (T) == typeof(AggregateFilter))
              {
                  var container = WhenIHaveA<AggregateFilterContainer>();
                  return (T)(object)new AggregateFilter(Repository,"My Filter",container);
              }
              
              if (typeof (T) == typeof(AggregateFilterParameter))
              {
                  var filter = WhenIHaveA<AggregateFilter>();
                  filter.WhereSQL = "@MyP = 'mnnn apples'";
                  filter.SaveToDatabase();
  
                  return (T)filter.GetFilterFactory().CreateNewParameter(filter, "DECLARE @MyP as varchar(10)");
              }
  
              if (typeof (T) == typeof (LoadProgress))
                  return (T) (object) new LoadProgress(Repository, WhenIHaveA<LoadMetadata>());
  
              if (typeof (T) == typeof(CacheProgress))
                  return (T)(object)new CacheProgress(Repository,WhenIHaveA<LoadProgress>());
  
              if (typeof (T) == typeof(CacheFetchFailure))
                  return (T)(object)new CacheFetchFailure(Repository,WhenIHaveA<CacheProgress>(),DateTime.Now.Subtract(new TimeSpan(1,0,0,0)),DateTime.Now,new Exception("It didn't work"));
              
              if (typeof (T) == typeof(CohortAggregateContainer))
              {
  
gt:                 var cic = WhenIHaveA<CohortIdentificationConfiguration>();