diff --git a/csharpcore/GildedRose/AbstractItemBuilder.cs b/csharpcore/GildedRose/AbstractItemBuilder.cs
new file mode 100644
index 00000000..82d2fab2
--- /dev/null
+++ b/csharpcore/GildedRose/AbstractItemBuilder.cs
@@ -0,0 +1,13 @@
+namespace GildedRoseKata;
+
+public abstract class AbstractItemBuilder(string name, int sellIn, int quality)
+{
+ protected readonly string Name = name;
+ protected readonly int SellIn = sellIn;
+ protected readonly int Quality = quality;
+
+ public virtual Item Build()
+ {
+ return new() { Name = Name, SellIn = SellIn, Quality = Quality };
+ }
+}
\ No newline at end of file
diff --git a/csharpcore/GildedRose/ItemBuilder.cs b/csharpcore/GildedRose/ItemBuilder.cs
new file mode 100644
index 00000000..720058a7
--- /dev/null
+++ b/csharpcore/GildedRose/ItemBuilder.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace GildedRoseKata;
+
+public class ItemBuilder(string name, int sellIn, int quality) : AbstractItemBuilder(name, sellIn, quality)
+{
+ public override Item Build()
+ {
+ if (Quality < ItemQuality.MinQuality || Quality > ItemQuality.MaxQuality)
+ {
+ throw new ArgumentException("An item's quality must be >=0 and <=50");
+ }
+
+ if (ItemType.IsLegendaryItem(Name))
+ {
+ throw new ArgumentException("Legendary Items cannot be constructed using this builder");
+ }
+
+ return base.Build();
+ }
+}
+
+
diff --git a/csharpcore/GildedRose/LegendaryItemBuilder.cs b/csharpcore/GildedRose/LegendaryItemBuilder.cs
new file mode 100644
index 00000000..456f3d51
--- /dev/null
+++ b/csharpcore/GildedRose/LegendaryItemBuilder.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace GildedRoseKata;
+
+public class LegendaryItemBuilder(string name, int sellIn)
+ : AbstractItemBuilder(name, sellIn, ItemQuality.LegendaryItemQuality)
+{
+ public override Item Build()
+ {
+ if (!ItemType.IsLegendaryItem(Name))
+ {
+ throw new ArgumentException("Only Legendary items can be built using this builder");
+ }
+
+ return base.Build();
+ }
+}
\ No newline at end of file
diff --git a/csharpcore/GildedRose/RefactoringDiary.md b/csharpcore/GildedRose/RefactoringDiary.md
index 12efe822..954d10f9 100644
--- a/csharpcore/GildedRose/RefactoringDiary.md
+++ b/csharpcore/GildedRose/RefactoringDiary.md
@@ -25,6 +25,8 @@ However, I've chosen to use the factory itself to manually manage it using a sem
11. Moved ItemType and ItemQuality to their own classes to allow re-use
12. Seperated the derived DailyUpdater classes into their own files.
Now, adding a new updater won't have to change the existing DailyUpdater file.
+13. Added a new ItemBuilders hierarchy to enforce building valid items (quality limits and special rules for Legendary items). Also added unit tests and refactored the existing tests to use the new builders.
+note: if we could change Item, this could have been implemented in the Item's constructor itself and by using Quality TinyType. However, since we can't change Item, the builders gives us a nice decoupled way to validate the items are constructed consistently.
diff --git a/csharpcore/GildedRoseTests/ItemBuilderTestFixture.cs b/csharpcore/GildedRoseTests/ItemBuilderTestFixture.cs
new file mode 100644
index 00000000..67d85f56
--- /dev/null
+++ b/csharpcore/GildedRoseTests/ItemBuilderTestFixture.cs
@@ -0,0 +1,40 @@
+using System;
+using FluentAssertions;
+using GildedRoseKata;
+using NUnit.Framework;
+
+namespace GildedRoseTests;
+
+public class ItemBuilderTestFixture
+{
+ [TestCase("Aged Brie", 5, 0)]
+ [TestCase("Elixir of the Mongoose", 0, 5)]
+ [TestCase("Backstage passes to a TAFKAL80ETC concert", -5, 39)]
+ public void WhenGivenValidParameters_ShouldBuildExpectedItemSuccessfully(string name, int sellIn,
+ int quality)
+ {
+ var item = new ItemBuilder(name, sellIn, quality).Build();
+ item.Name.Should().Be(name);
+ item.SellIn.Should().Be(sellIn);
+ item.Quality.Should().Be(quality);
+ }
+
+
+ [TestCase("Aged Brie", 5, -1)]
+ [TestCase("Elixir of the Mongoose", 0, 51)]
+ public void WhenGivenInvalidQuality_ShouldThrow(string name, int sellIn, int quality)
+ {
+ var action = () => new ItemBuilder(name, sellIn, quality).Build();
+ action.Should().Throw().WithMessage("An item's quality must be >=0 and <=50");
+ }
+
+
+ [TestCase("Sulfuras, Hand of Ragnaros", 3, 3)]
+ [TestCase("something sulfuras", 3, 3)]
+ public void WhenGivenLegendaryItem_ShouldThrow(string name, int sellIn, int quality)
+ {
+ var action = () => new ItemBuilder(name, sellIn, quality).Build();
+ action.Should().Throw().WithMessage("Legendary Items cannot be constructed using this builder");
+ }
+
+}
\ No newline at end of file
diff --git a/csharpcore/GildedRoseTests/LegendaryItemBuilderTestFixture.cs b/csharpcore/GildedRoseTests/LegendaryItemBuilderTestFixture.cs
new file mode 100644
index 00000000..12cfb23a
--- /dev/null
+++ b/csharpcore/GildedRoseTests/LegendaryItemBuilderTestFixture.cs
@@ -0,0 +1,32 @@
+using System;
+using FluentAssertions;
+using GildedRoseKata;
+using NUnit.Framework;
+
+namespace GildedRoseTests;
+
+public class LegendaryItemBuilderTestFixture
+{
+
+ [TestCase("Aged Brie", 3)]
+ [TestCase("Elixir of the Mongoose", 0)]
+ [TestCase("Backstage passes to a TAFKAL80ETC concert", -3)]
+ public void WhenGivenOtherItemTypes_ShouldThrow(string name, int sellIn)
+ {
+ var action = () => new LegendaryItemBuilder(name, sellIn).Build();
+ action.Should().Throw().WithMessage("Only Legendary items can be built using this builder");
+ }
+
+
+ [TestCase("Sulfuras, Hand of Ragnaros", 3)]
+ [TestCase("Sulfuras, Hand of Ragnaros", 0)]
+ [TestCase("something sulfuras", -3)]
+ public void WhenGivenValidParameters_ShouldBuildExpectedItemSuccessfully(string name, int sellIn)
+ {
+ var item = new LegendaryItemBuilder(name, sellIn).Build();
+ item.Name.Should().Be(name);
+ item.SellIn.Should().Be(sellIn);
+ item.Quality.Should().Be(ItemQuality.LegendaryItemQuality);
+ }
+
+}
\ No newline at end of file
diff --git a/csharpcore/GildedRoseTests/TextTestFixture.cs b/csharpcore/GildedRoseTests/TextTestFixture.cs
index 89c48e79..c87b9608 100644
--- a/csharpcore/GildedRoseTests/TextTestFixture.cs
+++ b/csharpcore/GildedRoseTests/TextTestFixture.cs
@@ -11,31 +11,25 @@ public static class TextTestFixture
Console.WriteLine("OMGHAI!");
var items = new List- {
- new Item {Name = "+5 Dexterity Vest", SellIn = 10, Quality = 20},
- new Item {Name = "Aged Brie", SellIn = 2, Quality = 0},
- new Item {Name = "Elixir of the Mongoose", SellIn = 5, Quality = 7},
- new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = 0, Quality = 80},
- new Item {Name = "Sulfuras, Hand of Ragnaros", SellIn = -1, Quality = 80},
- new Item
- {
- Name = "Backstage passes to a TAFKAL80ETC concert",
- SellIn = 15,
- Quality = 20
- },
- new Item
- {
- Name = "Backstage passes to a TAFKAL80ETC concert",
- SellIn = 10,
- Quality = 49
- },
- new Item
- {
- Name = "Backstage passes to a TAFKAL80ETC concert",
- SellIn = 5,
- Quality = 49
- },
+ new ItemBuilder("+5 Dexterity Vest", 10,20).Build(),
+ new ItemBuilder("Aged Brie", 2, 0).Build(),
+ new ItemBuilder("Elixir of the Mongoose", 5, 7).Build(),
+ new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", 0).Build(),
+ new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", -1).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert",
+ 15,
+ 20
+ ).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert",
+ 10,
+ 49
+ ).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert",
+ 5,
+ 49
+ ).Build(),
// this conjured item does not work properly yet
- new Item {Name = "Conjured Mana Cake", SellIn = 3, Quality = 6}
+ new ItemBuilder("Conjured Mana Cake", 3, 6).Build()
};
var app = new GildedRose(items);
diff --git a/csharpcore/GildedRoseTests/UpdateQualityTestFixture.cs b/csharpcore/GildedRoseTests/UpdateQualityTestFixture.cs
index e8123d4c..d39bc908 100644
--- a/csharpcore/GildedRoseTests/UpdateQualityTestFixture.cs
+++ b/csharpcore/GildedRoseTests/UpdateQualityTestFixture.cs
@@ -11,8 +11,8 @@ public class UpdateQualityTestFixture
[Test]
public void PlainItems_WhenNotExpired_Should_DecreaseSellInAndQualityByOne()
{
- var items = new List
- { new() { Name = "item 1", SellIn = 2, Quality = 3 } };
- var expectedItemsAfterTest = new List
- { new() { Name = "item 1", SellIn = 1, Quality = 2 } };
+ var items = new List
- { new ItemBuilder("item 1", 2, 3).Build() };
+ var expectedItemsAfterTest = new List
- { new ItemBuilder("item 1", 1, 2).Build() };
var app = new GildedRose(items);
app.UpdateQuality();
@@ -24,9 +24,9 @@ public class UpdateQualityTestFixture
[Test]
public void PlainItems_WhenExpired_Should_DecreaseSellInByOneAndQualityByTwo()
{
- var items = new List
- { new() { Name = "item 1", SellIn = 0, Quality = 5 } };
- var expectedItemsAfterDay1 = new List
- { new() { Name = "item 1", SellIn = -1, Quality = 3 } };
- var expectedItemsAfterDay2 = new List
- { new() { Name = "item 1", SellIn = -2, Quality = 1 } };
+ var items = new List
- { new ItemBuilder("item 1", 0, 5).Build() };
+ var expectedItemsAfterDay1 = new List
- { new ItemBuilder("item 1", -1, 3).Build() };
+ var expectedItemsAfterDay2 = new List
- { new ItemBuilder("item 1", -2, 1).Build() };
var app = new GildedRose(items);
@@ -43,13 +43,13 @@ public class UpdateQualityTestFixture
{
var items = new List
-
{
- new() { Name = "item 1", SellIn = 0, Quality = 0 },
- new() { Name = "item 1", SellIn = 0, Quality = 1 }
+ new ItemBuilder("item 1", 0, 0).Build(),
+ new ItemBuilder("item 1", 0, 1).Build()
};
var expectedItemsAfterTest = new List
-
{
- new() { Name = "item 1", SellIn = -1, Quality = 0 },
- new() { Name = "item 1", SellIn = -1, Quality = 0 }
+ new ItemBuilder("item 1", -1, 0).Build(),
+ new ItemBuilder("item 1", -1, 0).Build()
};
@@ -63,8 +63,8 @@ public class UpdateQualityTestFixture
[Test]
public void BetterWithAgeItems_WhenNotExpired_Should_IncreaseQualityByOne()
{
- var items = new List
- { new() { Name = "Aged Brie", SellIn = 1, Quality = 0 } };
- var expectedItemsAfterTest = new List
- { new() { Name = "Aged Brie", SellIn = 0, Quality = 1 } };
+ var items = new List
- { new ItemBuilder("Aged Brie", 1, 0).Build() };
+ var expectedItemsAfterTest = new List
- { new ItemBuilder("Aged Brie", 0, 1).Build() };
var app = new GildedRose(items);
app.UpdateQuality();
@@ -75,8 +75,8 @@ public class UpdateQualityTestFixture
[Test]
public void BetterWithAgeItems_WhenExpired_Should_IncreaseQualityByTwo()
{
- var items = new List
- { new() { Name = "aged Brie", SellIn = 0, Quality = 1 } };
- var expectedItemsAfterTest = new List
- { new() { Name = "aged Brie", SellIn = -1, Quality = 3 } };
+ var items = new List
- { new ItemBuilder("aged Brie", 0, 1).Build() };
+ var expectedItemsAfterTest = new List
- { new ItemBuilder("aged Brie", -1, 3).Build() };
var app = new GildedRose(items);
app.UpdateQuality();
@@ -88,9 +88,9 @@ public class UpdateQualityTestFixture
[Test]
public void ItemsQuality_Should_NeverIncreaseAbove50()
{
- var items = new List
- { new() { Name = "Aged Brie", SellIn = 1, Quality = 49 } };
- var expectedItemsAfterDay1 = new List
- { new() { Name = "Aged Brie", SellIn = 0, Quality = 50 } };
- var expectedItemsAfterDay2 = new List
- { new() { Name = "Aged Brie", SellIn = -1, Quality = 50 } };
+ var items = new List
- { new ItemBuilder("Aged Brie", 1, 49).Build() };
+ var expectedItemsAfterDay1 = new List
- { new ItemBuilder("Aged Brie", 0, 50).Build() };
+ var expectedItemsAfterDay2 = new List
- { new ItemBuilder("Aged Brie", -1, 50).Build() };
var app = new GildedRose(items);
@@ -107,13 +107,13 @@ public class UpdateQualityTestFixture
{
var items = new List
-
{
- new() { Name = "Sulfuras, Hand of Ragnaros", SellIn = 3, Quality = 3 },
- new() { Name = "sulfuras", SellIn = 3, Quality = 31 }
+ new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", 3).Build(),
+ new LegendaryItemBuilder("sulfuras", 3).Build()
};
var expectedItemsAfterTest = new List
-
{
- new() { Name = "Sulfuras, Hand of Ragnaros", SellIn = 3, Quality = 3 },
- new() { Name = "sulfuras", SellIn = 3, Quality = 31 }
+ new LegendaryItemBuilder("Sulfuras, Hand of Ragnaros", 3).Build(),
+ new LegendaryItemBuilder("sulfuras", 3).Build()
};
var app = new GildedRose(items);
@@ -123,11 +123,10 @@ public class UpdateQualityTestFixture
}
[Test]
- [Ignore("not implemented yet - Legendary Items should not take quality (constant at 80)")]
public void LegendaryItems_Quality_IsConstant80()
{
- var items = new List
- { new() { Name = "something sulfuras something", SellIn = 3 } };
- var expectedItemsAfterTest = new List
- { new() { Name = "something sulfuras something", SellIn = 3, Quality = 80 } };
+ var items = new List
- { new LegendaryItemBuilder("something sulfuras something", 3).Build() };
+ var expectedItemsAfterTest = new List
- { new LegendaryItemBuilder("something sulfuras something", 3).Build() };
var app = new GildedRose(items);
@@ -141,13 +140,13 @@ public class UpdateQualityTestFixture
{
var items = new List
-
{
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 11, Quality = 4 },
- new() { Name = "backstage passes to some other show", SellIn = 20, Quality = 40 }
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 11, 4).Build(),
+ new ItemBuilder("backstage passes to some other show", 20, 40).Build()
};
var expectedItemsAfterTest = new List
-
{
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 10, Quality = 5 },
- new() { Name = "backstage passes to some other show", SellIn = 19, Quality = 41 }
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 10, 5).Build(),
+ new ItemBuilder("backstage passes to some other show", 19, 41).Build()
};
var app = new GildedRose(items);
@@ -162,13 +161,13 @@ public class UpdateQualityTestFixture
{
var items = new List
-
{
- new() { Name = "backstage passes to a TAFKAL80ETC concert", SellIn = 10, Quality = 4 },
- new() { Name = "Backstage passes to some other show", SellIn = 6, Quality = 41 }
+ new ItemBuilder("backstage passes to a TAFKAL80ETC concert", 10, 4).Build(),
+ new ItemBuilder("Backstage passes to some other show", 6, 41).Build()
};
var expectedItemsAfterTest = new List
-
{
- new() { Name = "backstage passes to a TAFKAL80ETC concert", SellIn = 9, Quality = 6 },
- new() { Name = "Backstage passes to some other show", SellIn = 5, Quality = 43 }
+ new ItemBuilder("backstage passes to a TAFKAL80ETC concert", 9, 6).Build(),
+ new ItemBuilder("Backstage passes to some other show", 5, 43).Build()
};
var app = new GildedRose(items);
@@ -182,13 +181,13 @@ public class UpdateQualityTestFixture
{
var items = new List
-
{
- new() { Name = "Backstage passes to some other show", SellIn = 5, Quality = 4 },
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 1, Quality = 41 }
+ new ItemBuilder("Backstage passes to some other show", 5, 4).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 1, 41).Build()
};
var expectedItemsAfterTest = new List
-
{
- new() { Name = "Backstage passes to some other show", SellIn = 4, Quality = 7 },
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 0, Quality = 44 }
+ new ItemBuilder("Backstage passes to some other show", 4, 7).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 0, 44).Build()
};
var app = new GildedRose(items);
@@ -202,21 +201,21 @@ public class UpdateQualityTestFixture
{
var items = new List
-
{
- new() { Name = "Backstage passes", SellIn = 15, Quality = 49 },
- new() { Name = "Backstage passes to some other show", SellIn = 10, Quality = 49 },
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 5, Quality = 48 }
+ new ItemBuilder("Backstage passes", 15, 49).Build(),
+ new ItemBuilder("Backstage passes to some other show", 10,49).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 5, 48).Build()
};
var expectedItemsAfterDay1 = new List
-
{
- new() { Name = "Backstage passes", SellIn = 14, Quality = 50 },
- new() { Name = "Backstage passes to some other show", SellIn = 9, Quality = 50 },
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 4, Quality = 50 }
+ new ItemBuilder("Backstage passes", 14, 50).Build(),
+ new ItemBuilder("Backstage passes to some other show", 9, 50).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 4, 50).Build()
};
var expectedItemsAfterDay2 = new List
-
{
- new() { Name = "Backstage passes", SellIn = 13, Quality = 50 },
- new() { Name = "Backstage passes to some other show", SellIn = 8, Quality = 50 },
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 3, Quality = 50 }
+ new ItemBuilder("Backstage passes", 13, 50).Build(),
+ new ItemBuilder("Backstage passes to some other show", 8, 50).Build(),
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 3, 50).Build()
};
var app = new GildedRose(items);
@@ -233,13 +232,13 @@ public class UpdateQualityTestFixture
{
var items = new List
-
{
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = 0, Quality = 4 },
- new() { Name = "Another type of backstage passes", SellIn = 0, Quality = 4 }
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", 0, 4).Build(),
+ new ItemBuilder("Another type of backstage passes", 0, 4).Build()
};
var expectedItemsAfterTest = new List
-
{
- new() { Name = "Backstage passes to a TAFKAL80ETC concert", SellIn = -1, Quality = 0 },
- new() { Name = "Another type of backstage passes", SellIn = -1, Quality = 0 }
+ new ItemBuilder("Backstage passes to a TAFKAL80ETC concert", -1, 0).Build(),
+ new ItemBuilder("Another type of backstage passes", -1, 0).Build()
};
var app = new GildedRose(items);