-
-
Save bentayloruk/9218078 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using System; | |
using System.Collections.Generic; | |
using System.Collections.Immutable; | |
using System.Linq; | |
namespace TDDCoverage | |
{ | |
public class Order | |
{ | |
public class Line | |
{ | |
public readonly String ArticleId; | |
public readonly decimal Amount; | |
public Line(String ArticleId, decimal Amount) | |
{ | |
this.ArticleId = ArticleId; | |
this.Amount = Amount; | |
} | |
} | |
public readonly IEnumerable<Line> Lines; | |
public Order(IEnumerable<Line> Lines) | |
{ | |
this.Lines = Lines; | |
} | |
public class LineList : List<Line> | |
{ | |
public void Add(String ArticleId, decimal Amount) | |
{ | |
this.Add(new Line(ArticleId,Amount)); | |
} | |
} | |
} | |
public class Invoice | |
{ | |
public class Item | |
{ | |
public readonly String Name; | |
public readonly decimal Amount, Price, VAT; | |
public Item(String Name, decimal Amount, decimal Price, decimal VAT) | |
{ | |
this.Name = Name; | |
this.Amount = Amount; | |
this.Price = Price; | |
this.VAT = VAT; | |
} | |
public override bool Equals(object obj) | |
{ | |
var x = obj as Item; | |
return x != null && | |
x.Name == Name && | |
x.Price == Price && | |
x.VAT == VAT && | |
x.Amount == Amount; | |
} | |
public override int GetHashCode() | |
{ | |
return Name.GetHashCode() ^ Price.GetHashCode() ^ VAT.GetHashCode() ^ Amount.GetHashCode(); | |
} | |
} | |
public readonly IEnumerable<Item> Items; | |
public Invoice(IEnumerable<Item> Items) | |
{ | |
this.Items = Items; | |
} | |
public class ItemList:List<Item> | |
{ | |
public void Add(String Name, decimal Amount, decimal Price, decimal VAT) | |
{ | |
this.Add(new Item(Name,Amount,Price,VAT)); | |
} | |
} | |
} | |
public enum VATCode | |
{ | |
Unknown, | |
Regular, | |
Food, | |
None | |
} | |
public class VATRates : Dictionary<VATCode, decimal> { } | |
public class Article | |
{ | |
public readonly string Id; | |
public readonly VATCode VATCode; | |
public readonly string Name; | |
public readonly decimal Price; | |
public Article(string Id, string Name, decimal Price, VATCode VAT) | |
{ | |
this.Id = Id; | |
this.Name = Name; | |
this.Price = Price; | |
this.VATCode = VAT; | |
} | |
public class Dictionary : Dictionary<string, Article> | |
{ | |
public void Add(string Id, string Name, int Price, VATCode VATCode) | |
{ | |
this.Add(Id, new Article(Id, Name, Price, VATCode)); | |
} | |
} | |
} | |
public class ArticleNotFoundException : Exception { } | |
public class VATRateNotFoundException : Exception { } | |
class Invoicer | |
{ | |
private VATRates VATRates; | |
private Article.Dictionary Articles; | |
public Invoicer(VATRates VATRates, Article.Dictionary Articles) | |
{ | |
this.VATRates = VATRates; | |
this.Articles = Articles; | |
} | |
public Invoice BillOrder(Order o) | |
{ | |
Invoice.ItemList items = new Invoice.ItemList(); | |
foreach (var orderline in o.Lines) | |
{ | |
Article article; | |
Decimal vatcode; | |
if (!Articles.TryGetValue(orderline.ArticleId, out article)) throw new ArticleNotFoundException(); | |
if (!VATRates.TryGetValue(article.VATCode, out vatcode)) throw new VATRateNotFoundException(); | |
var total = orderline.Amount * article.Price; | |
items.Add(article.Name, orderline.Amount, total, total * vatcode); | |
} | |
return new Invoice(items); | |
} | |
} | |
[TestClass] | |
public class Bill_order_tests | |
{ | |
Invoicer SUT; | |
Order.LineList OrderLines; | |
string unknown_article_id, article_id_with_unknown_vat; | |
[TestInitialize] | |
public void Setup() | |
{ | |
var rates = new VATRates(); | |
rates.Add(VATCode.Regular, .21M); | |
rates.Add(VATCode.Food, .06M); | |
rates.Add(VATCode.None, 0); | |
var articles = new Article.Dictionary(); | |
articles.Add("HTC-123", "HTC One Two TTT", 1000, VATCode.Regular); | |
articles.Add("APPL", "Apple blah", 1, VATCode.Food); | |
articles.Add("RNV", "Stairs renovation", 1500, VATCode.None); | |
articles.Add("???", "VAT Unknown", 123, VATCode.Unknown); | |
SUT = new Invoicer(rates, articles); | |
OrderLines = new Order.LineList(); | |
OrderLines.Add("HTC-123", 1); | |
OrderLines.Add("APPL", 12); | |
OrderLines.Add("RNV", 1); | |
unknown_article_id = "XYZ"; | |
article_id_with_unknown_vat = "???"; | |
} | |
[TestMethod] | |
public void Bill_an_order() | |
{ | |
var invoice = SUT.BillOrder(new Order(OrderLines)); | |
var invoiceItems = invoice.Items.ToArray(); | |
var expectedItems = new Invoice.ItemList(); | |
expectedItems.Add("HTC One Two TTT", 1, 1000, 210); | |
expectedItems.Add("Apple blah", 12, 12, 0.72M); | |
expectedItems.Add("Stairs renovation", 1, 1500, 0); | |
Assert.AreEqual(invoiceItems[0], expectedItems[0]); | |
Assert.AreEqual(invoiceItems[1], expectedItems[1]); | |
Assert.AreEqual(invoiceItems[2], expectedItems[2]); | |
} | |
[TestMethod] | |
[ExpectedException(typeof(ArticleNotFoundException))] | |
public void Bill_an_order_with_an_unkown_article() | |
{ | |
OrderLines.Add(unknown_article_id, 5); | |
SUT.BillOrder(new Order(OrderLines)); | |
} | |
[TestMethod] | |
[ExpectedException(typeof(VATRateNotFoundException))] | |
public void Bill_an_order_with_an_unknown_VAT_code() | |
{ | |
OrderLines.Add(article_id_with_unknown_vat, 5); | |
SUT.BillOrder(new Order(OrderLines)); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace UnitTestProject1 | |
open System | |
//Don't need first | when all on one line :) | |
type VATType = Unknown | Regular | Food | NoVat | |
type VatRates = Map<VATType, decimal> | |
//Type alias for ArticleId so reads clearly in Map declaration. | |
type ArticleId = string | |
type Article = {ArticleId: ArticleId; Name: string; Price: decimal; VATCode: VATType} | |
//Made this map like in your C# code. | |
type Articles = Map<ArticleId,Article> | |
type OrderLine = { ArticleId: ArticleId; Amount: decimal } | |
type OrderLines = List<OrderLine> | |
type Order = { Lines: OrderLines} | |
type InvoiceItem = {Name:string; Amount: decimal; Price: decimal; VAT: decimal} | |
type Invoice = { Items: List<InvoiceItem>} | |
exception ArticleNotFoundException | |
exception VATRateNotFoundException | |
module Invoicer = | |
let BillOrder (order: Order) (vatrates: VatRates) (articles: Articles) = | |
let invoiceLines = | |
order.Lines | |
|> Seq.map (fun line -> | |
//Get article price AND VAT rate or die. | |
let articlePrice, vatRate = | |
match articles.TryFind line.ArticleId with | |
| Some article -> | |
match vatrates.TryFind article.VATCode with | |
| Some vatRate -> article.Price, vatRate | |
| None -> raise VATRateNotFoundException | |
| None -> raise ArticleNotFoundException | |
let total = articlePrice * line.Amount | |
//Not sure about this bit as was not complete in yours. | |
{ Name=""; Amount=total; Price=articlePrice; VAT=vatRate*total } | |
) |> List.ofSeq | |
{ Items = invoiceLines} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This version should have the same behaviour as the original (although I've not run it). Would suggest the exceptions are removed. You could return a DU from BillOrder with Success or a Failure of errors.