public async void TryInvokeFromCacheAsync_WithTypeParameter_IsThreadSafe()
        {
            // Arrange
            const string      dummyArg    = "success";
            HttpNodeJSService testSubject = CreateHttpNodeJSService();
            await testSubject.InvokeFromStringAsync <DummyResult>(_dummyReturnsArgModule, DUMMY_CACHE_IDENTIFIER, args : new[] { dummyArg }).ConfigureAwait(false);

            // Act
            var       results    = new ConcurrentQueue <(bool, DummyResult)>();
            const int numThreads = 5;
            var       threads    = new List <Thread>();

            for (int i = 0; i < numThreads; i++)
            {
                var thread = new Thread(() => results.Enqueue(testSubject.
                                                              TryInvokeFromCacheAsync <DummyResult>(DUMMY_CACHE_IDENTIFIER, args: new[] { dummyArg }).GetAwaiter().GetResult()));
                threads.Add(thread);
                thread.Start();
            }
            foreach (Thread thread in threads)
            {
                thread.Join();
            }

            // Assert
            Assert.Equal(numThreads, results.Count);
            foreach ((bool success, DummyResult value) in results)
            {
                Assert.True(success);
                Assert.Equal(dummyArg, value.Result);
            }
        }
        public async void TryInvokeFromCacheAsync_WithoutTypeParameter_IsThreadSafe()
        {
            // Arrange
            HttpNodeJSService testSubject = CreateHttpNodeJSService();
            await testSubject.InvokeFromStringAsync(_dummyExportsMultipleFunctionsModule, DUMMY_CACHE_IDENTIFIER, "incrementNumber").ConfigureAwait(false);

            // Act
            const int numThreads = 5;
            var       threads    = new List <Thread>();

            for (int i = 0; i < numThreads; i++)
            {
                var thread = new Thread(() => testSubject.TryInvokeFromCacheAsync(DUMMY_CACHE_IDENTIFIER, "incrementNumber").GetAwaiter().GetResult());
                threads.Add(thread);
                thread.Start();
            }
            foreach (Thread thread in threads)
            {
                thread.Join();
            }

            // Assert
            int result = await testSubject.InvokeFromStringAsync <int>(_dummyExportsMultipleFunctionsModule, DUMMY_CACHE_IDENTIFIER, "getNumber").ConfigureAwait(false);

            Assert.Equal(numThreads + 1, result);
        }
        public async void TryInvokeFromCacheAsync_WithoutTypeParameter_ReturnsFalseIfModuleIsNotCached()
        {
            // Arrange
            HttpNodeJSService testSubject = CreateHttpNodeJSService();

            // Act
            bool success = await testSubject.TryInvokeFromCacheAsync(DUMMY_CACHE_IDENTIFIER).ConfigureAwait(false);

            // Assert
            Assert.False(success);
        }
        public async void TryInvokeFromCacheAsync_WithTypeParameter_ReturnsFalseIfModuleIsNotCached()
        {
            // Arrange
            HttpNodeJSService testSubject = CreateHttpNodeJSService();

            // Act
            (bool success, DummyResult value) = await testSubject.TryInvokeFromCacheAsync <DummyResult>(DUMMY_CACHE_IDENTIFIER, args : new[] { "success" }).ConfigureAwait(false);

            // Assert
            Assert.False(success);
            Assert.Null(value);
        }
        public async void TryInvokeFromCacheAsync_WithTypeParameter_InvokesFromCacheIfModuleIsCached()
        {
            // Arrange
            const string      dummyArg    = "success";
            HttpNodeJSService testSubject = CreateHttpNodeJSService();
            await testSubject.InvokeFromStringAsync <DummyResult>(_dummyReturnsArgModule, DUMMY_CACHE_IDENTIFIER, args : new[] { dummyArg }).ConfigureAwait(false);

            // Act
            (bool success, DummyResult value) = await testSubject.TryInvokeFromCacheAsync <DummyResult>(DUMMY_CACHE_IDENTIFIER, args : new[] { dummyArg }).ConfigureAwait(false);

            // Assert
            Assert.True(success);
            Assert.Equal(dummyArg, value.Result);
        }
        public async void TryInvokeFromCacheAsync_ReturnsFalseIfModuleIsNotCached()
        {
            // Arrange
            const string      dummyResultString    = "success";
            const string      dummyCacheIdentifier = "dummyCacheIdentifier";
            HttpNodeJSService testSubject          = CreateHttpNodeJSService();

            // Act
            (bool success, DummyResult value) = await testSubject.TryInvokeFromCacheAsync <DummyResult>(dummyCacheIdentifier, args : new[] { dummyResultString }).ConfigureAwait(false);

            // Assert
            Assert.False(success);
            Assert.Null(value);
        }
        public async void TryInvokeFromCacheAsync_WithoutTypeParameter_InvokesFromCacheIfModuleIsCached()
        {
            // Arrange
            HttpNodeJSService testSubject = CreateHttpNodeJSService();
            await testSubject.InvokeFromStringAsync(_dummyExportsMultipleFunctionsModule, DUMMY_CACHE_IDENTIFIER, "incrementNumber").ConfigureAwait(false);

            // Act
            bool success = await testSubject.TryInvokeFromCacheAsync(DUMMY_CACHE_IDENTIFIER, "incrementNumber").ConfigureAwait(false);

            // Assert
            Assert.True(success);
            int result = await testSubject.InvokeFromStringAsync <int>(_dummyExportsMultipleFunctionsModule, DUMMY_CACHE_IDENTIFIER, "getNumber").ConfigureAwait(false);

            Assert.Equal(2, result);
        }
        public async void InvokeFromStringAsync_WithoutTypeParameter_WithModuleFactory_InvokesFromStringAndCachesModuleIfModuleIsNotCached()
        {
            // Arrange
            HttpNodeJSService testSubject = CreateHttpNodeJSService();

            // Act
            await testSubject.
            InvokeFromStringAsync(() => _dummyExportsMultipleFunctionsModule, DUMMY_CACHE_IDENTIFIER, "incrementNumber").ConfigureAwait(false);

            // Assert
            // Ensure module was cached
            (bool success, int result) = await testSubject.TryInvokeFromCacheAsync <int>(DUMMY_CACHE_IDENTIFIER, "getNumber").ConfigureAwait(false);

            Assert.True(success);
            Assert.Equal(1, result);
        }
        public async void InvokeFromStringAsync_WithTypeParameter_WithModuleFactory_InvokesFromStringAndCachesModuleIfModuleIsNotCached()
        {
            // Arrange
            const string      dummyArg    = "success";
            HttpNodeJSService testSubject = CreateHttpNodeJSService();

            // Act
            // Module hasn't been cached, so if this returns the expected value, string was sent over
            DummyResult result1 = await testSubject.
                                  InvokeFromStringAsync <DummyResult>(() => _dummyReturnsArgModule, DUMMY_CACHE_IDENTIFIER, args : new[] { dummyArg }).ConfigureAwait(false);

            // Assert
            // Ensure module was cached
            (bool success, DummyResult result2) = await testSubject.TryInvokeFromCacheAsync <DummyResult>(DUMMY_CACHE_IDENTIFIER, args : new[] { dummyArg }).ConfigureAwait(false);

            Assert.Equal(dummyArg, result1.Result);
            Assert.True(success);
            Assert.Equal(dummyArg, result2.Result);
        }
        public async void TryInvokeFromCacheAsync_InvokesJavascriptIfModuleIsCached()
        {
            // Arrange
            const string      dummyResultString    = "success";
            const string      dummyCacheIdentifier = "dummyCacheIdentifier";
            HttpNodeJSService testSubject          = CreateHttpNodeJSService();

            // Cache
            await testSubject.
            InvokeFromStringAsync <DummyResult>("module.exports = (callback, resultString) => callback(null, {result: resultString});",
                                                dummyCacheIdentifier,
                                                args : new[] { dummyResultString }).
            ConfigureAwait(false);

            // Act
            (bool success, DummyResult value) = await testSubject.TryInvokeFromCacheAsync <DummyResult>(dummyCacheIdentifier, args : new[] { dummyResultString }).ConfigureAwait(false);

            // Assert
            Assert.True(success);
            Assert.Equal(dummyResultString, value.Result);
        }
        public async void TryInvokeFromCacheAsync_IsThreadSafe()
        {
            // Arrange
            const string      dummyResultString    = "success";
            const string      dummyCacheIdentifier = "dummyCacheIdentifier";
            HttpNodeJSService testSubject          = CreateHttpNodeJSService();

            // Cache
            await testSubject.
            InvokeFromStringAsync <DummyResult>("module.exports = (callback, resultString) => callback(null, {result: resultString});",
                                                dummyCacheIdentifier,
                                                args : new[] { dummyResultString }).
            ConfigureAwait(false);

            // Act
            var       results    = new ConcurrentQueue <(bool, DummyResult)>();
            const int numThreads = 5;
            var       threads    = new List <Thread>();

            for (int i = 0; i < numThreads; i++)
            {
                var thread = new Thread(() => results.Enqueue(testSubject.TryInvokeFromCacheAsync <DummyResult>(dummyCacheIdentifier, args: new[] { dummyResultString }).GetAwaiter().GetResult()));
                threads.Add(thread);
                thread.Start();
            }
            foreach (Thread thread in threads)
            {
                thread.Join();
            }

            // Assert
            Assert.Equal(numThreads, results.Count);
            foreach ((bool success, DummyResult value) in results)
            {
                Assert.True(success);
                Assert.Equal(dummyResultString, value.Result);
            }
        }