public async Task TestRemoveStockWithDuplicateRequest()
        {
            int totalStartingStock = 8;
            int expectedQuantity = 5;
            int quantityToRemove = 3;
            MockReliableStateManager stateManager = new MockReliableStateManager();
            InventoryService target = new InventoryService(stateManager);

            InventoryItem item = new InventoryItem("test", 1, totalStartingStock, 1, expectedQuantity);

            await target.CreateInventoryItemAsync(item);

            CustomerOrderActorMessageId cmid = CustomerOrderActorMessageId.GetRandom();

            int actualRemoved = await target.RemoveStockAsync(item.Id, quantityToRemove, cmid);

            Assert.AreEqual(quantityToRemove, actualRemoved);
            Assert.AreEqual(expectedQuantity, item.AvailableStock);

            //save the current availablestock so we can check to be sure it doesn't change
            int priorAvailableStock = item.AvailableStock;

            //but now lets say that the reciever didn't get the response and so sends the exact same request again
            int actualRemoved2 = await target.RemoveStockAsync(item.Id, quantityToRemove, cmid);

            //in this case the response for the amount removed should be the same
            Assert.AreEqual(actualRemoved, actualRemoved2);

            //also, since the request was a duplicate the remaining invintory should be the same as it was before. 
            Assert.AreEqual(item.AvailableStock, priorAvailableStock);
        }
        public async Task TestCreateAndIsItemInInventoryAsync()
        {
            MockReliableStateManager stateManager = new MockReliableStateManager();
            InventoryService target = new InventoryService(stateManager);

            InventoryItem expected = new InventoryItem("test", 1, 10, 1, 10);

            await target.CreateInventoryItemAsync(expected);
            bool resultTrue = await target.IsItemInInventoryAsync(expected.Id);
            bool resultFalse = await target.IsItemInInventoryAsync(new InventoryItemId());

            Assert.IsTrue(resultTrue);
            Assert.IsFalse(resultFalse);
        }
        /// <summary>
        /// Used internally to generate inventory items and adds them to the ReliableDict we have.
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public async Task<bool> CreateInventoryItemAsync(InventoryItem item)
        {
            IReliableDictionary<InventoryItemId, InventoryItem> inventoryItems =
                await this.stateManager.GetOrAddAsync<IReliableDictionary<InventoryItemId, InventoryItem>>(InventoryItemDictionaryName);

            using (ITransaction tx = this.stateManager.CreateTransaction())
            {
                await inventoryItems.AddAsync(tx, item.Id, item);
                await tx.CommitAsync();
                ServiceEventSource.Current.ServiceMessage(this, "Created inventory item: {0}", item);
            }

            return true;
        }
        public async Task TestRemoveStock()
        {
            int expectedQuantity = 5;
            int quantityToRemove = 3;
            MockReliableStateManager stateManager = new MockReliableStateManager();
            InventoryService target = new InventoryService(stateManager);

            InventoryItem item = new InventoryItem("test", 1, expectedQuantity + quantityToRemove, 1, expectedQuantity);

            await target.CreateInventoryItemAsync(item);
            int actualRemoved = await target.RemoveStockAsync(item.Id, quantityToRemove, CustomerOrderActorMessageId.GetRandom());

            Assert.AreEqual(quantityToRemove, actualRemoved);
            Assert.AreEqual(expectedQuantity, item.AvailableStock);
        }
        public async Task TestAddStock()
        {
            int expectedQuantity = 10;
            int quantityToAdd = 3;
            MockReliableStateManager stateManager = new MockReliableStateManager();
            InventoryService target = new InventoryService(statefulServiceContext, stateManager);

            InventoryItem item = new InventoryItem("test", 1, expectedQuantity - quantityToAdd, 1, expectedQuantity);

            RestockRequest.Domain.RestockRequest request = new RestockRequest.Domain.RestockRequest(item.Id, quantityToAdd);

            await target.CreateInventoryItemAsync(item);
            int actualAdded = await target.AddStockAsync(request.ItemId, quantityToAdd);

            Assert.AreEqual(quantityToAdd, actualAdded);
            Assert.AreEqual(item.AvailableStock, expectedQuantity);
        }
        public Task<bool> CreateInventoryItem(string description, decimal price, int number, int reorderThreshold, int max)
        {
            InventoryItem i = new InventoryItem(description, price, number, reorderThreshold, max);

            ServiceUriBuilder builder = new ServiceUriBuilder(InventoryServiceName);
            IInventoryService inventoryServiceClient = ServiceProxy.Create<IInventoryService>(builder.ToUri(), i.Id.GetPartitionKey());

            try
            {
                return inventoryServiceClient.CreateInventoryItemAsync(i);
            }
            catch (Exception ex)
            {
                ServiceEventSource.Current.Message("Web Service: Exception creating {0}: {1}", i, ex);
                throw;
            }
        }
 public Task<bool> CreateInventoryItemAsync(InventoryItem item)
 {
     return this.CreateInventoryItemAsyncFunc(item);
 }
        private async Task PeriodicInventoryCheck(CancellationToken cancellationToken)
        {
            IReliableDictionary<InventoryItemId, InventoryItem> inventoryItems =
                await this.stateManager.GetOrAddAsync<IReliableDictionary<InventoryItemId, InventoryItem>>(InventoryItemDictionaryName);

            while (!cancellationToken.IsCancellationRequested)
            {
                ServiceEventSource.Current.ServiceMessage(this, "Checking inventory stock for {0} items.", await inventoryItems.GetCountAsync());

                foreach (InventoryItem item in inventoryItems.Select(x => x.Value))
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    try
                    {
                        //Check if stock is below restockThreshold and if the item is not already on reorder
                        if ((item.AvailableStock <= item.RestockThreshold) && !item.OnReorder)
                        {
                            ServiceUriBuilder builder = new ServiceUriBuilder(RestockRequestManagerServiceName);

                            IRestockRequestManager restockRequestManagerClient = ServiceProxy.Create<IRestockRequestManager>(0, builder.ToUri());

                            // we reduce the quantity passed in to RestockRequest to ensure we don't overorder   
                            RestockRequest newRequest = new RestockRequest(item.Id, (item.MaxStockThreshold - item.AvailableStock));

                            InventoryItem updatedItem = new InventoryItem(
                                item.Description,
                                item.Price,
                                item.AvailableStock,
                                item.RestockThreshold,
                                item.MaxStockThreshold,
                                item.Id,
                                true);

                            // TODO: this call needs to be idempotent in case we fail to update the InventoryItem after this completes.
                            await restockRequestManagerClient.AddRestockRequestAsync(newRequest);

                            // Write operations take an exclusive lock on an item, which means we can't do anything else with that item while the transaction is open.
                            // If something blocks before the transaction is committed, the open transaction on the item will prevent all operations on it, including reads.
                            // Once the transaction commits, the lock is released and other operations on the item can proceed.
                            // Operations on the transaction all have timeouts to prevent deadlocking an item, 
                            // but we should do as little work inside the transaction as possible that is not related to the transaction itself.
                            using (ITransaction tx = this.stateManager.CreateTransaction())
                            {
                                await inventoryItems.TryUpdateAsync(tx, item.Id, updatedItem, item);

                                await tx.CommitAsync();
                            }

                            ServiceEventSource.Current.ServiceMessage(
                                this,
                                "Restock order placed. Item ID: {0}. Quantity: {1}",
                                newRequest.ItemId,
                                newRequest.Quantity);
                        }
                    }
                    catch (Exception e)
                    {
                        ServiceEventSource.Current.ServiceMessage(this, "Failed to place restock order for item {0}. {1}", item.Id, e.ToString());
                    }
                }

                await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
            }
        }