public async Task Posting_command_JSON_applies_a_command_with_the_specified_name_to_an_aggregate_with_the_specified_id() { var order = new Order(Guid.NewGuid()) .Apply(new ChangeCustomerInfo { CustomerName = "Joe" }) .SavedToEventStore(); var json = new AddItem { Quantity = 5, Price = 19.99m, ProductName = "Bag o' Treats" }.ToJson(); var request = new HttpRequestMessage(HttpMethod.Post, $"http://contoso.com/orders/{order.Id}/additem") { Content = new StringContent(json, Encoding.UTF8, "application/json") }; var testApi = new TestApi <Order>(); var client = testApi.GetClient(); var response = client.SendAsync(request).Result; response.StatusCode.Should().Be(HttpStatusCode.OK); var updatedOrder = await Configuration.Current.Repository <Order>().GetLatest(order.Id); updatedOrder.Items.Single().Quantity.Should().Be(5); }
public async Task Posting_a_second_constructor_command_with_the_same_aggregate_id_results_in_a_409_Conflict() { // arrange var testApi = new TestApi <Order>(); var orderId = Any.Guid(); await testApi.GetClient() .PostAsJsonAsync(string.Format("http://contoso.com/orders/createorder/{0}", orderId), new CreateOrder(Any.FullName())); // act var response = await testApi.GetClient() .PostAsJsonAsync(string.Format("http://contoso.com/orders/createorder/{0}", orderId), new CreateOrder(Any.FullName())); // assert response.ShouldFailWith(HttpStatusCode.Conflict); }
public async Task Posting_command_JSON_applies_a_command_with_the_specified_name_to_an_aggregate_with_the_specified_id() { var order = new Order(Guid.NewGuid()) .Apply(new ChangeCustomerInfo { CustomerName = "Joe" }) .SavedToEventStore(); var json = new AddItem { Quantity = 5, Price = 19.99m, ProductName = "Bag o' Treats" }.ToJson(); var request = new HttpRequestMessage(HttpMethod.Post, $"http://contoso.com/orders/{order.Id}/additem") { Content = new StringContent(json, Encoding.UTF8, "application/json") }; var testApi = new TestApi<Order>(); var client = testApi.GetClient(); var response = client.SendAsync(request).Result; response.StatusCode.Should().Be(HttpStatusCode.OK); var updatedOrder = await Configuration.Current.Repository<Order>().GetLatest(order.Id); updatedOrder.Items.Single().Quantity.Should().Be(5); }
public async Task Posting_an_invalid_command_does_not_affect_the_aggregate_state() { var order = new Order(Guid.NewGuid(), new Order.CustomerInfoChanged { CustomerName = "Joe" }, new Order.Fulfilled()); await order.SaveToEventStore(); var json = new AddItem { Quantity = 5, Price = 19.99m, ProductName = "Bag o' Treats" }.ToJson(); var request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://contoso.com/orders/{0}/additem", order.Id)) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; var testApi = new TestApi <Order>(); var client = testApi.GetClient(); var response = client.SendAsync(request).Result; response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var updatedOrder = await new SqlEventSourcedRepository <Order>().GetLatest(order.Id); updatedOrder.Items.Count().Should().Be(0); }
public async Task Posting_an_invalid_create_command_returns_400_Bad_request() { var testApi = new TestApi <Order>(); var response = await testApi.GetClient() .PostAsJsonAsync(string.Format("http://contoso.com/orders/createorder/{0}", Any.Guid()), new object()); response.ShouldFailWith(HttpStatusCode.BadRequest); }
public async Task Posting_a_constructor_command_creates_a_new_aggregate_instance() { var testApi = new TestApi <Order>(); var orderId = Any.Guid(); var response = await testApi.GetClient() .PostAsJsonAsync(string.Format("http://contoso.com/orders/createorder/{0}", orderId), new CreateOrder(Any.FullName())); response.ShouldSucceed(HttpStatusCode.Created); }
public async Task ApplyBatch_can_accept_an_array_of_commands() { var repository = new SqlEventSourcedRepository <Order>(new FakeEventBus()); var order = new Order(); await repository.Save(order); var json = new[] { new { AddItem = new { Quantity = 1, Price = 1, ProductName = "Sprocket" } }, new { AddItem = new { Quantity = 1, Price = 2, ProductName = "Cog" } } }.ToJson(); var testApi = new TestApi <Order>(); var client = testApi.GetClient(); var request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://contoso.com/orders/{0}", order.Id)) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; var response = await client.SendAsync(request); response.ShouldSucceed(); order = await repository.GetLatest(order.Id); order.Items.Count.Should().Be(2); order.Balance.Should().Be(3); }
public async Task Posting_a_command_that_causes_a_concurrency_error_returns_409_Conflict() { var order = new Order(Guid.NewGuid(), new Order.CustomerInfoChanged { CustomerName = "Joe" }); var testApi = new TestApi<Order>(); var repository = new Mock<IEventSourcedRepository<Order>>(); repository.Setup(r => r.GetLatest(It.IsAny<Guid>())) .Returns(order); repository.Setup(r => r.Save(It.IsAny<Order>())) .Throws(new ConcurrencyException("oops!", new IEvent[0], new Exception("inner oops"))); testApi.Container.Register(c => repository.Object); var client = testApi.GetClient(); var response = await client.PostAsJsonAsync(string.Format("http://contoso.com/orders/{0}/additem", order.Id), new { Price = 3m, ProductName = Any.Word() }); response.ShouldFailWith(HttpStatusCode.Conflict); }
public async Task An_ETag_header_is_applied_to_the_command() { var order = new Order(Guid.NewGuid(), new Order.CustomerInfoChanged { CustomerName = "Joe" }); await order.SaveToEventStore(); var json = new AddItem { Quantity = 5, Price = 19.99m, ProductName = "Bag o' Treats" }.ToJson(); var etag = new EntityTagHeaderValue("\"" + Any.Guid() + "\""); Func <HttpRequestMessage> createRequest = () => { var request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://contoso.com/orders/{0}/additem", order.Id)) { Content = new StringContent(json, Encoding.UTF8, "application/json"), }; request.Headers.IfNoneMatch.Add(etag); return(request); }; var testApi = new TestApi <Order>(); var client = testApi.GetClient(); // act: send the request twice var response1 = await client.SendAsync(createRequest()); var response2 = await client.SendAsync(createRequest()); // assert response1.ShouldSucceed(HttpStatusCode.OK); response2.ShouldFailWith(HttpStatusCode.NotModified); var updatedOrder = await new SqlEventSourcedRepository <Order>().GetLatest(order.Id); updatedOrder.Items.Single().Quantity.Should().Be(5); }
public async Task ApplyBatch_can_accept_an_array_of_commands() { var order = new Order().SavedToEventStore(); var json = new[] { new { AddItem = new { Quantity = 1, Price = 1, ProductName = "Sprocket" } }, new { AddItem = new { Quantity = 1, Price = 2, ProductName = "Cog" } } }.ToJson(); var testApi = new TestApi<Order>(); var client = testApi.GetClient(); var request = new HttpRequestMessage(HttpMethod.Post, $"http://contoso.com/orders/{order.Id}") { Content = new StringContent(json, Encoding.UTF8, "application/json") }; var response = await client.SendAsync(request); response.ShouldSucceed(); order = await Configuration.Current.Repository<Order>().GetLatest(order.Id); order.Items.Count.Should().Be(2); order.Balance.Should().Be(3); }
public async Task Posting_a_command_that_causes_a_concurrency_error_returns_409_Conflict() { var order = new Order(Guid.NewGuid()) .Apply(new ChangeCustomerInfo { CustomerName = "Joe" }) .SavedToEventStore(); var testApi = new TestApi <Order>(); var repository = new Mock <IEventSourcedRepository <Order> >(); repository.Setup(r => r.GetLatest(It.IsAny <Guid>())) .Returns(Task.FromResult(order)); repository.Setup(r => r.Save(It.IsAny <Order>())) .Throws(new ConcurrencyException("oops!", new IEvent[0], new Exception("inner oops"))); Configuration.Current.UseDependency(c => repository.Object); var client = testApi.GetClient(); var response = await client.PostAsJsonAsync($"http://contoso.com/orders/{order.Id}/additem", new { Price = 3m, ProductName = Any.Word() }); response.ShouldFailWith(HttpStatusCode.Conflict); }
public async Task Posting_an_invalid_create_command_returns_400_Bad_request() { var testApi = new TestApi<Order>(); var response = await testApi.GetClient() .PostAsJsonAsync(string.Format("http://contoso.com/orders/createorder/{0}", Any.Guid()), new object()); response.ShouldFailWith(HttpStatusCode.BadRequest); }
public async Task Posting_an_invalid_command_does_not_affect_the_aggregate_state() { var order = new Order(Guid.NewGuid(), new Order.CustomerInfoChanged { CustomerName = "Joe" }, new Order.Fulfilled()); await order.SaveToEventStore(); var json = new AddItem { Quantity = 5, Price = 19.99m, ProductName = "Bag o' Treats" }.ToJson(); var request = new HttpRequestMessage(HttpMethod.Post, string.Format("http://contoso.com/orders/{0}/additem", order.Id)) { Content = new StringContent(json, Encoding.UTF8, "application/json") }; var testApi = new TestApi<Order>(); var client = testApi.GetClient(); var response = client.SendAsync(request).Result; response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var updatedOrder = await new SqlEventSourcedRepository<Order>().GetLatest(order.Id); updatedOrder.Items.Count().Should().Be(0); }
public async Task Posting_a_command_that_causes_a_concurrency_error_returns_409_Conflict() { var order = new Order(Guid.NewGuid()) .Apply(new ChangeCustomerInfo { CustomerName = "Joe" }) .SavedToEventStore(); var testApi = new TestApi<Order>(); var repository = new Mock<IEventSourcedRepository<Order>>(); repository.Setup(r => r.GetLatest(It.IsAny<Guid>())) .Returns(Task.FromResult(order)); repository.Setup(r => r.Save(It.IsAny<Order>())) .Throws(new ConcurrencyException("oops!", new IEvent[0], new Exception("inner oops"))); Configuration.Current.UseDependency(c => repository.Object); var client = testApi.GetClient(); var response = await client.PostAsJsonAsync($"http://contoso.com/orders/{order.Id}/additem", new { Price = 3m, ProductName = Any.Word() }); response.ShouldFailWith(HttpStatusCode.Conflict); }
public async Task Posting_a_constructor_command_creates_a_new_aggregate_instance() { var testApi = new TestApi<Order>(); var orderId = Any.Guid(); var response = await testApi.GetClient() .PostAsJsonAsync($"http://contoso.com/orders/createorder/{orderId}", new CreateOrder(Any.FullName())); response.ShouldSucceed(HttpStatusCode.Created); }
public async Task Posting_a_second_constructor_command_with_the_same_aggregate_id_results_in_a_409_Conflict() { // arrange var testApi = new TestApi<Order>(); var orderId = Any.Guid(); await testApi.GetClient() .PostAsJsonAsync($"http://contoso.com/orders/createorder/{orderId}", new CreateOrder(Any.FullName())); // act var response = await testApi.GetClient() .PostAsJsonAsync($"http://contoso.com/orders/createorder/{orderId}", new CreateOrder(Any.FullName())); // assert response.ShouldFailWith(HttpStatusCode.Conflict); }
public async Task An_ETag_header_is_applied_to_the_command() { var order = new Order(Guid.NewGuid()) .Apply(new ChangeCustomerInfo { CustomerName = "Joe" }) .SavedToEventStore(); var json = new AddItem { Quantity = 5, Price = 19.99m, ProductName = "Bag o' Treats" }.ToJson(); var etag = new EntityTagHeaderValue("\"" + Any.Guid() + "\""); Func<HttpRequestMessage> createRequest = () => { var request = new HttpRequestMessage(HttpMethod.Post, $"http://contoso.com/orders/{order.Id}/additem") { Content = new StringContent(json, Encoding.UTF8, "application/json") }; request.Headers.IfNoneMatch.Add(etag); return request; }; var testApi = new TestApi<Order>(); var client = testApi.GetClient(); // act: send the request twice var response1 = await client.SendAsync(createRequest()); var response2 = await client.SendAsync(createRequest()); // assert response1.ShouldSucceed(HttpStatusCode.OK); response2.ShouldFailWith(HttpStatusCode.NotModified); var updatedOrder = await Configuration.Current.Repository<Order>().GetLatest(order.Id); updatedOrder.Items.Single().Quantity.Should().Be(5); }