diff --git a/.gitignore b/.gitignore
index b335d4e..fa989d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -245,6 +245,5 @@ ModelManifest.xml
.fake/
/src/.idea
-tests/
.vscode/
.idea/
\ No newline at end of file
diff --git a/Chapter13/Marketplace.sln b/Chapter13/Marketplace.sln
index b725549..2ab4579 100644
--- a/Chapter13/Marketplace.sln
+++ b/Chapter13/Marketplace.sln
@@ -4,8 +4,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace", "src\Marketpl
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace.Ads.Domain", "src\Marketplace.Ads.Domain\Marketplace.Ads.Domain.csproj", "{AD477131-DFD9-4CF1-A42F-7B932148B4AD}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace.Ads.Tests", "tests\Marketplace.Ads.Tests\Marketplace.Ads.Tests.csproj", "{F682B8F9-A43B-4D6D-BECE-4F45767A026A}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace.EventSourcing", "src\Marketplace.EventSourcing\Marketplace.EventSourcing.csproj", "{FEE71FF8-559C-40CC-9AF5-1E4051EE9C7C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace.PaidServices.Domain", "src\Marketplace.PaidServices.Domain\Marketplace.PaidServices.Domain.csproj", "{5B72EA27-0DE0-45C9-80C1-17A3F30DB6E5}"
@@ -34,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace.Users.Messages"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace.WebApi", "src\Marketplace.WebApi\Marketplace.WebApi.csproj", "{2ED6B97C-4C4B-4A35-8CF3-57B3BD8F8D2B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Marketplace.Ads.Tests", "tests\Marketplace.Ads.Tests\Marketplace.Ads.Tests.csproj", "{513B7F24-8DB2-4D18-AF8E-3707F4D75E5C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,10 +48,6 @@ Global
{AD477131-DFD9-4CF1-A42F-7B932148B4AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD477131-DFD9-4CF1-A42F-7B932148B4AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD477131-DFD9-4CF1-A42F-7B932148B4AD}.Release|Any CPU.Build.0 = Release|Any CPU
- {F682B8F9-A43B-4D6D-BECE-4F45767A026A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F682B8F9-A43B-4D6D-BECE-4F45767A026A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F682B8F9-A43B-4D6D-BECE-4F45767A026A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F682B8F9-A43B-4D6D-BECE-4F45767A026A}.Release|Any CPU.Build.0 = Release|Any CPU
{FEE71FF8-559C-40CC-9AF5-1E4051EE9C7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FEE71FF8-559C-40CC-9AF5-1E4051EE9C7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEE71FF8-559C-40CC-9AF5-1E4051EE9C7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -100,12 +96,16 @@ Global
{2ED6B97C-4C4B-4A35-8CF3-57B3BD8F8D2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2ED6B97C-4C4B-4A35-8CF3-57B3BD8F8D2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2ED6B97C-4C4B-4A35-8CF3-57B3BD8F8D2B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {513B7F24-8DB2-4D18-AF8E-3707F4D75E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {513B7F24-8DB2-4D18-AF8E-3707F4D75E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {513B7F24-8DB2-4D18-AF8E-3707F4D75E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {513B7F24-8DB2-4D18-AF8E-3707F4D75E5C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {F682B8F9-A43B-4D6D-BECE-4F45767A026A} = {11ACA044-780C-4C71-A6F7-ECFB6B26B9E7}
{FEE71FF8-559C-40CC-9AF5-1E4051EE9C7C} = {B8B2A008-6E95-4BEE-9BE3-DC621B4DC4EC}
{EA7B5B15-9095-4082-ADC3-E367513C54B6} = {B8B2A008-6E95-4BEE-9BE3-DC621B4DC4EC}
{D2D334CA-B388-47EB-848A-4345A4E0E221} = {B8B2A008-6E95-4BEE-9BE3-DC621B4DC4EC}
{2ED6B97C-4C4B-4A35-8CF3-57B3BD8F8D2B} = {B8B2A008-6E95-4BEE-9BE3-DC621B4DC4EC}
+ {513B7F24-8DB2-4D18-AF8E-3707F4D75E5C} = {11ACA044-780C-4C71-A6F7-ECFB6B26B9E7}
EndGlobalSection
EndGlobal
diff --git a/Chapter13/src/Marketplace.Users/Marketplace.Users.csproj b/Chapter13/src/Marketplace.Users/Marketplace.Users.csproj
index 221e89c..8d9600c 100644
--- a/Chapter13/src/Marketplace.Users/Marketplace.Users.csproj
+++ b/Chapter13/src/Marketplace.Users/Marketplace.Users.csproj
@@ -6,6 +6,7 @@
+
diff --git a/Chapter13/tests/Marketplace.Ads.Tests/ClassifiedAd_Publish_Spec.cs b/Chapter13/tests/Marketplace.Ads.Tests/ClassifiedAd_Publish_Spec.cs
new file mode 100644
index 0000000..232d12f
--- /dev/null
+++ b/Chapter13/tests/Marketplace.Ads.Tests/ClassifiedAd_Publish_Spec.cs
@@ -0,0 +1,101 @@
+using System;
+using Marketplace.Ads.Domain.ClassifiedAds;
+using Marketplace.Ads.Domain.Shared;
+using Xunit;
+
+namespace Marketplace.ClassifiedAds.Tests
+{
+ public class ClassifiedAd_Publish_Spec
+ {
+ public ClassifiedAd_Publish_Spec()
+ => Ad = ClassifiedAd.Create(
+ ClassifiedAdId.FromGuid(Guid.NewGuid()),
+ UserId.FromGuid(Guid.NewGuid())
+ );
+
+ ClassifiedAd Ad { get; }
+
+ [Fact]
+ public void Can_publish_a_valid_ad()
+ {
+ Ad.SetTitle(ClassifiedAdTitle.FromString("Test ad"));
+
+ Ad.UpdateText(
+ ClassifiedAdText.FromString("Please buy my stuff")
+ );
+
+ Ad.UpdatePrice(
+ Price.FromDecimal(100.10m, "EUR", new FakeCurrencyLookup())
+ );
+
+ Ad.RequestToPublish();
+
+ Assert.Equal(
+ ClassifiedAd.ClassifiedAdState.PendingReview,
+ Ad.State
+ );
+ }
+
+ [Fact]
+ public void Cannot_publish_with_zero_price()
+ {
+ Ad.SetTitle(ClassifiedAdTitle.FromString("Test ad"));
+
+ Ad.UpdateText(
+ ClassifiedAdText.FromString("Please buy my stuff")
+ );
+
+ Ad.UpdatePrice(
+ Price.FromDecimal(0.0m, "EUR", new FakeCurrencyLookup())
+ );
+
+ Assert.Throws(
+ () => Ad.RequestToPublish()
+ );
+ }
+
+ [Fact]
+ public void Cannot_publish_without_price()
+ {
+ Ad.SetTitle(ClassifiedAdTitle.FromString("Test ad"));
+
+ Ad.UpdateText(
+ ClassifiedAdText.FromString("Please buy my stuff")
+ );
+
+ Assert.Throws(
+ () => Ad.RequestToPublish()
+ );
+ }
+
+ [Fact]
+ public void Cannot_publish_without_text()
+ {
+ Ad.SetTitle(ClassifiedAdTitle.FromString("Test ad"));
+
+ Ad.UpdatePrice(
+ Price.FromDecimal(100.10m, "EUR", new FakeCurrencyLookup())
+ );
+
+ Assert.Throws(
+ () => Ad.RequestToPublish()
+ );
+ }
+
+ [Fact]
+ public void Cannot_publish_without_title()
+ {
+ Ad.UpdateText(
+ ClassifiedAdText.FromString("Please buy my stuff")
+ );
+
+ Ad.UpdatePrice(
+ Price.FromDecimal(100.10m, "EUR", new FakeCurrencyLookup())
+ );
+
+ Assert.Throws(
+ () => Ad.RequestToPublish()
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter13/tests/Marketplace.Ads.Tests/FakeCurrencyLookup.cs b/Chapter13/tests/Marketplace.Ads.Tests/FakeCurrencyLookup.cs
new file mode 100644
index 0000000..6708ced
--- /dev/null
+++ b/Chapter13/tests/Marketplace.Ads.Tests/FakeCurrencyLookup.cs
@@ -0,0 +1,44 @@
+using System.Collections.Generic;
+using System.Linq;
+using Marketplace.Ads.Domain.Shared;
+
+namespace Marketplace.ClassifiedAds.Tests
+{
+ public class FakeCurrencyLookup : ICurrencyLookup
+ {
+ static readonly IEnumerable _currencies =
+ new[]
+ {
+ new Currency
+ {
+ CurrencyCode = "EUR",
+ DecimalPlaces = 2,
+ InUse = true
+ },
+ new Currency
+ {
+ CurrencyCode = "USD",
+ DecimalPlaces = 2,
+ InUse = true
+ },
+ new Currency
+ {
+ CurrencyCode = "JPY",
+ DecimalPlaces = 0,
+ InUse = true
+ },
+ new Currency
+ {
+ CurrencyCode = "DEM",
+ DecimalPlaces = 2,
+ InUse = false
+ }
+ };
+
+ public Currency FindCurrency(string currencyCode)
+ {
+ var currency = _currencies.FirstOrDefault(x => x.CurrencyCode == currencyCode);
+ return currency ?? Currency.None;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Chapter13/tests/Marketplace.Ads.Tests/Marketplace.Ads.Tests.csproj b/Chapter13/tests/Marketplace.Ads.Tests/Marketplace.Ads.Tests.csproj
new file mode 100644
index 0000000..68bdf67
--- /dev/null
+++ b/Chapter13/tests/Marketplace.Ads.Tests/Marketplace.Ads.Tests.csproj
@@ -0,0 +1,18 @@
+
+
+ netcoreapp2.2
+ latest
+ false
+ Marketplace.ClassifiedAds.Tests
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Chapter13/tests/Marketplace.Ads.Tests/Money_Spec.cs b/Chapter13/tests/Marketplace.Ads.Tests/Money_Spec.cs
new file mode 100644
index 0000000..8d90242
--- /dev/null
+++ b/Chapter13/tests/Marketplace.Ads.Tests/Money_Spec.cs
@@ -0,0 +1,96 @@
+using System;
+using Marketplace.Ads.Domain.Shared;
+using Xunit;
+
+namespace Marketplace.ClassifiedAds.Tests
+{
+ public class Money_Spec
+ {
+ static readonly ICurrencyLookup CurrencyLookup =
+ new FakeCurrencyLookup();
+
+ [Fact]
+ public void FromString_and_FromDecimal_should_be_equal()
+ {
+ var firstAmount = Money.FromDecimal(5, "EUR", CurrencyLookup);
+ var secondAmount = Money.FromString("5.00", "EUR", CurrencyLookup);
+
+ Assert.Equal(firstAmount, secondAmount);
+ }
+
+ [Fact]
+ public void Sum_of_money_gives_full_amount()
+ {
+ var coin1 = Money.FromDecimal(1, "EUR", CurrencyLookup);
+ var coin2 = Money.FromDecimal(2, "EUR", CurrencyLookup);
+ var coin3 = Money.FromDecimal(2, "EUR", CurrencyLookup);
+
+ var banknote = Money.FromDecimal(5, "EUR", CurrencyLookup);
+
+ Assert.Equal(banknote, coin1 + coin2 + coin3);
+ }
+
+ [Fact]
+ public void Throw_when_too_many_decimal_places()
+ => Assert.Throws(
+ () =>
+ Money.FromDecimal(100.123m, "EUR", CurrencyLookup)
+ );
+
+ [Fact]
+ public void Throws_on_adding_different_currencies()
+ {
+ var firstAmount = Money.FromDecimal(5, "USD", CurrencyLookup);
+ var secondAmount = Money.FromDecimal(5, "EUR", CurrencyLookup);
+
+ Assert.Throws(
+ () =>
+ firstAmount + secondAmount
+ );
+ }
+
+ [Fact]
+ public void Throws_on_substracting_different_currencies()
+ {
+ var firstAmount = Money.FromDecimal(5, "USD", CurrencyLookup);
+ var secondAmount = Money.FromDecimal(5, "EUR", CurrencyLookup);
+
+ Assert.Throws(
+ () =>
+ firstAmount - secondAmount
+ );
+ }
+
+ [Fact]
+ public void Two_of_same_amount_but_different_Currencies_should_not_be_equal()
+ {
+ var firstAmount = Money.FromDecimal(5, "EUR", CurrencyLookup);
+ var secondAmount = Money.FromDecimal(5, "USD", CurrencyLookup);
+
+ Assert.NotEqual(firstAmount, secondAmount);
+ }
+
+ [Fact]
+ public void Two_of_same_amount_should_be_equal()
+ {
+ var firstAmount = Money.FromDecimal(5, "EUR", CurrencyLookup);
+ var secondAmount = Money.FromDecimal(5, "EUR", CurrencyLookup);
+
+ Assert.Equal(firstAmount, secondAmount);
+ }
+
+ [Fact]
+ public void Unknown_currency_should_not_be_allowed()
+ => Assert.Throws(
+ () =>
+ Money.FromDecimal(100, "WHAT?", CurrencyLookup)
+ );
+
+ [Fact]
+ public void Unused_currency_should_not_be_allowed()
+ => Assert.Throws(
+ () =>
+ Money.FromDecimal(100, "DEM", CurrencyLookup)
+ );
+ }
+}
\ No newline at end of file