예제 #1
0
        public async Task TestConcurrentAccountRequestsAsync()
        {
            var logger      = new MockLogger();
            var cosmosState = new MockCosmosState(logger);

            var client = new MockImageGalleryClient(cosmosState, logger);
            await client.InitializeCosmosDbAsync();

            // Try create a new account, and wait for it to be created before proceeding with the test.
            var account = new Account("0", "alice", "*****@*****.**");

            var result = await client.CreateAccountAsync(account);

            Assert.IsTrue(result);

            var updatedAccount = new Account("0", "alice", "*****@*****.**");

            // Try update the account and delete it concurrently, which can cause a data race and a bug.
            var updateTask = client.UpdateAccountAsync(updatedAccount);
            var deleteTask = client.DeleteAccountAsync(updatedAccount.Id);

            // Wait for the two concurrent requests to complete.
            await Task.WhenAll(updateTask, deleteTask);

            // Bug: the update request can nondeterministically fail due to an unhandled exception (500 error code).
            // See the `Update` handler in the account controller for more info.
            _ = updateTask.Result;

            var deleteAccountRes = deleteTask.Result;

            // deleteAccountRes.EnsureSuccessStatusCode();
            Assert.IsTrue(deleteAccountRes);
        }
예제 #2
0
        public async Task TestFirstScenario()
        {
            // Initialize the mock in-memory DB and account manager.
            var cosmosState      = new MockCosmosState();
            var database         = new MockCosmosDatabase(cosmosState);
            var accountContainer = await database.CreateContainerAsync(Constants.AccountContainerName);

            var petImagesClient = new TestPetImagesClient(accountContainer);

            // Create an account request payload
            var account = new Account()
            {
                Name = "MyAccount"
            };

            // Call CreateAccount twice without awaiting, which makes both methods run
            // asynchronously with each other.
            var task1 = petImagesClient.CreateAccountAsync(account);
            var task2 = petImagesClient.CreateAccountAsync(account);

            // Then wait both requests to complete.
            await Task.WhenAll(task1, task2);

            var statusCode1 = task1.Result.StatusCode;
            var statusCode2 = task2.Result.StatusCode;

            // Finally, assert that only one of the two requests succeeded and the other
            // failed. Note that we do not know which one of the two succeeded as the
            // requests ran concurrently (this is why we use an exclusive OR).
            Assert.IsTrue(
                (statusCode1 == HttpStatusCode.OK && statusCode2 == HttpStatusCode.Conflict) ||
                (statusCode1 == HttpStatusCode.Conflict && statusCode2 == HttpStatusCode.OK));
        }
예제 #3
0
        public async Task TestThirdScenario()
        {
            var cosmosState      = new MockCosmosState();
            var database         = new MockCosmosDatabase(cosmosState);
            var accountContainer = (MockCosmosContainer)await database.CreateContainerAsync(Constants.AccountContainerName);

            var imageContainer = (MockCosmosContainer)await database.CreateContainerAsync(Constants.ImageContainerName);

            var blobContainer   = new MockBlobContainerProvider();
            var messagingClient = new MockMessagingClient(blobContainer);

            var petImagesClient = new TestPetImagesClient(accountContainer, imageContainer, blobContainer, messagingClient);

            string accountName = "MyAccount";
            string imageName   = "pet.jpg";

            // Create an account request payload
            var account = new Account()
            {
                Name = accountName
            };

            var accountResult = await petImagesClient.CreateAccountAsync(account);

            Assert.IsTrue(accountResult.StatusCode == HttpStatusCode.OK);

            var task1 = petImagesClient.CreateOrUpdateImageAsync(accountName, new Image()
            {
                Name = imageName, Content = GetDogImageBytes()
            });
            var task2 = petImagesClient.CreateOrUpdateImageAsync(accountName, new Image()
            {
                Name = imageName, Content = GetCatImageBytes()
            });
            await Task.WhenAll(task1, task2);

            Assert.IsTrue(task1.Result.StatusCode == HttpStatusCode.OK);
            Assert.IsTrue(task1.Result.StatusCode == HttpStatusCode.OK);

            var imageResult = await petImagesClient.GetImageAsync(accountName, imageName);

            Assert.IsTrue(imageResult.StatusCode == HttpStatusCode.OK);
            byte[] image = imageResult.Resource;

            byte[] thumbnail;
            while (true)
            {
                var thumbnailResult = await petImagesClient.GetImageThumbnailAsync(accountName, imageName);

                if (thumbnailResult.StatusCode == HttpStatusCode.OK)
                {
                    thumbnail = thumbnailResult.Resource;
                    break;
                }
            }

            Assert.IsTrue(
                (IsDogImage(image) && IsDogThumbnail(thumbnail)) ||
                (IsCatImage(image) && IsCatThumbnail(thumbnail)));
        }
예제 #4
0
        public async Task TestSecondScenario()
        {
            var cosmosState      = new MockCosmosState();
            var database         = new MockCosmosDatabase(cosmosState);
            var accountContainer = (MockCosmosContainer)await database.CreateContainerAsync(Constants.AccountContainerName);

            var imageContainer = (MockCosmosContainer)await database.CreateContainerAsync(Constants.ImageContainerName);

            var blobContainer   = new MockBlobContainerProvider();
            var messagingClient = new MockMessagingClient(blobContainer);

            var petImagesClient = new TestPetImagesClient(accountContainer, imageContainer, blobContainer, messagingClient);

            string accountName = "MyAccount";
            string imageName   = "pet.jpg";

            // Create an account request payload
            var account = new Account()
            {
                Name = accountName
            };

            var accountResult = await petImagesClient.CreateAccountAsync(account);

            Assert.IsTrue(accountResult.StatusCode == HttpStatusCode.OK);

            imageContainer.EnableRandomizedFaults();

            var task1 = petImagesClient.CreateImageAsync(accountName, new Image()
            {
                Name = imageName, Content = GetDogImageBytes()
            });
            var task2 = petImagesClient.CreateImageAsync(accountName, new Image()
            {
                Name = imageName, Content = GetDogImageBytes()
            });
            await Task.WhenAll(task1, task2);

            var statusCode1 = task1.Result.StatusCode;
            var statusCode2 = task2.Result.StatusCode;

            imageContainer.DisableRandomizedFaults();

            Assert.IsTrue(statusCode1 == HttpStatusCode.OK || statusCode1 == HttpStatusCode.Conflict || statusCode1 == HttpStatusCode.ServiceUnavailable);
            Assert.IsTrue(statusCode2 == HttpStatusCode.OK || statusCode2 == HttpStatusCode.Conflict || statusCode2 == HttpStatusCode.ServiceUnavailable);

            if (task1.Result.StatusCode == HttpStatusCode.OK || task2.Result.StatusCode == HttpStatusCode.OK)
            {
                var imageContentResult = await petImagesClient.GetImageAsync(accountName, imageName);

                Assert.IsTrue(imageContentResult.StatusCode == HttpStatusCode.OK);
                Assert.IsTrue(IsDogImage(imageContentResult.Resource));
            }
        }
예제 #5
0
        public async Task TestConcurrentAccountAndImageRequestsAsync()
        {
            var logger      = new MockLogger();
            var cosmosState = new MockCosmosState(logger);

            var client = new MockImageGalleryClient(cosmosState, logger);
            IDatabaseProvider databaseProvider = await client.InitializeCosmosDbAsync();

            // Try create a new account, and wait for it to be created before proceeding with the test.
            var account = new Account("0", "alice", "*****@*****.**");
            await client.CreateAccountAsync(account);

            // Try store the image and delete the account concurrently, which can cause a data race and a bug.
            var image             = new Image(account.Id, "beach", Encoding.Default.GetBytes("waves"));
            var storeImageTask    = client.CreateOrUpdateImageAsync(image);
            var deleteAccountTask = client.DeleteAccountAsync(account.Id);

            // Wait for the two concurrent requests to complete.
            await Task.WhenAll(storeImageTask, deleteAccountTask);

            // BUG: The above two concurrent requests can race and result into the image being stored
            // in an "orphan" container in Azure Storage, even if the associated account was deleted.

            // Check that the image was deleted from Azure Storage.
            var exists = await client.AzureStorageProvider.ExistsBlobAsync(Constants.GetContainerName(account.Id), image.Name);

            if (exists)
            {
                throw new AssertFailedException("The image was not deleted from Azure Blob Storage.");
            }

            // Check that the account was deleted from Cosmos DB.
            var accountContainer = databaseProvider.GetContainer(Constants.AccountCollectionName);

            exists = await accountContainer.ExistsItemAsync <AccountEntity>(account.Id, account.Id);

            if (exists)
            {
                throw new AssertFailedException("The account was not deleted from Cosmos DB.");
            }
        }