public void Multi_Threaded_ApiFactory_Tasks(int quoteCount, int threadCount)
        {
            var config   = ApiConfigurationBuilder.Build("secrets.json");
            var provider = new ClientCredentialsFlowTokenProvider(config);

            var date = new DateTimeOffset(2018, 1, 1, 0, 0, 0, TimeSpan.Zero);

            var request = Enumerable.Range(0, quoteCount).Select(i => new UpsertQuoteRequest(
                                                                     new QuoteId(
                                                                         new QuoteSeriesId(
                                                                             provider: "DataScope",
                                                                             priceSource: "BankA",
                                                                             instrumentId: "BBG000B9XRY4",
                                                                             instrumentIdType: QuoteSeriesId.InstrumentIdTypeEnum.Figi,
                                                                             quoteType: QuoteSeriesId.QuoteTypeEnum.Price,
                                                                             field: "mid"),
                                                                         effectiveAt: date.AddDays(i)),
                                                                     metricValue: new MetricValue(
                                                                         value: 199.23m,
                                                                         unit: "USD"),
                                                                     lineage: "InternalSystem")).ToDictionary(k => k.QuoteId.EffectiveAt.ToString(), v => v);

            var tasks = Enumerable.Range(0, threadCount).Select(x => Task.Run(() =>
            {
                var factory = LusidApiFactoryBuilder.Build(config.ApiUrl, provider);
                var result  = factory.Api <IQuotesApi>().UpsertQuotes("mt-scope", request);
                Assert.That(result.Failed, Is.Empty);

                Console.WriteLine($"{DateTimeOffset.UtcNow} {Thread.CurrentThread.ManagedThreadId} {result.Values.Count}");
            }));

            Task.WaitAll(tasks.ToArray());
        }
        public async Task CanGetToken()
        {
            // GIVEN a token provider initialised with required secrets
            var provider = new ClientCredentialsFlowTokenProvider(ApiConfig.Value);

            // WHEN the token is requested
            var token = await provider.GetAuthenticationTokenAsync();

            // THEN it is populated
            Assert.That(token, Is.Not.Empty);
        }
        public async Task CanRefreshWithoutToken()
        {
            // GIVEN a token from the TokenProvider that DOES NOT contain a refresh token
            var provider = new ClientCredentialsFlowTokenProvider(ApiConfig.Value);
            var _        = await provider.GetAuthenticationTokenAsync();

            var firstTokenDetails = provider.GetLastToken();

            Assert.That(firstTokenDetails.RefreshToken, Is.Null, "refresh_token was returned so unable to verify refresh behaviour with a token.  This requires the userid defined in secrets.json to be set to NOT 'allow offline access' in Okta");

            // WHEN we pretend to delay until the original token has expired (for expediency update the expires_on on the token)
            provider.ExpireToken();

            Assert.That(DateTimeOffset.UtcNow, Is.GreaterThan(firstTokenDetails.ExpiresOn));
            var refreshedToken = await provider.GetAuthenticationTokenAsync();

            // THEN it should be populated, and the ExpiresOn should be in the future
            Assert.That(refreshedToken, Is.Not.Empty);
            Assert.That(provider.GetLastToken().ExpiresOn, Is.GreaterThan(DateTimeOffset.UtcNow));
        }
        public void LinuxSocketLeakTest() // See DEV-7152
        {
            ApiConfiguration config = ApiConfigurationBuilder.Build("secrets.json");
            var provider            = new ClientCredentialsFlowTokenProvider(config);

            var api = BuildApi();

            api.CreatePortfolioGroup("sdktest", new CreatePortfolioGroupRequest("TestGroup", displayName: "TestGroup"));

            // This loop should eventually throw a SocketException: "Address already in use" once all the sockets have been exhausted
            for (int i = 0; i < 50_000; i++)
            {
                api = BuildApi();
                PortfolioGroup result = api.GetPortfolioGroup("sdktest", "TestGroup");
                Assert.That(result, Is.Not.Null);

                // Option 1: force dispose of ApiClient
                //api.Configuration.ApiClient.Dispose();

                // Option 2: force all finalizers to run
                if (i % 100 == 0)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }
            }

            /*** Local Functions ***/
            IPortfolioGroupsApi BuildApi()
            {
                // An instance of HttpClient is created within LusidApiFactory.Configuration.ApiClient.RestClient
                // which wasn't being disposed
                ILusidApiFactory    factory = LusidApiFactoryBuilder.Build(config.ApiUrl, provider);
                IPortfolioGroupsApi api     = factory.Api <IPortfolioGroupsApi>();

                return(api);
            }
        }
        public async Task CanGetNewTokenWhenRefreshTokenExpired()
        {
            var provider = new ClientCredentialsFlowTokenProvider(ApiConfig.Value);
            var _        = await provider.GetAuthenticationTokenAsync();

            var firstTokenDetails = provider.GetLastToken();

            Assert.That(firstTokenDetails.RefreshToken, Is.Not.Null.And.Not.Empty, "refresh_token not returned so unable to verify refresh behaviour.");

            Console.WriteLine($"Token expiring at {firstTokenDetails.ExpiresOn:o}");

            // WHEN we pretend to delay until both...
            // (1) the original token has expired (for expediency update the expires_on on the token)
            provider.ExpireToken();
            // (2) the refresh token has expired (for expediency update the refresh_token to an invalid value that will not be found)
            provider.GetLastToken().RefreshToken = "InvalidRefreshToken";
            Assert.That(DateTimeOffset.UtcNow, Is.GreaterThan(firstTokenDetails.ExpiresOn));
            var refreshedToken = await provider.GetAuthenticationTokenAsync();

            // THEN it should be populated, and the ExpiresOn should be in the future
            Assert.That(refreshedToken, Is.Not.Empty);
            Assert.That(provider.GetLastToken().ExpiresOn, Is.GreaterThan(DateTimeOffset.UtcNow));
        }