/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using NUnit.Framework; using QuantConnect.Api; using QuantConnect.Configuration; using QuantConnect.Logging; using System; using System.Collections; using System.IO; using System.Text.Json; using System.Threading; namespace QuantConnect.Tests.API { /// /// Base test class for Api tests, provides the setup needed for all api tests /// public class ApiTestBase { internal int TestAccount; internal string TestToken; internal string TestOrganization; internal string DataFolder; internal Api.Api ApiClient; protected Project TestProject { get; private set; } protected Backtest TestBacktest { get; private set; } /// /// Run once before any RestApiTests /// [OneTimeSetUp] public void Setup() { ReloadConfiguration(); TestAccount = Globals.UserId; TestToken = Globals.UserToken; TestOrganization = Globals.OrganizationID; DataFolder = Globals.DataFolder; ApiClient = new Api.Api(); ApiClient.Initialize(TestAccount, TestToken, DataFolder); // Let's create a project and backtest that can be used for general testing CreateTestProjectAndBacktest(); } [OneTimeTearDown] public void TearDown() { DeleteTestProjectAndBacktest(); } private void CreateTestProjectAndBacktest() { // Create a new project and backtest that can be used for testing Log.Debug("ApiTestBase.Setup(): Creating test project and backtest"); var createProjectResult = ApiClient.CreateProject($"TestProject{DateTime.UtcNow.ToStringInvariant("yyyyMMddHHmmssfff")}", Language.CSharp, TestOrganization); if (!createProjectResult.Success) { Assert.Warn("Could not create test project, tests using it will fail."); return; } TestProject = createProjectResult.Projects[0]; // Create a new compile for the project Log.Debug("ApiTestBase.Setup(): Creating test compile"); var compile = ApiClient.CreateCompile(TestProject.ProjectId); if (!compile.Success) { Assert.Warn("Could not create compile for the test project, tests using it will fail."); return; } Log.Debug("ApiTestBase.Setup(): Waiting for test compile to complete"); compile = WaitForCompilerResponse(ApiClient, TestProject.ProjectId, compile.CompileId); if (!compile.Success) { Assert.Warn("Could not create compile for the test project, tests using it will fail."); return; } // Create a backtest Log.Debug("ApiTestBase.Setup(): Creating test backtest"); var backtestName = $"{DateTime.UtcNow.ToStringInvariant("u")} API Backtest"; var backtest = ApiClient.CreateBacktest(TestProject.ProjectId, compile.CompileId, backtestName); if (!backtest.Success) { Assert.Warn("Could not create backtest for the test project, tests using it will fail."); return; } Log.Debug("ApiTestBase.Setup(): Waiting for test backtest to complete"); TestBacktest = WaitForBacktestCompletion(ApiClient, TestProject.ProjectId, backtest.BacktestId); if (!TestBacktest.Success) { Assert.Warn("Could not create backtest for the test project, tests using it will fail."); return; } Log.Debug("ApiTestBase.Setup(): Test project and backtest created successfully"); } private void DeleteTestProjectAndBacktest() { if (TestBacktest != null && TestBacktest.Success) { Log.Debug("ApiTestBase.TearDown(): Deleting test backtest"); ApiClient.DeleteBacktest(TestProject.ProjectId, TestBacktest.BacktestId); } if (TestProject != null) { Log.Debug("ApiTestBase.TearDown(): Deleting test project"); ApiClient.DeleteProject(TestProject.ProjectId); } } public static bool IsValidJson(string jsonString) { try { using (JsonDocument doc = JsonDocument.Parse(jsonString)) { } return true; } catch (JsonException) { return false; } } /// /// Wait for the compiler to respond to a specified compile request /// /// Id of the project /// Id of the compilation of the project /// public static Compile WaitForCompilerResponse(Api.Api apiClient, int projectId, string compileId, int seconds = 60) { var compile = new Compile(); var finish = DateTime.UtcNow.AddSeconds(seconds); do { Thread.Sleep(100); compile = apiClient.ReadCompile(projectId, compileId); } while (compile.State == CompileState.InQueue && DateTime.UtcNow < finish); return compile; } /// /// Wait for the backtest to complete /// /// Project id to scan /// Backtest id previously started /// Completed backtest object public static Backtest WaitForBacktestCompletion(Api.Api apiClient, int projectId, string backtestId, int secondsTimeout = 60, bool returnFailedBacktest = false) { Backtest backtest; var finish = DateTime.UtcNow.AddSeconds(secondsTimeout); do { Thread.Sleep(1000); backtest = apiClient.ReadBacktest(projectId, backtestId); if (backtest == null) { // api failed, let's retry continue; } if (!string.IsNullOrEmpty(backtest.Error) || backtest.HasInitializeError) { if (!returnFailedBacktest) { Assert.Fail($"Backtest {projectId}/{backtestId} failed: {backtest.Error}. Stacktrace: {backtest.Stacktrace}. Api errors: {string.Join(",", backtest.Errors)}"); } else { return backtest; } } } while (((backtest == null || (backtest.Success && backtest.Progress < 1)) && DateTime.UtcNow < finish)); return backtest; } /// /// Reload configuration, making sure environment variables are loaded into the config /// internal static void ReloadConfiguration() { // nunit 3 sets the current folder to a temp folder we need it to be the test bin output folder var dir = TestContext.CurrentContext.TestDirectory; Environment.CurrentDirectory = dir; Directory.SetCurrentDirectory(dir); // reload config from current path Config.Reset(); var environment = Environment.GetEnvironmentVariables(); foreach (DictionaryEntry entry in environment) { var envKey = entry.Key.ToString(); var value = entry.Value.ToString(); if (envKey.StartsWith("QC_", StringComparison.InvariantCulture)) { var key = envKey.Substring(3).Replace("_", "-", StringComparison.InvariantCulture).ToLowerInvariant(); Log.Trace($"TestSetup(): Updating config setting '{key}' from environment var '{envKey}'"); Config.Set(key, value); } } // resets the version among other things Globals.Reset(); } } }