Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e9e883c
add domain-model
SnorreAldstedt Aug 11, 2025
9b331b0
Add user stories to domain-model
SnorreAldstedt Aug 11, 2025
24b0634
Add classes to create tests after the domain model
SnorreAldstedt Aug 11, 2025
fbf875c
Merge branch 'main' of https://github.com/SnorreAldstedt/csharp-tdd-o…
SnorreAldstedt Aug 11, 2025
b46e87b
Add tests for StoreItems and Basket
SnorreAldstedt Aug 11, 2025
da863d3
Rename Tests to BasketTests in BasketTests.cs
SnorreAldstedt Aug 11, 2025
4e7ac43
Add test for removing fillings
SnorreAldstedt Aug 11, 2025
fd0d754
Pass all StoreItemTests
SnorreAldstedt Aug 11, 2025
e61bc23
Pass all Basket-, and StoreItem-tests
SnorreAldstedt Aug 11, 2025
4189462
Add copy method in domain model
SnorreAldstedt Aug 11, 2025
280613b
Add tests for checking Copy() and Equivalent(item)
SnorreAldstedt Aug 11, 2025
00bcdd0
Pass all tests regarding equivalent items
SnorreAldstedt Aug 11, 2025
6e528a9
Pass Equivalent and Copy tests
SnorreAldstedt Aug 11, 2025
93339d7
Expand Domain-Model
SnorreAldstedt Aug 12, 2025
dcc118c
Add to domain-model
SnorreAldstedt Aug 12, 2025
b7eb18d
Add test for InventoryItem
SnorreAldstedt Aug 12, 2025
7054dec
Add tests for store and clearBasket
SnorreAldstedt Aug 12, 2025
8044545
Pass tests
SnorreAldstedt Aug 12, 2025
7b4275f
Add and pass discount-tests
SnorreAldstedt Aug 12, 2025
b7f177e
Add user stories etc for the extensions
SnorreAldstedt Aug 13, 2025
4630466
Add receipt
SnorreAldstedt Aug 13, 2025
0f296e1
Add discounts to receipt(put it in the original receipt class)
SnorreAldstedt Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions domain-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Domain Model
```
1.
As a member of the public,
So I can order a bagel before work,
I'd like to add a specific type of bagel to my basket.
```

```
2.
As a member of the public,
So I can change my order,
I'd like to remove a bagel from my basket.
```

```
3.
As a member of the public,
So that I can not overfill my small bagel basket
I'd like to know when my basket is full when I try adding an item beyond my basket capacity.
```

```
4.
As a Bob's Bagels manager,
So that I can expand my business,
I’d like to change the capacity of baskets.
```

```
5.
As a member of the public
So that I can maintain my sanity
I'd like to know if I try to remove an item that doesn't exist in my basket.
```

```
6.
As a customer,
So I know how much money I need,
I'd like to know the total cost of items in my basket.
```

```
7.
As a customer,
So I know what the damage will be,
I'd like to know the cost of a bagel before I add it to my basket.
```

```
8.
As a customer,
So I can shake things up a bit,
I'd like to be able to choose fillings for my bagel.
```

```
9.
As a customer,
So I don't over-spend,
I'd like to know the cost of each filling before I add it to my bagel order.
```

```
10.
As the manager,
So we don't get any weird requests,
I want customers to only be able to order things that we stock in our inventory.
```
## Core
| Class | Method/Property | Scenario | Output |
|-------|-----------------|----------|--------|
| Basket | AddItem(IStoreItem item) | Add an Item to a Basket | void |
| Basket | RemoveItem(IStoreItem item) | Remove an Item to a Basket | void |
| Basket | IsFull() | Checks if basket is full | bool |
| Store | ChangeCap() | Changes capacity of baskets | void |
| Basket | BasketHas(IStoreItem item) | Checks if basket has item | bool |
| Basket | Remove(StoreItem item) | Removes item from basket if the basket has that item | void |
| Basket | TotalCost() | Returns total cost of Basket | int |
| Bagel | AddFillings(Filling filling) | Add filling to bagel | void |
| Bagel | GetTotalPrice() | Get total cost of bagel including fillings | Decimal |
| IStoreItem | Interface with methods for products | Interface for bagel, filling and coffee | |
| IStoreItem | Price | attribute that has the price of an IStoreItem | int |
| Store | GetInventory() | function that returns the inventory of the store | List<IStoreItem> |
| IStoreItem | Copy() | function to copy a storeItem, makes it easier to choose a bagel/coffee/filling from a menu | IStoreItem |
| IStoreItem | Equivalent(IstoreItem item) | Checks wheter an item is equivalent to the instance it's called on| bool |
| Inventory | SKU | Attribute that has the Sku of an Inventory instance | string |
| Inventory | Price | Attribute that has the Price of an Inventory instance | decimal |
| Inventory | Name | Attribute that has the Name of an Inventory instance | string |
| Inventory | Variant | Attribute that has the Variant of an Inventory instance | string |
| Store | MaxCapacity | Attribute that should be handed over when creatiing a basket at the store | int |
| Store | Name | Attribute that holds the name of the store | string |
| Store | InventoryList | List of all Items available | List<Inventory> |
| Store | StoreHasItem(IStoreItem item) | Method that checks if the Item exists in Inventory | bool |
| Basket | ClearBasket() | Removes all items from basket | void |
| Store | CreateBagel(string sku) | Creates a bagel from inventory | Bagel |
| Store | CreateCoffee(string sku) | Creates a coffee from inventory | Coffee |
| Store | CreateFilling(string sku) | Creates a filling from inventory | Filling |
| Store | CreateNewBasket() | Creates a new basket with max capacity from the store | Basket |
| Store | AddFillToBagel(Bagel bagel, Filling fill) | Adds a filling to a bagel | void |

## Extension

### Discounts
- I want to be able to get discounts when buying large amounts of bagels
- I want to have a breakfast deal with a coffee and a bagel

| BasketCheckout | ApplyDiscount() | Method that applies discount on the basket | void |
| BasketCheckout | CalculateDiscount() | Mrthod that calculates and returns Discount | int |

### Receipts

- As a customer I want to get an receipt to be able to verify that my order is correct
- As an employee at Bob's Bagels I want to assure the customer that they got the right order
70 changes: 70 additions & 0 deletions exercise.main/Basket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using exercise.main.StoreItem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace exercise.main
{
public class Basket
{
public List<IStoreItem> storeItems = new List<IStoreItem>();
public int ItemCount { get { return storeItems.Count; } }

public Basket(int capacity = 20)
{
Capacity = capacity;
}

public int Capacity { get; set; }

public void Add(IStoreItem storeItem)
{
storeItems.Add(storeItem);
}

public bool BasketHas(IStoreItem storeItem)
{
return storeItems.Contains(storeItem);
}

public bool IsFull()
{
return storeItems.Count >= Capacity;
}

public void Remove(IStoreItem storeItem)
{
if (storeItems.Contains(storeItem))
{
storeItems.Remove(storeItem);
}
}

public int CountOccurences(string sku)
{
int count = 0;
foreach(IStoreItem item in storeItems)
{
if (item.Sku == sku) count++;
}
return count;
}

public decimal TotalCost()
{
decimal totalPrice = 0;
foreach(IStoreItem storeItem in storeItems)
{
totalPrice += storeItem.Price;
}
return totalPrice;
}

public void ClearBasket()
{
storeItems.Clear();
}
}
}
118 changes: 118 additions & 0 deletions exercise.main/BasketCheckout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using exercise.main.StoreItem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace exercise.main
{
public class BasketCheckout
{
public Basket basket;
public decimal originalCost;
public decimal discountCost;
public decimal discountTotal{ get { return originalCost - discountCost; } }
public Store Store { get; set; }

public BasketCheckout(Basket b, Store store)
{
basket = b;
originalCost = basket.TotalCost();
Store = store;
}

private bool hasTwelveOfSameBagel(string sku)
{
bool isBagel = Store.InventoryDict[sku].Name == "Bagel";
int occurences = basket.CountOccurences(sku);
return (isBagel && occurences >= 12);
}
private bool hasSixOfSameBagel(string sku)
{
bool isBagel = Store.InventoryDict[sku].Name == "Bagel";
int occurences = basket.CountOccurences(sku);
return (isBagel && occurences >= 6);
}

private bool hasCoffeeAndBagel()
{
bool hasBagel = basket.storeItems.Any(i => Store.InventoryDict.ContainsKey(i.Sku) && Store.InventoryDict[i.Sku].Name == "Bagel");
bool hasCoffee = basket.storeItems.Any(i => Store.InventoryDict.ContainsKey(i.Sku) && Store.InventoryDict[i.Sku].Name == "Coffee");

return (hasBagel && hasCoffee);
}




public List<IStoreItem> coffeeSorted()
{
List<IStoreItem> onlyCoffee = basket.storeItems.FindAll(i => Store.InventoryDict[i.Sku].Name == "Coffee");
List<IStoreItem> coffeeSorted = onlyCoffee.OrderByDescending(i => i.Price).ToList();
return coffeeSorted;
}
public List<IStoreItem> bagelSorted()
{
List<IStoreItem> onlyBagel = basket.storeItems.FindAll(i => Store.InventoryDict[i.Sku].Name == "Bagel");
List<IStoreItem> bagelSorted = onlyBagel.OrderByDescending(i => i.Price).ToList();
return bagelSorted;
}


public decimal calculateDiscountCAndB()
{
decimal discount = 0;
if (hasCoffeeAndBagel())
{
decimal maxCoffeePrice = coffeeSorted().First().Price;
decimal maxBagelPrice = bagelSorted().First().Price;
discount = (maxCoffeePrice + maxBagelPrice) - 1.25m ;
}
return discount;
}

public decimal calculateDiscountBagels()
{
decimal discount = 0;
List<IStoreItem> bagelSort = bagelSorted();
foreach(IStoreItem bagel in bagelSort)
{
if (hasTwelveOfSameBagel(bagel.Sku))
{
if(discount < (bagel.Price*12) - 3.99m)
{
discount = (bagel.Price * 12) - 3.99m;
}
}
else if (hasSixOfSameBagel(bagel.Sku))
{
if (discount < (bagel.Price * 6) - 2.49m)
{
discount = (bagel.Price * 6) - 2.49m;
}
}
}
return discount;
}

public void applyDiscount()
{
decimal maxDiscount = 0;
if (hasCoffeeAndBagel())
{
decimal currentDiscount = calculateDiscountCAndB();
if (currentDiscount > maxDiscount)
{
maxDiscount = currentDiscount;
}
}
decimal bagelDiscount = calculateDiscountBagels();
if(bagelDiscount > maxDiscount)
{
maxDiscount = bagelDiscount;
}
discountCost = originalCost - maxDiscount;
}
}
}
18 changes: 18 additions & 0 deletions exercise.main/IReceipt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using exercise.main.StoreItem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace exercise.main
{
public interface IReceipt {

string buildReceipt();

void printReceipt();


}
}
24 changes: 24 additions & 0 deletions exercise.main/Inventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace exercise.main
{
public class Inventory
{
public Inventory(string sku, decimal price, string name, string variant)
{
Sku = sku;
Price = price;
Name = name;
Variant = variant;
}

public string Sku { get; }
public decimal Price { get; }
public string Name { get; }
public string Variant { get; }
}
}
1 change: 1 addition & 0 deletions exercise.main/Program.cs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

Loading