diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs new file mode 100644 index 00000000..c5f5c97b --- /dev/null +++ b/exercise.main/Basket.cs @@ -0,0 +1,54 @@ +using exercise.main.Inventory; +using exercise.main.Products; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +namespace exercise.main +{ + public class Basket + { + private readonly List _items = new(); + private int _capacity; + + public Basket(int capacity = 4) + { + _capacity = capacity; + } + + public int Capacity { get { return _capacity; } set { _capacity = value; } } + + public string AddProduct(IProduct product) + { + if(_items.Count >= _capacity) + { + return $"the basket is full"; + } else + { + _items.Add(product); + return $"Item {product.Name} was successfully added to the basket."; + } + } + + // add by SKU with in-stock check + public string AddProduct(ICatalog catalog, string sku) + { + if (catalog == null) return "no catalog"; + if (!catalog.ProductExists(sku)) return "item not found"; + + var item = catalog.Get(sku); + if (!item.InStock) return $"{item.Name} - {item.Variant} is out of stock."; + + // create & add (capacity check + message reused) + var product = catalog.CreateProduct(sku); + return AddProduct(product); + } + + public List Products => _items; + + public decimal TotalPrice => _items.Sum(p => p.Price); + } +} diff --git a/exercise.main/Inventory/Catalog.cs b/exercise.main/Inventory/Catalog.cs new file mode 100644 index 00000000..19d0bece --- /dev/null +++ b/exercise.main/Inventory/Catalog.cs @@ -0,0 +1,122 @@ +using exercise.main.Products; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Inventory +{ + public class Catalog : ICatalog + { + private static int _nextId = 1; + private readonly Dictionary _items; + + public Catalog() + { + _items = new(StringComparer.OrdinalIgnoreCase) + { + ["BGLO"] = new("BGLO", 0.49m, "Bagel", "Onion", ProductType.Bagel, true), + ["BGLP"] = new("BGLP", 0.39m, "Bagel", "Plain", ProductType.Bagel, true), + ["BGLE"] = new("BGLE", 0.49m, "Bagel", "Everything", ProductType.Bagel, true), + ["BGLS"] = new("BGLS", 0.49m, "Bagel", "Sesame", ProductType.Bagel, true), + + ["COFB"] = new("COFB", 0.99m, "Coffee", "Black", ProductType.Coffee, true), + ["COFW"] = new("COFW", 1.19m, "Coffee", "White", ProductType.Coffee, true), + ["COFC"] = new("COFC", 1.29m, "Coffee", "Cappuccino", ProductType.Coffee, false), + ["COFL"] = new("COFL", 1.29m, "Coffee", "Latte", ProductType.Coffee, false), + + ["FILB"] = new("FILB", 0.12m, "Filling", "Bacon", ProductType.Filling, true), + ["FILE"] = new("FILE", 0.12m, "Filling", "Egg", ProductType.Filling, true), + ["FILC"] = new("FILC", 0.12m, "Filling", "Cheese", ProductType.Filling, true), + ["FILX"] = new("FILX", 0.12m, "Filling", "Cream Cheese", ProductType.Filling, true), + ["FILS"] = new("FILS", 0.12m, "Filling", "Smoked Salmon", ProductType.Filling, true), + ["FILH"] = new("FILH", 0.12m, "Filling", "Ham", ProductType.Filling, true), + }; + } + + public bool ProductExists(string sku) + { + return _items.ContainsKey(sku); + } + + public CatalogItem Get(string sku) + { + CatalogItem item; + if (_items.TryGetValue(sku, out item)) + { + return item; + } + return null; + } + + // Returns all items of a specific type, optionally filtering out those that are not in stock. + public IEnumerable GetByType(ProductType type, bool onlyInStock = true) + { + var result = new List(); + + foreach (var i in _items.Values) + { + if (i.Type == type) + { + if (onlyInStock && !i.InStock) + { + continue; + } + result.Add(i); + } + } + + return result; + } + + // Returns all sold-out items of a specific type. + public IEnumerable GetSoldOutByType(ProductType type) + { + var result = new List(); + + foreach (var i in _items.Values) + { + if (i.Type == type && !i.InStock) + { + result.Add(i); + } + } + + return result; + } + + // Creates a product instance based on the SKU. + // Returns null if the SKU doesn't exist, the item is out of stock, or the type is unsupported. + public IProduct CreateProduct(string sku) + { + if (!_items.TryGetValue(sku, out var item)) + { + // SKU not found + return null; + } + + // Not in stock + if (!item.InStock) + { + return null; + } + + // Unique id in case of multible of the same sku + var id = _nextId++; + + // Create the correct product type + if (item.Type == ProductType.Bagel) + return new Bagel(id, item.Sku, item.Variant, item.Price); + if (item.Type == ProductType.Coffee) + return new Coffee(id, item.Sku, item.Variant, item.Price); + if (item.Type == ProductType.Filling) + return new Filling(id, item.Sku, item.Variant, item.Price); + + return null; + } + + + public decimal GetPrice(string sku) => Get(sku).Price; + } +} diff --git a/exercise.main/Inventory/CatalogItem.cs b/exercise.main/Inventory/CatalogItem.cs new file mode 100644 index 00000000..2b4b9fe4 --- /dev/null +++ b/exercise.main/Inventory/CatalogItem.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using exercise.main.Products; + +namespace exercise.main.Inventory +{ + public class CatalogItem + { + public string Sku { get; } + public decimal Price { get; } + public string Name { get; } + public string Variant { get; } + public ProductType Type { get; } + public bool InStock { get; set; } + + public CatalogItem(string sku, decimal price, string name, string variant, ProductType type, bool inStock = true) + { + Sku = sku; + Price = price; + Name = name; + Variant = variant; + Type = type; + InStock = inStock; + } + } +} + diff --git a/exercise.main/Inventory/ICatalog.cs b/exercise.main/Inventory/ICatalog.cs new file mode 100644 index 00000000..da2b4d5e --- /dev/null +++ b/exercise.main/Inventory/ICatalog.cs @@ -0,0 +1,19 @@ +using exercise.main.Products; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Inventory +{ + public interface ICatalog + { + bool ProductExists(string sku); + CatalogItem Get(string sku); + IEnumerable GetByType(ProductType type, bool onlyInStock = true); + IEnumerable GetSoldOutByType(ProductType type); + IProduct CreateProduct(string sku); + decimal GetPrice(string sku); + } +} diff --git a/exercise.main/Products/Bagel.cs b/exercise.main/Products/Bagel.cs new file mode 100644 index 00000000..3932d4da --- /dev/null +++ b/exercise.main/Products/Bagel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Products +{ + public class Bagel : Product + { + public Bagel(int id, string sku, string variant, decimal price) + : base(id, sku, "Bagel", variant, price, ProductType.Bagel) { } + } +} + diff --git a/exercise.main/Products/Coffee.cs b/exercise.main/Products/Coffee.cs new file mode 100644 index 00000000..9551062e --- /dev/null +++ b/exercise.main/Products/Coffee.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Products +{ + public class Coffee : Product + { + public Coffee(int id, string sku, string variant, decimal price) + : base(id, sku, "Coffee", variant, price, ProductType.Coffee) { } + } +} + diff --git a/exercise.main/Products/Filling.cs b/exercise.main/Products/Filling.cs new file mode 100644 index 00000000..99367b54 --- /dev/null +++ b/exercise.main/Products/Filling.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Products +{ + public class Filling : Product + { + public Filling(int id, string sku, string variant, decimal price) + : base(id, sku, "Filling", variant, price, ProductType.Filling) { } + } +} + diff --git a/exercise.main/Products/IDiscountable.cs b/exercise.main/Products/IDiscountable.cs new file mode 100644 index 00000000..d7aaf9df --- /dev/null +++ b/exercise.main/Products/IDiscountable.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Products +{ + public interface IDiscountable + { + + } +} diff --git a/exercise.main/Products/IProduct.cs b/exercise.main/Products/IProduct.cs new file mode 100644 index 00000000..68c656a2 --- /dev/null +++ b/exercise.main/Products/IProduct.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Products +{ + public interface IProduct + { + int Id { get; } + string Sku { get; } + string Name { get; } + string Variant { get; } + decimal Price { get; } + ProductType Type { get; } + } +} + diff --git a/exercise.main/Products/Product.cs b/exercise.main/Products/Product.cs new file mode 100644 index 00000000..fdb725c6 --- /dev/null +++ b/exercise.main/Products/Product.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Products +{ + public abstract class Product : IProduct + { + public int Id { get; } + public string Sku { get; } + public string Name { get; } + public string Variant { get; } + public decimal Price { get; } + public ProductType Type { get; } + + protected Product(int id, string sku, string name, string variant, decimal price, ProductType type) + { + Id = id; + Sku = sku; + Name = name; + Variant = variant; + Price = price; + Type = type; + } + + public override string ToString() => $"{Name} - {Variant} ({Sku}) ${Price:0.00}"; + } +} + diff --git a/exercise.main/Products/ProductType.cs b/exercise.main/Products/ProductType.cs new file mode 100644 index 00000000..89b4d58d --- /dev/null +++ b/exercise.main/Products/ProductType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Products +{ + public enum ProductType + { + Bagel = 1, + Coffee = 2, + Filling = 3 + } +} + diff --git a/exercise.main/Program.cs b/exercise.main/Program.cs index 3751555c..4ac66f89 100644 --- a/exercise.main/Program.cs +++ b/exercise.main/Program.cs @@ -1,2 +1,234 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using exercise.main; +using exercise.main.Inventory; +using exercise.main.Products; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace exercise.main +{ + public class Program + { + public static void Main() + { + Console.OutputEncoding = System.Text.Encoding.UTF8; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + + var basket = new Basket(); + var catalog = new Catalog(); + + while (true) + { + Console.WriteLine("Welcome to Bob's Bagels! What would you like to order?"); + Console.WriteLine("1. Bagel"); + Console.WriteLine("2. Coffee"); + Console.WriteLine("(q = quit)"); + Console.Write("Choice: "); + var first = (Console.ReadLine() ?? "").Trim(); + if (first.Equals("q", StringComparison.OrdinalIgnoreCase)) return; + + if (first == "1") + { + AddBagelWithFillings(catalog, basket); + } + else if (first == "2") + { + AddCoffee(catalog, basket); + } + else + { + Console.WriteLine("Invalid choice.\n"); + continue; + } + + // Change your order menu + while (true) + { + Console.WriteLine("\nDo you want to change anything in your basket?"); + Console.WriteLine("1. Add something"); + Console.WriteLine("2. Remove something"); + Console.WriteLine("3. No, I'm good"); + Console.Write("Choice: "); + var change = (Console.ReadLine() ?? "").Trim(); + + if (change == "1") + { + Console.WriteLine("\nWhat would you like to add?"); + Console.WriteLine("1. Bagel"); + Console.WriteLine("2. Coffee"); + Console.WriteLine("3. Cancel"); + Console.Write("Choice: "); + var add = (Console.ReadLine() ?? "").Trim(); + + if (add == "1") AddBagelWithFillings(catalog, basket); + else if (add == "2") AddCoffee(catalog, basket); + } + else if (change == "2") + { + if (!RemoveByPrintedNumber(basket)) + continue; + } + else if (change == "3") break; + else Console.WriteLine("Invalid choice."); + } + + // Summary and exit + PrintBasketIndented(basket); + Console.WriteLine($"\nYour total is: ${basket.TotalPrice:0.00}\n"); + Console.WriteLine("Thank you for your order!"); + return; + } + } + + // Adds a bagel with optional fillings to the basket + private static void AddBagelWithFillings(ICatalog catalog, Basket basket) + { + var bagel = PickOne(catalog, ProductType.Bagel, "Which bagel would you like?"); + if (bagel == null) return; + + basket.AddProduct(catalog.CreateProduct(bagel.Sku)); + + var chosenFillings = PickFillings(catalog, bagel.Variant); + foreach (var f in chosenFillings) basket.AddProduct(catalog.CreateProduct(f.Sku)); + + Console.WriteLine(chosenFillings.Count == 0 + ? $"\nYou have ordered a {bagel.Variant.ToLower()} bagel with no fillings." + : $"\nYou have ordered a {bagel.Variant.ToLower()} bagel with {FormatList(chosenFillings.Select(x => x.Variant))}."); + } + + // Adds a coffee to the basket + private static void AddCoffee(ICatalog catalog, Basket basket) + { + var coffee = PickOne(catalog, ProductType.Coffee, "Which coffee would you like?"); + if (coffee == null) return; + + basket.AddProduct(catalog.CreateProduct(coffee.Sku)); + Console.WriteLine($"\nYou have added {coffee.Variant.ToLower()} coffee."); + } + + // pick one item from the printed catalog + private static CatalogItem? PickOne(ICatalog catalog, ProductType type, string title) + { + var ItemsInStock = catalog.GetByType(type, onlyInStock: true).ToList(); + var ItemsOutOfStock = catalog.GetSoldOutByType(type).ToList(); + + Console.WriteLine($"\n{title}"); + for (int i = 0; i < ItemsInStock.Count; i++) Console.WriteLine($"{i + 1}. {ItemsInStock[i].Variant} — ${ItemsInStock[i].Price:0.00}"); + if (ItemsOutOfStock.Count > 0) Console.WriteLine($"\nWe are unfortunately out of stock for: {string.Join(", ", ItemsOutOfStock.Select(x => x.Variant))}"); + Console.Write("Enter number: "); + + int idx = ReadIndex(ItemsInStock.Count); + return ItemsInStock[idx]; + } + + // Pick one or more fillings for the bagel, returns a list of CatalogItems + private static List PickFillings(ICatalog catalog, string bagelVariant) + { + var fills = catalog.GetByType(ProductType.Filling, onlyInStock: true).ToList(); + + Console.WriteLine($"\nWhat would you like on your {bagelVariant.ToLower()} bagel?"); + for (int i = 0; i < fills.Count; i++) Console.WriteLine($"{i + 1}. {fills[i].Variant} — ${fills[i].Price:0.00}"); + Console.WriteLine($"{fills.Count + 1}. No fillings"); + Console.Write($"Pick one or more (e.g., 1,3) or {fills.Count + 1} for none: "); + + var input = (Console.ReadLine() ?? "").Trim(); + if (int.TryParse(input, out var single) && single == fills.Count + 1) return new(); + + // Check if there are more than one filling selected + var chosen = new List(); + foreach (var p in input.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + if (int.TryParse(p, out var n) && n >= 1 && n <= fills.Count) + if (!chosen.Contains(fills[n - 1])) chosen.Add(fills[n - 1]); + + return chosen; + } + + // Printing out the basket with indentation for fillings + private static void PrintBasketIndented(Basket basket) + { + Console.WriteLine("\nYour basket:"); + for (int i = 0; i < basket.Products.Count; i++) + { + var p = basket.Products[i]; + if (p.Type == ProductType.Bagel) + { + Console.WriteLine($"- {p.Name} - {p.Variant} … ${p.Price:0.00}"); + int j = i + 1; + while (j < basket.Products.Count && basket.Products[j].Type == ProductType.Filling) + { + var f = basket.Products[j]; + Console.WriteLine($" - {f.Name} - {f.Variant} … ${f.Price:0.00}"); + j++; + } + i = j - 1; + } + else + { + Console.WriteLine($"- {p.Name} - {p.Variant} … ${p.Price:0.00}"); + } + } + } + + private static bool RemoveByPrintedNumber(Basket basket) + { + if (basket.Products.Count == 0) { Console.WriteLine("\nYour basket is empty."); return false; } + + Console.WriteLine("\nYour basket (choose a number to remove):"); + for (int i = 0; i < basket.Products.Count; i++) + { + var p = basket.Products[i]; + var indent = p.Type == ProductType.Filling ? " " : ""; + Console.WriteLine($"{i + 1}. {indent}{p.Name} - {p.Variant} … ${p.Price:0.00}"); + } + Console.Write("Enter number to remove: "); + var raw = (Console.ReadLine() ?? "").Trim(); + + var found = false; int choice; + if (int.TryParse(raw, out choice)) + { + int pos = 1; + foreach (var _ in basket.Products) { if (pos == choice) { found = true; break; } pos++; } + } + if (!found) { Console.WriteLine("That item is not in your basket."); return false; } + + int idx = choice - 1; + var item = basket.Products[idx]; + + if (item.Type == ProductType.Bagel) + { + basket.Products.RemoveAt(idx); + while (idx < basket.Products.Count && basket.Products[idx].Type == ProductType.Filling) + basket.Products.RemoveAt(idx); + Console.WriteLine("\nRemoved bagel (and its fillings)."); + } + else + { + basket.Products.RemoveAt(idx); + Console.WriteLine("\nRemoved item."); + } + return true; + } + + // Reads the input and ensuring it is valid based on the choices available. + private static int ReadIndex(int max) + { + while (true) + { + var input = (Console.ReadLine() ?? "").Trim(); + if (int.TryParse(input, out var n) && n >= 1 && n <= max) return n - 1; + Console.Write("Invalid choice. Try again: "); + } + } + + // Formats the list of items for output depending on how many items there are. + private static string FormatList(IEnumerable items) + { + var list = items.Select(s => s.ToLower()).ToList(); + if (list.Count == 0) return ""; + if (list.Count == 1) return list[0]; + if (list.Count == 2) return $"{list[0]} and {list[1]}"; + return string.Join(", ", list.Take(list.Count - 1)) + " and " + list[^1]; + } + } +} diff --git a/exercise.main/domain_model.md b/exercise.main/domain_model.md new file mode 100644 index 00000000..dd0bf3f6 --- /dev/null +++ b/exercise.main/domain_model.md @@ -0,0 +1,30 @@ +# Domain Modelling + +## Core Tasks + +1. Add items to basket +2. Remove items from basket +3. Notify when basket is full +4. Change basket capacity +5. Handle removing items that don’t exist in basket +6. View total cost of basket +7. View price of item before adding to basket +8. Add extra fillings to bagels +9. View price of fillings +10. Only order items that are in stock + + +| # | Classes/Interfaces | Methods/Properties | Scenario | Outputs | +| -- | ------------------ | ----------------------------- | --------------------------------------------- | ---------------------------- | +| 1 | Basket | AddItem() | Add a bagel or other product to basket | Item added | +| 2 | Basket | RemoveItem() | Remove product from basket | Item removed | +| 3 | Basket | AddItem() - capacity check | Notify when basket is full | BasketFullException | +| 4 | Basket | SetCapacity() | Change the basket capacity | Updated capacity | +| 5 | IBasket | RemoveItem() | Attempt to remove item that doesn’t exist | ItemNotInBasketException | +| 6 | Basket | Totalprice() | Get total price of all items in basket | Decimal sum | +| 7 | ICatalog | GetProduct().Price | View price of item before adding it to basket | Decimal price | +| 8 | Basket | AddItem() | Choose extra fillings for bagel | Filling added | +| 9 | ICatalog | GetProduct().Price | View price of each filling | Decimal price | +| 10 | ICatalog | ProductExists | Only allow adding items that exist in stock | OK / NotInInventoryException | + + diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index 7bdb8968..1899afc9 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -1,15 +1,221 @@ +using static NUnit.Framework.Internal.OSPlatform; +using exercise.main; +using exercise.main.Inventory; +using ProductType = exercise.main.Products.ProductType; +using exercise.main.Products; + + namespace exercise.tests; public class Tests { - [SetUp] - public void Setup() + [Test] + public void AddToBasket_Bagel() + { + var catalog = new Catalog(); + var basket = new Basket(); + + var bagel = catalog.CreateProduct("BGLP"); + basket.AddProduct(bagel); + + Assert.That(basket.Products.Count, Is.EqualTo(1)); + Assert.That(basket.Products[0].Type, Is.EqualTo(ProductType.Bagel)); + Assert.That(basket.Products[0].Variant, Is.EqualTo("Plain")); + } + + [Test] + public void RemoveFromBasket() + { + var catalog = new Catalog(); + var basket = new Basket(); + + basket.AddProduct(catalog.CreateProduct("BGLP")); + basket.AddProduct(catalog.CreateProduct("COFB")); + + basket.Products.RemoveAt(0); + + Assert.That(basket.Products.Count, Is.EqualTo(1)); + } + + [Test] + public void TotalCost() + { + var catalog = new Catalog(); + var basket = new Basket(); + + basket.AddProduct(catalog.CreateProduct("BGLP")); // 0.39 + basket.AddProduct(catalog.CreateProduct("COFB")); // 0.99 + basket.AddProduct(catalog.CreateProduct("FILC")); // 0.12 + + Assert.That(basket.TotalPrice, Is.EqualTo(0.39m + 0.99m + 0.12m)); // 1.50 + } + + [Test] + public void IfBasketIsFullMessageShouldBe() + { + var catalog = new Catalog(); + var basket = new Basket(); + + + basket.AddProduct(catalog.CreateProduct("BGLP")); + basket.AddProduct(catalog.CreateProduct("BGLP")); + basket.AddProduct(catalog.CreateProduct("BGLP")); + basket.AddProduct(catalog.CreateProduct("BGLP")); + + string resultat = basket.AddProduct(catalog.CreateProduct("BGLO")); + + string expected = $"the basket is full"; + + Assert.That(resultat, Is.EqualTo(expected)); + } + + [Test] + public void ChangeBasketCapacity() + { + var catalog = new Catalog(); + var basket = new Basket(2); + + Assert.That(basket.Capacity, Is.EqualTo(2)); + + basket.AddProduct(catalog.CreateProduct("BGLP")); + basket.AddProduct(catalog.CreateProduct("COFB")); + basket.AddProduct(catalog.CreateProduct("FILC")); + + string resultat = basket.AddProduct(catalog.CreateProduct("BGLO")); + + string expected = $"the basket is full"; + Assert.That(resultat, Is.EqualTo(expected)); + + basket.Capacity = 8; // change capacity to 8 + + string result = basket.AddProduct(catalog.CreateProduct("BGLO")); + + string expected2 = $"Item {catalog.CreateProduct("BGLO").Name} was successfully added to the basket."; + + Assert.That(basket.Capacity, Is.EqualTo(8)); + Assert.That(result, Is.EqualTo(expected2)); + } + + [Test] + public void ViewPriceOfItem() + { + var catalog = new Catalog(); + var basket = new Basket(); + + basket.AddProduct(catalog.CreateProduct("BGLP")); // 0.39 + basket.AddProduct(catalog.CreateProduct("COFB")); // 0.99 + basket.AddProduct(catalog.CreateProduct("FILC")); // 0.12 + + Assert.That(basket.Products[0].Price, Is.EqualTo(0.39m)); + Assert.That(basket.Products[1].Price, Is.EqualTo(0.99m)); + Assert.That(basket.Products[2].Price, Is.EqualTo(0.12m)); + } + + [Test] + public void OnlyOrderItemsInStock() { + var catalog = new Catalog(); + var basket = new Basket(); + + // in-stock -> added + string expected = "Item Bagel was successfully added to the basket."; + string result = basket.AddProduct(catalog, "BGLP"); + Assert.That(result, Is.EqualTo(expected)); + Assert.That(basket.Products.Count, Is.EqualTo(1)); + + // try add out-of-stock item + var outOfStockItem = catalog.Get("COFC"); + expected = $"{outOfStockItem.Name} - {outOfStockItem.Variant} is out of stock."; + result = basket.AddProduct(catalog, "COFC"); + + Assert.That(result, Is.EqualTo(expected)); + Assert.That(basket.Products.Count, Is.EqualTo(1)); } [Test] - public void Test1() + public void AddExtraFillingsToBagel() { - Assert.Pass(); + var catalog = new Catalog(); + var basket = new Basket(); + + basket.AddProduct(catalog.CreateProduct("BGLP")); // bagel + + string expected = "Item Filling was successfully added to the basket."; + + string result = basket.AddProduct(catalog.CreateProduct("FILC")); // cheese + Assert.That(result, Is.EqualTo(expected)); + + result = basket.AddProduct(catalog.CreateProduct("FILB")); // bacon + Assert.That(result, Is.EqualTo(expected)); + + Assert.That(basket.Products.Count, Is.EqualTo(3)); + Assert.That(basket.Products[1].Type, Is.EqualTo(ProductType.Filling)); + Assert.That(basket.Products[2].Type, Is.EqualTo(ProductType.Filling)); } -} \ No newline at end of file + + [Test] + public void AddFillings() + { + var catalog = new Catalog(); + var basket = new Basket(); + + basket.AddProduct(catalog.CreateProduct("BGLP")); // bagel + + string expected = "Item Filling was successfully added to the basket."; + string result = basket.AddProduct(catalog.CreateProduct("FILC")); + Assert.That(result, Is.EqualTo(expected)); + + result = basket.AddProduct(catalog.CreateProduct("FILB")); // bacon + Assert.That(result, Is.EqualTo(expected)); + + Assert.That(basket.Products.Count, Is.EqualTo(3)); + Assert.That(basket.Products.Count(p => p.Type == ProductType.Filling), Is.EqualTo(2)); + } + + [Test] + public void ViewPriceOfEachFilling() + { + var catalog = new Catalog(); + + var expected = new Dictionary + { + ["FILB"] = 0.12m, // Bacon + ["FILE"] = 0.12m, // Egg + ["FILC"] = 0.12m, // Cheese + ["FILX"] = 0.12m, // Cream Cheese + ["FILS"] = 0.12m, // Smoked Salmon + ["FILH"] = 0.12m, // Ham + }; + + foreach (var (sku, expectedPrice) in expected) + { + var result = catalog.GetPrice(sku); + Assert.That(result, Is.EqualTo(expectedPrice)); + } + } + + [Test] + public void ViewPriceOfEachBagel() + { + var catalog = new Catalog(); + + var expected = new Dictionary + { + ["BGLO"] = 0.49m, // Onion + ["BGLP"] = 0.39m, // Plain + ["BGLE"] = 0.49m, // Everything + ["BGLS"] = 0.49m, // Sesame + }; + + foreach (var entry in expected) + { + decimal expectedPrice = entry.Value; + decimal result = catalog.GetPrice(entry.Key); + Assert.That(result, Is.EqualTo(expectedPrice)); + } + } + + + + +} diff --git a/exercise.tests/exercise.tests.csproj b/exercise.tests/exercise.tests.csproj index 9fed8e17..a3a97d4f 100644 --- a/exercise.tests/exercise.tests.csproj +++ b/exercise.tests/exercise.tests.csproj @@ -17,4 +17,8 @@ + + + +