From 864f416cd5036a21d231af75391c9344a7813d99 Mon Sep 17 00:00:00 2001 From: Paul L West Date: Fri, 7 Feb 2020 07:25:49 +0900 Subject: [PATCH 01/15] Initial Project Creation --- .gitattributes | 63 ++++++ .gitignore | 340 +++++++++++++++++++++++++++++++ CashRegister.sln | 25 +++ CashRegister/CashRegister.csproj | 8 + CashRegister/Program.cs | 11 + gitignore.git | 0 6 files changed, 447 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CashRegister.sln create mode 100644 CashRegister/CashRegister.csproj create mode 100644 CashRegister/Program.cs create mode 100644 gitignore.git diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1ff0c423 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4ce6fdde --- /dev/null +++ b/.gitignore @@ -0,0 +1,340 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb \ No newline at end of file diff --git a/CashRegister.sln b/CashRegister.sln new file mode 100644 index 00000000..b2e36cff --- /dev/null +++ b/CashRegister.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29503.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CashRegister", "CashRegister\CashRegister.csproj", "{A04E856B-69A9-41E4-B164-A2ACDFB2D709}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A04E856B-69A9-41E4-B164-A2ACDFB2D709}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A04E856B-69A9-41E4-B164-A2ACDFB2D709}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A04E856B-69A9-41E4-B164-A2ACDFB2D709}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A04E856B-69A9-41E4-B164-A2ACDFB2D709}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {84E678E1-0FBE-4993-B4DC-7A5354811E9D} + EndGlobalSection +EndGlobal diff --git a/CashRegister/CashRegister.csproj b/CashRegister/CashRegister.csproj new file mode 100644 index 00000000..958d2f1d --- /dev/null +++ b/CashRegister/CashRegister.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.0 + + + diff --git a/CashRegister/Program.cs b/CashRegister/Program.cs new file mode 100644 index 00000000..5ee8fafe --- /dev/null +++ b/CashRegister/Program.cs @@ -0,0 +1,11 @@ +using System; + +namespace CashRegister +{ + class Program + { + static void Main(string[] args) + { + } + } +} diff --git a/gitignore.git b/gitignore.git new file mode 100644 index 00000000..e69de29b From 8282dfa2305ffec8bfc4f37770779fb0a340d2d3 Mon Sep 17 00:00:00 2001 From: Paul L West Date: Fri, 7 Feb 2020 07:32:57 +0900 Subject: [PATCH 02/15] Added Testing Project Updated xunit to 2.4.1 Updated xunit.runner.visualstudio to 2.4.1 Updated ccoverlet.collector to 1.2.0 Updated Microsoft.NET.Test.Sdk to 16.5.0 --- CashRegister.sln | 8 +++++++- CashRegisterTests/CashRegisterTests.csproj | 22 ++++++++++++++++++++++ CashRegisterTests/UnitTest1.cs | 14 ++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 CashRegisterTests/CashRegisterTests.csproj create mode 100644 CashRegisterTests/UnitTest1.cs diff --git a/CashRegister.sln b/CashRegister.sln index b2e36cff..1b36328e 100644 --- a/CashRegister.sln +++ b/CashRegister.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29503.13 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CashRegister", "CashRegister\CashRegister.csproj", "{A04E856B-69A9-41E4-B164-A2ACDFB2D709}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CashRegister", "CashRegister\CashRegister.csproj", "{A04E856B-69A9-41E4-B164-A2ACDFB2D709}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CashRegisterTests", "CashRegisterTests\CashRegisterTests.csproj", "{369B12A2-ACEE-48AF-BAA8-690EF1EAD6C1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {A04E856B-69A9-41E4-B164-A2ACDFB2D709}.Debug|Any CPU.Build.0 = Debug|Any CPU {A04E856B-69A9-41E4-B164-A2ACDFB2D709}.Release|Any CPU.ActiveCfg = Release|Any CPU {A04E856B-69A9-41E4-B164-A2ACDFB2D709}.Release|Any CPU.Build.0 = Release|Any CPU + {369B12A2-ACEE-48AF-BAA8-690EF1EAD6C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {369B12A2-ACEE-48AF-BAA8-690EF1EAD6C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {369B12A2-ACEE-48AF-BAA8-690EF1EAD6C1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {369B12A2-ACEE-48AF-BAA8-690EF1EAD6C1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CashRegisterTests/CashRegisterTests.csproj b/CashRegisterTests/CashRegisterTests.csproj new file mode 100644 index 00000000..0a347c42 --- /dev/null +++ b/CashRegisterTests/CashRegisterTests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp3.0 + + false + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/CashRegisterTests/UnitTest1.cs b/CashRegisterTests/UnitTest1.cs new file mode 100644 index 00000000..12fb8464 --- /dev/null +++ b/CashRegisterTests/UnitTest1.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace CashRegisterTests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} From 841fd50fec01be3da1a99e4490e5abe6ef3dab2a Mon Sep 17 00:00:00 2001 From: Paul L West Date: Fri, 7 Feb 2020 13:16:38 +0900 Subject: [PATCH 03/15] Added Scaffolding for CashRegister, attempt to allow for different currencies. Needs refinement. --- .../CashRegister/Abstract/CashRegister.cs | 57 +++++++++++ .../CashRegister/Concrete/POSCashRegister.cs | 12 +++ .../Currencies/Abstract/Currency.cs | 46 +++++++++ .../Currencies/Abstract/ICurrency.cs | 11 +++ CashRegister/CashRegister/Currencies/Bill.cs | 7 ++ CashRegister/CashRegister/Currencies/Coin.cs | 7 ++ .../CashRegister/Currencies/Concrete/USD.cs | 30 ++++++ .../CashRegister/Currencies/Concrete/YEN.cs | 6 ++ CashRegister/CashRegister/Currencies/Money.cs | 14 +++ .../Stragegies/Abstract/ChangeCalculator.cs | 11 +++ .../Stragegies/Abstract/IChangeCalculator.cs | 7 ++ .../Concrete/Random3ChangeCalculator.cs | 21 ++++ .../Concrete/StandardChangeCalculator.cs | 14 +++ CashRegister/InputText.txt | 3 + CashRegister/_diagramCashRegister.cd | 97 +++++++++++++++++++ 15 files changed, 343 insertions(+) create mode 100644 CashRegister/CashRegister/Abstract/CashRegister.cs create mode 100644 CashRegister/CashRegister/Concrete/POSCashRegister.cs create mode 100644 CashRegister/CashRegister/Currencies/Abstract/Currency.cs create mode 100644 CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs create mode 100644 CashRegister/CashRegister/Currencies/Bill.cs create mode 100644 CashRegister/CashRegister/Currencies/Coin.cs create mode 100644 CashRegister/CashRegister/Currencies/Concrete/USD.cs create mode 100644 CashRegister/CashRegister/Currencies/Concrete/YEN.cs create mode 100644 CashRegister/CashRegister/Currencies/Money.cs create mode 100644 CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs create mode 100644 CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs create mode 100644 CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs create mode 100644 CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs create mode 100644 CashRegister/InputText.txt create mode 100644 CashRegister/_diagramCashRegister.cd diff --git a/CashRegister/CashRegister/Abstract/CashRegister.cs b/CashRegister/CashRegister/Abstract/CashRegister.cs new file mode 100644 index 00000000..2edcc8d4 --- /dev/null +++ b/CashRegister/CashRegister/Abstract/CashRegister.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +namespace CashRegisterConsumer +{ + public abstract class CashRegister + { + private ICurrency _price, _tender; + private IChangeCalculator _changeCalculator; + + public CashRegister(){} + public CashRegister(ICurrency priceCurrency, ICurrency tenderCurrency, IChangeCalculator changeCalculator) + { + RegisterPriceCurrency(priceCurrency); + RegisterTenderCurrency(tenderCurrency); + RegisterChangeCalculator(changeCalculator); + } + public void RegisterPriceCurrency(ICurrency priceCurrency) + { + this._price = priceCurrency; + } + public void RegisterTenderCurrency(ICurrency tenderCurrency) + { + this._tender = tenderCurrency; + } + public void RegisterChangeCalculator(IChangeCalculator changeCalculator) + { + this._changeCalculator = changeCalculator; + } + + public virtual string Tender(string path) + { + using (StreamReader sr = new StreamReader(path, System.Text.Encoding.UTF8)) + { + return Tender(sr); + } + } + public virtual string Tender(StreamReader sr) + { + InputTransaction(sr); + return _changeCalculator.Calculate(_price, _tender); + } + + private void InputTransaction(StreamReader sr) + { + //todo Using the file stream, load the price and tender + //todo into the _price and _tender fields + do + { + + + } while (!sr.EndOfStream); + throw new NotImplementedException(); + } + + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Concrete/POSCashRegister.cs b/CashRegister/CashRegister/Concrete/POSCashRegister.cs new file mode 100644 index 00000000..2e72fae4 --- /dev/null +++ b/CashRegister/CashRegister/Concrete/POSCashRegister.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CashRegisterConsumer +{ + public class POSCashRegister : CashRegister + { + public POSCashRegister() { } + public POSCashRegister(ICurrency price, ICurrency tender, IChangeCalculator changeCalculator):base(price, tender, changeCalculator) { } + } +} diff --git a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs new file mode 100644 index 00000000..fed8cf71 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace CashRegisterConsumer +{ + public abstract class Currency : ICurrency + { + protected List _bills; + protected List _coins; + + + public List Bills + { + get { return _bills; } + } + + public List Coins + { + get { return _coins; } + } + + public decimal TotalValue() + { + //todo Calculate Total Value for both bills and coins + //todo return the decimal value. + throw new System.NotImplementedException(); + } + + public void AddMoney(decimal value) + { + //todo Using the "Bills" and "Coins", we can + //todo we can add the proper amount of bills and coins from + //todo the decimal equivilent + throw new System.NotImplementedException(); + } + public void AddMoney(Money money) + { + //todo Using the "Bills" and "Coins", we can + //todo we can add the proper amount of bills and coins from + //todo the Money equivilent + throw new System.NotImplementedException(); + } + + //? Possible future enhancement + //public decimal CreditLine { get; } + } +} diff --git a/CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs b/CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs new file mode 100644 index 00000000..49d2377e --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace CashRegisterConsumer +{ + public interface ICurrency + { + decimal TotalValue(); + void AddMoney(decimal value); + void AddMoney(Money money); + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Bill.cs b/CashRegister/CashRegister/Currencies/Bill.cs new file mode 100644 index 00000000..cb8d28e9 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Bill.cs @@ -0,0 +1,7 @@ +namespace CashRegisterConsumer +{ + public class Bill : Money + { + public Bill(int value, string name):base(value, name) { } + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Coin.cs b/CashRegister/CashRegister/Currencies/Coin.cs new file mode 100644 index 00000000..5005c7bc --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Coin.cs @@ -0,0 +1,7 @@ +namespace CashRegisterConsumer +{ + public class Coin : Money + { + public Coin(int value, string name) : base(value, name) { } + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Concrete/USD.cs b/CashRegister/CashRegister/Currencies/Concrete/USD.cs new file mode 100644 index 00000000..87416f45 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Concrete/USD.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace CashRegisterConsumer +{ + internal class USD : Currency + { + public USD():base() + { + this._bills = new List() { + new Bill(100000, "One Hundred Thousand"), + new Bill(10000, "Ten Thousand"), + new Bill(5000, "Five Thousand"), + new Bill(1000, "Thousand"), + new Bill(500, "Five Hundred"), + new Bill(100, "Hundred"), + new Bill(50, "Fifty"), + new Bill(20, "Twenty"), + new Bill(5, "Five"), + new Bill(1, "Dollar") + }; + + this._coins = new List() { + new Coin(25, "Quarter"), + new Coin(10, "Dime"), + new Coin(5, "Nickel"), + new Coin(1, "Penny") + }; ; + } + } +} diff --git a/CashRegister/CashRegister/Currencies/Concrete/YEN.cs b/CashRegister/CashRegister/Currencies/Concrete/YEN.cs new file mode 100644 index 00000000..a98ad463 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Concrete/YEN.cs @@ -0,0 +1,6 @@ +namespace CashRegisterConsumer +{ + class YEN : Currency + { + } +} diff --git a/CashRegister/CashRegister/Currencies/Money.cs b/CashRegister/CashRegister/Currencies/Money.cs new file mode 100644 index 00000000..aa464d8c --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Money.cs @@ -0,0 +1,14 @@ +namespace CashRegisterConsumer +{ + public abstract class Money + { + private int _value; + private string _name; + + public Money(int value, string name) + { + this._value = value; + this._name = name; + } + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs new file mode 100644 index 00000000..a5895d74 --- /dev/null +++ b/CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CashRegisterConsumer +{ + public abstract class ChangeCalculator : IChangeCalculator + { + public abstract string Calculate(ICurrency _price, ICurrency _tender); + } +} diff --git a/CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs new file mode 100644 index 00000000..8ee3bd1e --- /dev/null +++ b/CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs @@ -0,0 +1,7 @@ +namespace CashRegisterConsumer +{ + public interface IChangeCalculator + { + string Calculate(ICurrency _price, ICurrency _tender); + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs new file mode 100644 index 00000000..c5f799cb --- /dev/null +++ b/CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs @@ -0,0 +1,21 @@ +using System; + +namespace CashRegisterConsumer +{ + internal class Random3ChangeCalculator : ChangeCalculator + { + public override string Calculate(ICurrency _price, ICurrency _tender) + { + if (Math.Abs(((_tender.TotalValue() * 100) % 10)) == 3) + { + //todo Create "random" strategy + throw new NotImplementedException(); + } + else + { + throw new NotImplementedException(); + } + } + + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs new file mode 100644 index 00000000..d766626d --- /dev/null +++ b/CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CashRegisterConsumer +{ + internal class StandardChangeCalculator : ChangeCalculator + { + public override string Calculate(ICurrency _price, ICurrency _tender) + { + throw new NotImplementedException(); + } + } +} diff --git a/CashRegister/InputText.txt b/CashRegister/InputText.txt new file mode 100644 index 00000000..197cfb3e --- /dev/null +++ b/CashRegister/InputText.txt @@ -0,0 +1,3 @@ +2.12,3.00 +1.97,2.00 +3.33,5.00 \ No newline at end of file diff --git a/CashRegister/_diagramCashRegister.cd b/CashRegister/_diagramCashRegister.cd new file mode 100644 index 00000000..6ace4e0a --- /dev/null +++ b/CashRegister/_diagramCashRegister.cd @@ -0,0 +1,97 @@ + + + + + + AgCAAAAAAAAAQAAAAIAAAABAAQAAAQAEAAAAAAAAAAA= + CashRegister\Abstract\CashRegister.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Concrete\POSCashRegister.cs + + + + + + AAAAAAIAAAAAAAAAAAAADEAACAAAAAAAAAAACAAAAAA= + CashRegister\Currencies\Abstract\Currency.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Concrete\USD.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Concrete\YEN.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Bill.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Coin.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + CashRegister\Stragegies\Abstract\ChangeCalculator.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + CashRegister\Stragegies\Concrete\StandardChangeCalculator.cs + + + + + + ABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Money.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + CashRegister\Stragegies\Concrete\Random3ChangeCalculator.cs + + + + + + AAAAAAIAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Abstract\ICurrency.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + CashRegister\Stragegies\Abstract\IChangeCalculator.cs + + + + \ No newline at end of file From 5b1d01fcebd605af0734feef024501188fe1aa1b Mon Sep 17 00:00:00 2001 From: Paul L West Date: Fri, 7 Feb 2020 13:33:50 +0900 Subject: [PATCH 04/15] Added consumer code --- CashRegister/CashRegister.csproj | 9 ++++++ CashRegister/Program.cs | 24 ++++++++++++++- CashRegister/_diagramCashRegister.cd | 45 +++++++++++++--------------- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/CashRegister/CashRegister.csproj b/CashRegister/CashRegister.csproj index 958d2f1d..2a9a5059 100644 --- a/CashRegister/CashRegister.csproj +++ b/CashRegister/CashRegister.csproj @@ -5,4 +5,13 @@ netcoreapp3.0 + + + + + + + + + diff --git a/CashRegister/Program.cs b/CashRegister/Program.cs index 5ee8fafe..c3f7bd6f 100644 --- a/CashRegister/Program.cs +++ b/CashRegister/Program.cs @@ -1,11 +1,33 @@ using System; -namespace CashRegister +namespace CashRegisterConsumer { class Program { static void Main(string[] args) { + + // Consumer Console... This is for the benefit of the code submission + // to help the evaluators of my code to see the CashRegister Module + // as it would be used by a consuming class in real world development + // Consider the console the POS where the "CashRegister" just tenders transactions. + Console.WriteLine(@"The Console is the ""CONSUMER"" of the CashRegister Module."); + Console.WriteLine(); + Console.WriteLine(@"Press any button to continue with using the CashRegister Module. (Simulates POS ""Tender"" actions)"); + Console.ReadKey(); + + + CashRegister register = new POSCashRegister(new USD(), new USD(), new Random3ChangeCalculator()); + //register.RegisterPriceCurrency(new USD()); + //register.RegisterTenderCurrency(new USD()); + //register.RegisterChangeCalculator(new Random3ChangeCalculator()); + + var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\InputText.txt"); + + + Console.WriteLine(result); + Console.ReadKey(); + } } } diff --git a/CashRegister/_diagramCashRegister.cd b/CashRegister/_diagramCashRegister.cd index 6ace4e0a..6455b12e 100644 --- a/CashRegister/_diagramCashRegister.cd +++ b/CashRegister/_diagramCashRegister.cd @@ -1,57 +1,52 @@  - + AgCAAAAAAAAAQAAAAIAAAABAAQAAAQAEAAAAAAAAAAA= CashRegister\Abstract\CashRegister.cs + + + + + - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= CashRegister\Concrete\POSCashRegister.cs - + AAAAAAIAAAAAAAAAAAAADEAACAAAAAAAAAAACAAAAAA= CashRegister\Currencies\Abstract\Currency.cs + + + + - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= CashRegister\Currencies\Concrete\USD.cs - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= CashRegister\Currencies\Concrete\YEN.cs - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - CashRegister\Currencies\Bill.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - CashRegister\Currencies\Coin.cs - - - + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= CashRegister\Stragegies\Abstract\ChangeCalculator.cs @@ -59,35 +54,35 @@ - + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= CashRegister\Stragegies\Concrete\StandardChangeCalculator.cs - + ABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= CashRegister\Currencies\Money.cs - + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= CashRegister\Stragegies\Concrete\Random3ChangeCalculator.cs - + AAAAAAIAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA= CashRegister\Currencies\Abstract\ICurrency.cs - + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= CashRegister\Stragegies\Abstract\IChangeCalculator.cs From b0bab7e1a685aacff09b09b45e75a8ecc48497cc Mon Sep 17 00:00:00 2001 From: Paul L West Date: Sun, 9 Feb 2020 08:56:56 +0900 Subject: [PATCH 05/15] Initial structure and implementation completed. Unit Tests completed. Some design flaws detected during coding. Will evaluate these and determine future course of action. CashRegister.Tender return should be looked at. It seems too static at this point. Money/Currency structure still needs works, and were somewhat difficult to setup good tests with their current design. --- CashRegister/CashRegister.csproj | 7 +- .../CashRegister/Abstract/CashRegister.cs | 113 +++++++--- .../CashRegister/Concrete/POSCashRegister.cs | 3 +- .../Currencies/Abstract/Currency.cs | 62 +++--- .../Currencies/Abstract/ICurrency.cs | 8 +- .../Currencies/Abstract/IMoney.cs | 13 ++ .../CashRegister/Currencies/Abstract/Money.cs | 58 +++++ CashRegister/CashRegister/Currencies/Bill.cs | 7 - CashRegister/CashRegister/Currencies/Coin.cs | 7 - .../CashRegister/Currencies/Concrete/Bill.cs | 8 + .../CashRegister/Currencies/Concrete/Coin.cs | 9 + .../CashRegister/Currencies/Concrete/USD.cs | 39 ++-- .../CashRegister/Currencies/Concrete/YEN.cs | 23 +- CashRegister/CashRegister/Currencies/Money.cs | 14 -- .../Stragegies/Abstract/ChangeCalculator.cs | 11 - .../Stragegies/Abstract/IChangeCalculator.cs | 7 - .../Concrete/Random3ChangeCalculator.cs | 21 -- .../Concrete/StandardChangeCalculator.cs | 14 -- .../Strategies/Abstract/ITenderStrategy.cs | 7 + .../Strategies/Abstract/TenderStrategy.cs | 11 + .../Strategies/Concrete/Random3Strategy.cs | 43 ++++ .../Concrete/StandardTenderStrategy.cs | 32 +++ .../USD Input File.txt} | 3 +- .../CashRegisterInputFiles/YEN Input File.txt | 4 + .../InvalidCurrencyException.cs | 25 +++ .../NotEnoughTenderException.cs | 25 +++ CashRegister/Program.cs | 18 +- CashRegister/_diagramCashRegister.cd | 74 ++++--- CashRegisterTests/CashRegisterTests.cs | 150 +++++++++++++ CashRegisterTests/CashRegisterTests.csproj | 7 +- CashRegisterTests/CurrencyTests.cs | 207 ++++++++++++++++++ CashRegisterTests/MoneyTests.cs | 121 ++++++++++ CashRegisterTests/Random3StrategyTests.cs | 196 +++++++++++++++++ .../StandardTenderStrategyTests.cs | 103 +++++++++ .../Test Input Files/EmptyFile.txt | 1 + .../Test Input Files/EmptyLineFile.txt | 2 + .../SingleTransactionTestFile.txt | 1 + .../Test Input Files/TenderLessThanPrice.txt | 1 + .../ValueTests/PriceValueTestFile.txt | 1 + .../ValueTests/TenderValueTestFile.txt | 1 + CashRegisterTests/UnitTest1.cs | 14 -- 41 files changed, 1254 insertions(+), 217 deletions(-) create mode 100644 CashRegister/CashRegister/Currencies/Abstract/IMoney.cs create mode 100644 CashRegister/CashRegister/Currencies/Abstract/Money.cs delete mode 100644 CashRegister/CashRegister/Currencies/Bill.cs delete mode 100644 CashRegister/CashRegister/Currencies/Coin.cs create mode 100644 CashRegister/CashRegister/Currencies/Concrete/Bill.cs create mode 100644 CashRegister/CashRegister/Currencies/Concrete/Coin.cs delete mode 100644 CashRegister/CashRegister/Currencies/Money.cs delete mode 100644 CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs delete mode 100644 CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs delete mode 100644 CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs delete mode 100644 CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs create mode 100644 CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs create mode 100644 CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs create mode 100644 CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs create mode 100644 CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs rename CashRegister/{InputText.txt => CashRegisterInputFiles/USD Input File.txt} (54%) create mode 100644 CashRegister/CashRegisterInputFiles/YEN Input File.txt create mode 100644 CashRegister/Custom Exceptions/InvalidCurrencyException.cs create mode 100644 CashRegister/Custom Exceptions/NotEnoughTenderException.cs create mode 100644 CashRegisterTests/CashRegisterTests.cs create mode 100644 CashRegisterTests/CurrencyTests.cs create mode 100644 CashRegisterTests/MoneyTests.cs create mode 100644 CashRegisterTests/Random3StrategyTests.cs create mode 100644 CashRegisterTests/StandardTenderStrategyTests.cs create mode 100644 CashRegisterTests/Test Input Files/EmptyFile.txt create mode 100644 CashRegisterTests/Test Input Files/EmptyLineFile.txt create mode 100644 CashRegisterTests/Test Input Files/SingleTransactionTestFile.txt create mode 100644 CashRegisterTests/Test Input Files/TenderLessThanPrice.txt create mode 100644 CashRegisterTests/Test Input Files/ValueTests/PriceValueTestFile.txt create mode 100644 CashRegisterTests/Test Input Files/ValueTests/TenderValueTestFile.txt delete mode 100644 CashRegisterTests/UnitTest1.cs diff --git a/CashRegister/CashRegister.csproj b/CashRegister/CashRegister.csproj index 2a9a5059..3f0d6a5c 100644 --- a/CashRegister/CashRegister.csproj +++ b/CashRegister/CashRegister.csproj @@ -6,12 +6,13 @@ - - + + + - + diff --git a/CashRegister/CashRegister/Abstract/CashRegister.cs b/CashRegister/CashRegister/Abstract/CashRegister.cs index 2edcc8d4..5513487b 100644 --- a/CashRegister/CashRegister/Abstract/CashRegister.cs +++ b/CashRegister/CashRegister/Abstract/CashRegister.cs @@ -1,56 +1,109 @@ using System; +using System.Diagnostics; using System.IO; +using System.Text; namespace CashRegisterConsumer { public abstract class CashRegister { - private ICurrency _price, _tender; - private IChangeCalculator _changeCalculator; + private ICurrency _currency; + private decimal _price, _tender; + private int _transactionCount; + private ITenderStrategy _tenderStrategy; - public CashRegister(){} - public CashRegister(ICurrency priceCurrency, ICurrency tenderCurrency, IChangeCalculator changeCalculator) - { - RegisterPriceCurrency(priceCurrency); - RegisterTenderCurrency(tenderCurrency); - RegisterChangeCalculator(changeCalculator); - } - public void RegisterPriceCurrency(ICurrency priceCurrency) + public decimal PriceValue { get { return _price; } set { _price = value; } } + public decimal TenderValue { get { return _tender; } set { _tender = value; } } + + public CashRegister(ICurrency currency, ITenderStrategy tenderStrategy) { - this._price = priceCurrency; + RegisterCurrency(currency); + RegisterTenderStrategy(tenderStrategy); } - public void RegisterTenderCurrency(ICurrency tenderCurrency) + public virtual void RegisterCurrency(ICurrency currency) { - this._tender = tenderCurrency; + if (currency == null) + throw new NullReferenceException("Attempt to register a null currency is invalid."); + if (currency.AllDenominations.Count == 0) + throw new InvalidCurrencyException($"No denominations found in currency {currency.ToString()}"); + + this._currency = currency; } - public void RegisterChangeCalculator(IChangeCalculator changeCalculator) + public virtual void RegisterTenderStrategy(ITenderStrategy tenderStrategy) { - this._changeCalculator = changeCalculator; + if (tenderStrategy == null) + throw new NullReferenceException("Attempt to register a null tender strategy is invalid."); + + this._tenderStrategy = tenderStrategy; } - public virtual string Tender(string path) + public string Tender(string path) { - using (StreamReader sr = new StreamReader(path, System.Text.Encoding.UTF8)) + // ensure that the path is not null + if (path == null || path == string.Empty) + throw new FileNotFoundException("Input file not found.", path); + + // setup our stringbuilder for the response + StringBuilder tenderedValues = new StringBuilder(); + try + { + // using to ensure stream closure + using (StreamReader sr = new StreamReader(path, System.Text.Encoding.UTF8)) + { + // if the file is empty, throw format excpetion with message + if (sr.EndOfStream) + throw new FormatException($"The file {path} was empty"); + + while (!sr.EndOfStream) + { + + // log transaction count (for exception handling) + _transactionCount++; + // setup the _price and _tender for this transaction + SetTransactionAmounts(sr); + // add the transaction calculation based on the strategy to the return string + tenderedValues.Append(_tenderStrategy.Calculate(_currency, _price, _tender) + "\n"); + }; + } + + // return the tendered value string for all transactions + return tenderedValues.ToString(); + } + catch (FormatException e) // throw a generic message with a more specific inner message. { - return Tender(sr); + throw new FormatException($"The file {path} was not in the correct format", e); + } + catch (Exception) // Is something missed? + { + throw; } } - public virtual string Tender(StreamReader sr) + private void SetTransactionAmounts(StreamReader sr) { - InputTransaction(sr); - return _changeCalculator.Calculate(_price, _tender); - } - - private void InputTransaction(StreamReader sr) - { - //todo Using the file stream, load the price and tender - //todo into the _price and _tender fields - do + try { + // reset for new transaction + _currency.Clear(); + _price = _tender = 0; + // read the next line and set the _price and _tender + // NOTE: I used the Parse over TryParse to ensure non-numeric values throw an exception + var input = sr.ReadLine(); + _price = Decimal.Parse(input.Split(",")[0]); + _tender = Decimal.Parse(input.Split(",")[1]); - } while (!sr.EndOfStream); - throw new NotImplementedException(); + // ensure there is enough tender for the price. (they can be equal) + if (_tender < _price) + throw new NotEnoughTenderException($"Tender value less than price. Deficiency: {_price - _tender} on line {_transactionCount}"); + } + catch (NotEnoughTenderException) + { + throw; + } + catch (Exception e) + { + throw new FormatException(String.Format("{0} : Line {1}", e.Message, _transactionCount)); + } } } diff --git a/CashRegister/CashRegister/Concrete/POSCashRegister.cs b/CashRegister/CashRegister/Concrete/POSCashRegister.cs index 2e72fae4..04f2a026 100644 --- a/CashRegister/CashRegister/Concrete/POSCashRegister.cs +++ b/CashRegister/CashRegister/Concrete/POSCashRegister.cs @@ -6,7 +6,6 @@ namespace CashRegisterConsumer { public class POSCashRegister : CashRegister { - public POSCashRegister() { } - public POSCashRegister(ICurrency price, ICurrency tender, IChangeCalculator changeCalculator):base(price, tender, changeCalculator) { } + public POSCashRegister(ICurrency currency, ITenderStrategy tenderStrategy):base(currency, tenderStrategy) { } } } diff --git a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs index fed8cf71..f0b1d7ee 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; namespace CashRegisterConsumer { @@ -7,40 +10,47 @@ public abstract class Currency : ICurrency protected List _bills; protected List _coins; + public List Bills { get { return _bills; } } + public List Coins { get { return _coins; } } + public List AllDenominations { get { return _bills.Concat(_coins).ToList(); } } - public List Bills + public Currency() { - get { return _bills; } - } + this._bills = new List(); + this._coins = new List(); - public List Coins - { - get { return _coins; } + InitializeCurrency(); + this._bills.Sort(); + this._bills.Reverse(); + this._coins.Sort(); + this._coins.Reverse(); } - public decimal TotalValue() - { - //todo Calculate Total Value for both bills and coins - //todo return the decimal value. - throw new System.NotImplementedException(); - } + protected abstract void InitializeCurrency(); - public void AddMoney(decimal value) + public void Clear() { - //todo Using the "Bills" and "Coins", we can - //todo we can add the proper amount of bills and coins from - //todo the decimal equivilent - throw new System.NotImplementedException(); + foreach (Money money in AllDenominations) + { + money.Clear(); + } } - public void AddMoney(Money money) + + public override string ToString() { - //todo Using the "Bills" and "Coins", we can - //todo we can add the proper amount of bills and coins from - //todo the Money equivilent - throw new System.NotImplementedException(); - } + StringBuilder sr = new StringBuilder(); + foreach (Money money in AllDenominations) + { + if (money.Count > 0) + { + sr.Append(String.Format("{0} {1},", money.Count, money.Name)); + } + } - //? Possible future enhancement - //public decimal CreditLine { get; } + if (sr.Length == 0) + return "No Change Due."; // not part of the requirements, yet exact change is a viable value. + else + return sr.ToString().Trim(','); + } } } diff --git a/CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs b/CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs index 49d2377e..9ae31a45 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/ICurrency.cs @@ -4,8 +4,10 @@ namespace CashRegisterConsumer { public interface ICurrency { - decimal TotalValue(); - void AddMoney(decimal value); - void AddMoney(Money money); + List Bills { get; } + List Coins { get; } + List AllDenominations { get; } + + void Clear(); } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Abstract/IMoney.cs b/CashRegister/CashRegister/Currencies/Abstract/IMoney.cs new file mode 100644 index 00000000..72cbcdb2 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Abstract/IMoney.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CashRegisterConsumer +{ + public interface IMoney + { + void Add(int count); + void Subtract(int count); + void Clear(); + } +} diff --git a/CashRegister/CashRegister/Currencies/Abstract/Money.cs b/CashRegister/CashRegister/Currencies/Abstract/Money.cs new file mode 100644 index 00000000..cdb05041 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Abstract/Money.cs @@ -0,0 +1,58 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace CashRegisterConsumer +{ + public abstract class Money : IComparable + { + private int _count; + + private readonly decimal _denomination; + private readonly string _singleName; + private readonly string _pluralName; + + public decimal Denomination { get { return _denomination; } } + public string Name { get { return (_count == 1) ? _singleName : _pluralName; } } + public int Count { get { return _count; } } + + public Money() { } // for Moq + public Money(decimal denomination, string singleName, string pluralName) + { + this._denomination = denomination; + this._singleName = singleName; + this._pluralName = pluralName; + this._count = 0; + } + public Money(decimal denomination, string singleName, string pluralName, int count) + { + this._denomination = denomination; + this._singleName = singleName; + this._pluralName = pluralName; + this._count = count; + } + + public void Add(int count) + { + _count += count; + } + public void Subtract(int count) + { + _count -= count; + } + public void Clear() + { + _count = 0; + } + public int CompareTo(object other) + { + if (other == null) return 1; + + Money otherMoney = other as Money; + if (otherMoney != null) + return this._denomination.CompareTo(otherMoney._denomination); + else + throw new ArgumentException("Object is not Money"); + } + + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Bill.cs b/CashRegister/CashRegister/Currencies/Bill.cs deleted file mode 100644 index cb8d28e9..00000000 --- a/CashRegister/CashRegister/Currencies/Bill.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CashRegisterConsumer -{ - public class Bill : Money - { - public Bill(int value, string name):base(value, name) { } - } -} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Coin.cs b/CashRegister/CashRegister/Currencies/Coin.cs deleted file mode 100644 index 5005c7bc..00000000 --- a/CashRegister/CashRegister/Currencies/Coin.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CashRegisterConsumer -{ - public class Coin : Money - { - public Coin(int value, string name) : base(value, name) { } - } -} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Concrete/Bill.cs b/CashRegister/CashRegister/Currencies/Concrete/Bill.cs new file mode 100644 index 00000000..ffc641f1 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Concrete/Bill.cs @@ -0,0 +1,8 @@ +namespace CashRegisterConsumer +{ + public class Bill : Money + { + public Bill(decimal denomination, string singleName, string pluralName) : base(denomination, singleName, pluralName) { } + public Bill(decimal denomination, string singleName, string pluralName, int count) : base(denomination, singleName, pluralName, count) { } + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Concrete/Coin.cs b/CashRegister/CashRegister/Currencies/Concrete/Coin.cs new file mode 100644 index 00000000..2bff0858 --- /dev/null +++ b/CashRegister/CashRegister/Currencies/Concrete/Coin.cs @@ -0,0 +1,9 @@ +namespace CashRegisterConsumer +{ + public class Coin : Money + { + public Coin(decimal denomination, string singleName, string pluralName) : base(denomination, singleName, pluralName) { } + public Coin(decimal denomination, string singleName, string pluralName, int count) : base(denomination, singleName, pluralName, count) { } + + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Concrete/USD.cs b/CashRegister/CashRegister/Currencies/Concrete/USD.cs index 87416f45..d4d810ee 100644 --- a/CashRegister/CashRegister/Currencies/Concrete/USD.cs +++ b/CashRegister/CashRegister/Currencies/Concrete/USD.cs @@ -4,27 +4,30 @@ namespace CashRegisterConsumer { internal class USD : Currency { - public USD():base() + public USD() : base() { } + + protected override void InitializeCurrency() { - this._bills = new List() { - new Bill(100000, "One Hundred Thousand"), - new Bill(10000, "Ten Thousand"), - new Bill(5000, "Five Thousand"), - new Bill(1000, "Thousand"), - new Bill(500, "Five Hundred"), - new Bill(100, "Hundred"), - new Bill(50, "Fifty"), - new Bill(20, "Twenty"), - new Bill(5, "Five"), - new Bill(1, "Dollar") + this._bills = new List() { + new Bill(100000, "one hundred thousand", "one hundred thousands"), + new Bill(10000, "ten thousand", "ten thousands"), + new Bill(5000, "five thousand", "five thousands"), + new Bill(1000, "thousand", "thousands"), + new Bill(500, "five hundred", "five hundreds"), + new Bill(100, "hundred", "hundreds"), + new Bill(50, "fifty", "fifties"), + new Bill(20, "twenty", "twenties"), + new Bill(10, "ten", "tens"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars") }; - this._coins = new List() { - new Coin(25, "Quarter"), - new Coin(10, "Dime"), - new Coin(5, "Nickel"), - new Coin(1, "Penny") - }; ; + this._coins = new List() { + new Coin(.25m, "quarter", "quarters"), + new Coin(.10m, "dime", "dimes"), + new Coin(.05m, "nickel", "nickels"), + new Coin(.01m, "penny","pennies") + }; } } } diff --git a/CashRegister/CashRegister/Currencies/Concrete/YEN.cs b/CashRegister/CashRegister/Currencies/Concrete/YEN.cs index a98ad463..2738a91a 100644 --- a/CashRegister/CashRegister/Currencies/Concrete/YEN.cs +++ b/CashRegister/CashRegister/Currencies/Concrete/YEN.cs @@ -1,6 +1,27 @@ -namespace CashRegisterConsumer +using System.Collections.Generic; + +namespace CashRegisterConsumer { class YEN : Currency { + public YEN() : base() { } + protected override void InitializeCurrency() + { + this._bills = new List() { + new Bill(10000, "10000-yen note", "10000-yen notes"), + new Bill(5000, "5000-yen note", "5000-yen notes"), + new Bill(2000, "2000-yen note", "2000-yen notes"), + new Bill(1000, "1000-yen note", "1000-yen notes") + }; + + this._coins = new List() { + new Coin(500, "500-yen coin", "500-yen coins"), + new Coin(100, "100-yen coin", "100-yen coins"), + new Coin(50, "50-yen coin", "50-yen coins"), + new Coin(10, "10-yen coin", "10-yen coins"), + new Coin(5, "5-yen coin", "5-yen coins"), + new Coin(1, "1-yen coin","1-yen coins") + }; + } } } diff --git a/CashRegister/CashRegister/Currencies/Money.cs b/CashRegister/CashRegister/Currencies/Money.cs deleted file mode 100644 index aa464d8c..00000000 --- a/CashRegister/CashRegister/Currencies/Money.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace CashRegisterConsumer -{ - public abstract class Money - { - private int _value; - private string _name; - - public Money(int value, string name) - { - this._value = value; - this._name = name; - } - } -} \ No newline at end of file diff --git a/CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs deleted file mode 100644 index a5895d74..00000000 --- a/CashRegister/CashRegister/Stragegies/Abstract/ChangeCalculator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CashRegisterConsumer -{ - public abstract class ChangeCalculator : IChangeCalculator - { - public abstract string Calculate(ICurrency _price, ICurrency _tender); - } -} diff --git a/CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs deleted file mode 100644 index 8ee3bd1e..00000000 --- a/CashRegister/CashRegister/Stragegies/Abstract/IChangeCalculator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CashRegisterConsumer -{ - public interface IChangeCalculator - { - string Calculate(ICurrency _price, ICurrency _tender); - } -} \ No newline at end of file diff --git a/CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs deleted file mode 100644 index c5f799cb..00000000 --- a/CashRegister/CashRegister/Stragegies/Concrete/Random3ChangeCalculator.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace CashRegisterConsumer -{ - internal class Random3ChangeCalculator : ChangeCalculator - { - public override string Calculate(ICurrency _price, ICurrency _tender) - { - if (Math.Abs(((_tender.TotalValue() * 100) % 10)) == 3) - { - //todo Create "random" strategy - throw new NotImplementedException(); - } - else - { - throw new NotImplementedException(); - } - } - - } -} \ No newline at end of file diff --git a/CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs b/CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs deleted file mode 100644 index d766626d..00000000 --- a/CashRegister/CashRegister/Stragegies/Concrete/StandardChangeCalculator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CashRegisterConsumer -{ - internal class StandardChangeCalculator : ChangeCalculator - { - public override string Calculate(ICurrency _price, ICurrency _tender) - { - throw new NotImplementedException(); - } - } -} diff --git a/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs new file mode 100644 index 00000000..a30fe045 --- /dev/null +++ b/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs @@ -0,0 +1,7 @@ +namespace CashRegisterConsumer +{ + public interface ITenderStrategy + { + string Calculate(ICurrency currency, decimal price, decimal tender); + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs new file mode 100644 index 00000000..2be445bf --- /dev/null +++ b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CashRegisterConsumer +{ + public abstract class TenderStrategy : ITenderStrategy + { + public abstract string Calculate(ICurrency currency, decimal price, decimal tender); + } +} diff --git a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs new file mode 100644 index 00000000..ee5c62bb --- /dev/null +++ b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace CashRegisterConsumer +{ + public class Random3Strategy : TenderStrategy + { + public override string Calculate(ICurrency currency, decimal price, decimal tender) + { + if (currency.AllDenominations.Count == 0) + throw new InvalidCurrencyException("No currency denominations found"); + + if (Math.Abs(((price * 100) % 10)) == 3) + { + Random random = new Random(); + int count; + decimal change = tender - price; + + while (change >= currency.AllDenominations.Min(x => x.Denomination)) + { + foreach (Money money in currency.AllDenominations) + { + if (money.Denomination <= change) // if the current denomination is less than the change + { + count = random.Next(1, (int)Math.Floor(change / money.Denomination)); // get a random count (not bigger than the change) + money.Add(count); // add the appropriate amount of this denomination based on our random + change -= (money.Denomination * count); // remove the money denomination(times count) from the change + } + } + } + return currency.ToString(); + } + else + { + // when not using the "random", we can just use the standard strategy + return new StandardTenderStrategy().Calculate(currency, price, tender); + } + } + + } +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs new file mode 100644 index 00000000..1d22782d --- /dev/null +++ b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace CashRegisterConsumer +{ + public class StandardTenderStrategy : TenderStrategy + { + public override string Calculate(ICurrency currency, decimal price, decimal tender) + { + if (currency.AllDenominations.Count == 0) + throw new InvalidCurrencyException("No currency denominations found"); + + decimal change = tender - price; + // this is to ensure that coinage less than the smallest denomination will not cause an infinite loop. + // the extra "change" is ignored.. this can occur because some currencies do not have decimal value denominations (YEN) + while (change >= currency.AllDenominations.Min(x => x.Denomination)) + { + foreach (Money money in currency.AllDenominations) + { + while (change >= money.Denomination) + { + money.Add(1); + change -= money.Denomination; + } + } + } + return currency.ToString(); + } + } +} diff --git a/CashRegister/InputText.txt b/CashRegister/CashRegisterInputFiles/USD Input File.txt similarity index 54% rename from CashRegister/InputText.txt rename to CashRegister/CashRegisterInputFiles/USD Input File.txt index 197cfb3e..5d0bf229 100644 --- a/CashRegister/InputText.txt +++ b/CashRegister/CashRegisterInputFiles/USD Input File.txt @@ -1,3 +1,4 @@ 2.12,3.00 1.97,2.00 -3.33,5.00 \ No newline at end of file +3.33,5.00 +5.00,5.00 \ No newline at end of file diff --git a/CashRegister/CashRegisterInputFiles/YEN Input File.txt b/CashRegister/CashRegisterInputFiles/YEN Input File.txt new file mode 100644 index 00000000..59bef7ac --- /dev/null +++ b/CashRegister/CashRegisterInputFiles/YEN Input File.txt @@ -0,0 +1,4 @@ +5000, 10000 +100, 1000 +350, 500 +120, 12000 \ No newline at end of file diff --git a/CashRegister/Custom Exceptions/InvalidCurrencyException.cs b/CashRegister/Custom Exceptions/InvalidCurrencyException.cs new file mode 100644 index 00000000..a3efb553 --- /dev/null +++ b/CashRegister/Custom Exceptions/InvalidCurrencyException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; + +namespace CashRegisterConsumer +{ + [Serializable] + public class InvalidCurrencyException : Exception + { + public InvalidCurrencyException() + { + } + + public InvalidCurrencyException(string message) : base(message) + { + } + + public InvalidCurrencyException(string message, Exception innerException) : base(message, innerException) + { + } + + protected InvalidCurrencyException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/CashRegister/Custom Exceptions/NotEnoughTenderException.cs b/CashRegister/Custom Exceptions/NotEnoughTenderException.cs new file mode 100644 index 00000000..8772d825 --- /dev/null +++ b/CashRegister/Custom Exceptions/NotEnoughTenderException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; + +namespace CashRegisterConsumer +{ + [Serializable] + public class NotEnoughTenderException : Exception + { + public NotEnoughTenderException() + { + } + + public NotEnoughTenderException(string message) : base(message) + { + } + + public NotEnoughTenderException(string message, Exception innerException) : base(message, innerException) + { + } + + protected NotEnoughTenderException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/CashRegister/Program.cs b/CashRegister/Program.cs index c3f7bd6f..714a9a5f 100644 --- a/CashRegister/Program.cs +++ b/CashRegister/Program.cs @@ -14,18 +14,22 @@ static void Main(string[] args) Console.WriteLine(@"The Console is the ""CONSUMER"" of the CashRegister Module."); Console.WriteLine(); Console.WriteLine(@"Press any button to continue with using the CashRegister Module. (Simulates POS ""Tender"" actions)"); + Console.WriteLine(); Console.ReadKey(); - CashRegister register = new POSCashRegister(new USD(), new USD(), new Random3ChangeCalculator()); - //register.RegisterPriceCurrency(new USD()); - //register.RegisterTenderCurrency(new USD()); - //register.RegisterChangeCalculator(new Random3ChangeCalculator()); + CashRegister register = new POSCashRegister(new USD(), new Random3Strategy()); + var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\Input\USD Input File.txt"); + Console.WriteLine(result); + + // for example (change the register to use YEN with a Standard Tender Strategy note that the demoniations are different so + // the same files that use "USD" decimals will be off. There are no decimals in YEN + //register.RegisterCurrency(new YEN()); + //register.RegisterTenderStrategy(new StandardTenderStrategy()); + //result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\Input\YEN Input File.txt"); + //Console.WriteLine(result); - var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\InputText.txt"); - - Console.WriteLine(result); Console.ReadKey(); } diff --git a/CashRegister/_diagramCashRegister.cd b/CashRegister/_diagramCashRegister.cd index 6455b12e..5d72fd41 100644 --- a/CashRegister/_diagramCashRegister.cd +++ b/CashRegister/_diagramCashRegister.cd @@ -3,17 +3,16 @@ - AgCAAAAAAAAAQAAAAIAAAABAAQAAAQAEAAAAAAAAAAA= + AgAAAAAAAAAAEAAAAIAAIABAAQAAAAAAgIAAAAAAAAA= CashRegister\Abstract\CashRegister.cs - - - + + - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= CashRegister\Concrete\POSCashRegister.cs @@ -22,70 +21,85 @@ - AAAAAAIAAAAAAAAAAAAADEAACAAAAAAAAAAACAAAAAA= + AAAABAAAAAAAAAAEAAAADAAACAAAAAAAAAAACABEAAA= CashRegister\Currencies\Abstract\Currency.cs - - + + - + - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAA= CashRegister\Currencies\Concrete\USD.cs - + - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAA= CashRegister\Currencies\Concrete\YEN.cs - - + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= - CashRegister\Stragegies\Abstract\ChangeCalculator.cs + CashRegister\Strategies\Abstract\TenderStrategy.cs - - + + + + ABIAAAAAAAAAAEQAAAACAAQAAAAAAAAABACgAABAAAA= + CashRegister\Currencies\Abstract\Money.cs + + + + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= - CashRegister\Stragegies\Concrete\StandardChangeCalculator.cs + CashRegister\Strategies\Concrete\Random3Stragegy.cs - - + + - ABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - CashRegister\Currencies\Money.cs + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Concrete\Bill.cs - - + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Concrete\Coin.cs + + + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= - CashRegister\Stragegies\Concrete\Random3ChangeCalculator.cs + CashRegister\Strategies\Concrete\StandardTenderStrategy.cs - + - AAAAAAIAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA= + AAAABAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAABAAAA= CashRegister\Currencies\Abstract\ICurrency.cs - - + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= - CashRegister\Stragegies\Abstract\IChangeCalculator.cs + CashRegister\Strategies\Abstract\ITenderStrategy.cs diff --git a/CashRegisterTests/CashRegisterTests.cs b/CashRegisterTests/CashRegisterTests.cs new file mode 100644 index 00000000..2cfc8135 --- /dev/null +++ b/CashRegisterTests/CashRegisterTests.cs @@ -0,0 +1,150 @@ +using CashRegisterConsumer; +using System; +using Xunit; +using Moq; +using System.Collections.Generic; +using System.IO; + +namespace CashRegisterTests +{ + public class CashRegisterTests + { +#region SETUP + private readonly Mock currencyMock; + private readonly Mock tenderStrategyMock; + + + private readonly string TenderValueTestFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\ValueTests\TenderValueTestFile.txt"; + private readonly string PriceValueTestFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\ValueTests\PriceValueTestFile.txt"; + private readonly string EmptyFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\EmptyFile.txt"; + private readonly string EmptyLineFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\EmptyLineFile.txt"; + private readonly string NotEnoughTenderFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\TenderLessThanPrice.txt"; + public CashRegisterTests() + { + currencyMock = new Mock(); + tenderStrategyMock = new Mock(); + } + private void SetupUSDMockCurrency() + { + currencyMock.Setup(p => p.Bills).Returns(new List() { + new Bill(100000, "one hundred thousand", "one hundred thousands"), + new Bill(10000, "ten thousand", "ten thousands"), + new Bill(5000, "five thousand", "five thousands"), + new Bill(1000, "thousand", "thousands"), + new Bill(500, "five hundred", "five hundreds"), + new Bill(100, "hundred", "hundreds"), + new Bill(50, "fifty", "fifties"), + new Bill(20, "twenty", "twenties"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars") + }); + currencyMock.Setup(p => p.Coins).Returns(new List() { + new Coin(.25m, "quarter", "quarters"), + new Coin(.10m, "dime", "dimes"), + new Coin(.05m, "nickel", "nickels"), + new Coin(.01m, "penny","pennies") + }); + currencyMock.Setup(p => p.AllDenominations).Returns(new List(){new Bill(100000, "one hundred thousand", "one hundred thousands"), + new Bill(10000, "ten thousand", "ten thousands"), + new Bill(5000, "five thousand", "five thousands"), + new Bill(1000, "thousand", "thousands"), + new Bill(500, "five hundred", "five hundreds"), + new Bill(100, "hundred", "hundreds"), + new Bill(50, "fifty", "fifties"), + new Bill(20, "twenty", "twenties"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars"), + new Coin(.25m, "quarter", "quarters"), + new Coin(.10m, "dime", "dimes"), + new Coin(.05m, "nickel", "nickels"), + new Coin(.01m, "penny","pennies") + }); + } + #endregion + + [Fact] + public void CashRegisterPriceValueIsAccuratelySetBasedOnTextFileInput() + { + // setup + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Coin(.25m, "testCoin", "testCoins") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + + // setup excpeted + decimal expected = 2.45m; + // execute to a point where the actual can be tested + var results = register.Tender(PriceValueTestFile); + // are they equal???? + Assert.Equal(expected, register.PriceValue); + } + [Fact] + public void CashRegisterTenderValueIsAccuratelySetBasedOnTextFileInput() + { + // setup + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Coin(.25m, "testCoin", "testCoins") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + + // setup excpeted + decimal expected = 110.98m; + // execute to a point where the actual can be tested + var results = register.Tender(TenderValueTestFile); + // are they equal???? + Assert.Equal(expected, register.TenderValue); + } + + #region Exception Tests + [Fact] + public void CashRegisterThrowsFileNotFoundExceptionGivenEmptyOrNullPath() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1m, "testMoney", "testMonies") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + Assert.Throws(() => register.Tender("")); + } + [Fact] + public void CashRegisterThrowsFileNotFoundExceptionGivenNullPath() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1m, "testMoney", "testMonies") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + Assert.Throws(() => register.Tender(null)); + } + [Fact] + public void CashRegisterThrowsFormatExceptionWhenEmptyFileIsProvided() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1m, "testMoney", "testMonies") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + Assert.Throws(() => register.Tender(EmptyFile)); + } + [Fact] + public void CashRegisterThrowsFormatExceptionWhenEmptyLineFoundInFileProvided() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1m, "testMoney", "testMonies") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + Assert.Throws(() => register.Tender(EmptyLineFile)); + } + [Fact] + public void CashRegisterThrowsNotEnoughTenderExceptionWhenTenderIsLessThanPrice() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1m, "testMoney", "testMonies") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + Assert.Throws(() => register.Tender(NotEnoughTenderFile)); + } + [Fact] + public void CashRegisterThrowsInvalidCurrencyExceptionWhenNoDenominationsFound() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List()); + Assert.Throws(() => new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object)); + } + [Fact] + public void CashRegisterThrowsNullReferenceExceptionWhenAttemptingToRegisterNullCurrency() + { + Assert.Throws(() => new POSCashRegister(null, tenderStrategyMock.Object)); + } + [Fact] + public void CashRegisterThrowsNullReferenceExceptionWhenAttemptingToRegisterNullTenderStrategy() + { + Assert.Throws(() => new POSCashRegister(currencyMock.Object, null)); + } + #endregion + + + } +} diff --git a/CashRegisterTests/CashRegisterTests.csproj b/CashRegisterTests/CashRegisterTests.csproj index 0a347c42..134918e5 100644 --- a/CashRegisterTests/CashRegisterTests.csproj +++ b/CashRegisterTests/CashRegisterTests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.0 @@ -8,6 +8,7 @@ + all @@ -19,4 +20,8 @@ + + + + diff --git a/CashRegisterTests/CurrencyTests.cs b/CashRegisterTests/CurrencyTests.cs new file mode 100644 index 00000000..d0ac5980 --- /dev/null +++ b/CashRegisterTests/CurrencyTests.cs @@ -0,0 +1,207 @@ +using CashRegisterConsumer; +using System; +using Xunit; +using Moq; +using System.Collections.Generic; +using System.IO; + + +namespace CurrencyTests +{ + public class CurrencyTests + { + #region Setup + public CurrencyTests() + { + + } + #endregion + [Fact] + public void CurrencyClearClearsMoneyCountForBills() + { + Currency currency = new CurrencyTestSortReverseCurrency(); + + //now that we know we have a count in each of our bills... we will clear the currency and test. + currency.Clear(); + + foreach (Money bill in currency.Bills) + { + Assert.True(bill.Count == 0); + } + + } + [Fact] + public void CurrencyClearClearsMoneyCountForCoins() + { + Currency currency = new CurrencyTestSortReverseCurrency(); + + //now that we know we have a count in each of our coins... we will clear the currency and test. + currency.Clear(); + + foreach (Money coin in currency.Bills) + { + Assert.True(coin.Count == 0); + } + + } + [Fact] + public void CurrencyAllDenominationsReturnsConcatForBillsAndCoins() + { + + // this also effectively tests the "sort/reverse" functionality of the InitializeCurrency method + // so creating a new method for that would be redundent (not necessarily bad though) + Currency currency = new CurrencyTestPluralNameCurrencyNoMoney(); + + for (int i = 0; i < currency.Bills.Count-1; i++) + { + Assert.Equal(currency.Bills[i], currency.AllDenominations[i]); + } + + for (int i = 0; i < currency.Coins.Count-1; i++) + { + Assert.Equal(currency.Coins[i], currency.AllDenominations[i + currency.Bills.Count]); // coins should start after bills due to the sort/reverse (denomination based) + } + } + [Fact] + public void CurrencyToStringReturnsExpectedStringWithSingles() + { + Currency currency = new CurrencyTestSingularNameCurrency(); + + string expected = "1 one hundred thousand,1 ten thousand,1 five thousand,1 thousand,1 five hundred,1 hundred,1 fifty,1 twenty,1 ten,1 five,1 dollar,1 quarter,1 dime,1 nickel,1 penny"; + string actual = currency.ToString(); + + Assert.Equal(expected, actual); + } + [Fact] + public void CurrencyToStringReturnsExpectedStringWithPlurals() + { + Currency currency = new CurrencyTestPluralNameCurrency(); + + string expected = "50 one hundred thousands,2 ten thousands,2 five thousands,4 thousands,2 five hundreds,600 hundreds,56 fifties,2 twenties,4 tens,3 fives,1245 dollars,500 quarters,5 dimes,40 nickels,100 pennies"; + string actual = currency.ToString(); + + Assert.Equal(expected, actual); + } + [Fact] + public void CurrencyToStringReturnsExpectedStringWithPluralsWhenZeroMoneyExists() + { + Currency currency = new CurrencyTestPluralNameCurrencyNoMoney(); + + string expected = "No Change Due."; + string actual = currency.ToString(); + + Assert.Equal(expected, actual); + } + + } + + + #region CurrecyTestClass + public class CurrencyTestSortReverseCurrency : Currency + { + public CurrencyTestSortReverseCurrency():base() { } + + protected override void InitializeCurrency() + { + this._bills = new List() { + new Bill(5, "five", "fives"), + new Bill(100000, "one hundred thousand", "one hundred thousands"), + new Bill(50, "fifty", "fifties"), + new Bill(1000, "thousand", "thousands"), + new Bill(500, "five hundred", "five hundreds"), + new Bill(5000, "five thousand", "five thousands"), + new Bill(100, "hundred", "hundreds"), + new Bill(20, "twenty", "twenties"), + new Bill(10000, "ten thousand", "ten thousands"), + new Bill(10, "ten", "tens"), + new Bill(1, "dollar", "dollars") + }; + + this._coins = new List() { + new Coin(.05m, "nickel", "nickels"), + new Coin(.01m, "penny","pennies"), + new Coin(.25m, "quarter", "quarters"), + new Coin(.10m, "dime", "dimes") + }; + } + } + public class CurrencyTestSingularNameCurrency : Currency + { + protected override void InitializeCurrency() + { + this._bills = new List() { + new Bill(100000, "one hundred thousand", "one hundred thousands", 1), + new Bill(10000, "ten thousand", "ten thousands", 1), + new Bill(5000, "five thousand", "five thousands", 1), + new Bill(1000, "thousand", "thousands", 1), + new Bill(500, "five hundred", "five hundreds", 1), + new Bill(100, "hundred", "hundreds", 1), + new Bill(50, "fifty", "fifties", 1), + new Bill(20, "twenty", "twenties", 1), + new Bill(10, "ten", "tens", 1), + new Bill(5, "five", "fives", 1), + new Bill(1, "dollar", "dollars", 1) + }; + + this._coins = new List() { + new Coin(.25m, "quarter", "quarters", 1), + new Coin(.10m, "dime", "dimes", 1), + new Coin(.05m, "nickel", "nickels", 1), + new Coin(.01m, "penny","pennies", 1) + }; + } + } + public class CurrencyTestPluralNameCurrency : Currency + { + protected override void InitializeCurrency() + { + this._bills = new List() { + new Bill(100000, "one hundred thousand", "one hundred thousands", 50), + new Bill(10000, "ten thousand", "ten thousands", 2), + new Bill(5000, "five thousand", "five thousands", 2), + new Bill(1000, "thousand", "thousands", 4), + new Bill(500, "five hundred", "five hundreds", 2), + new Bill(100, "hundred", "hundreds", 600), + new Bill(50, "fifty", "fifties", 56), + new Bill(20, "twenty", "twenties", 2), + new Bill(10, "ten", "tens", 4), + new Bill(5, "five", "fives", 3), + new Bill(1, "dollar", "dollars", 1245) + }; + + this._coins = new List() { + new Coin(.25m, "quarter", "quarters", 500), + new Coin(.10m, "dime", "dimes", 5), + new Coin(.05m, "nickel", "nickels", 40), + new Coin(.01m, "penny","pennies", 100) + }; + } + } + public class CurrencyTestPluralNameCurrencyNoMoney : Currency + { + protected override void InitializeCurrency() + { + this._bills = new List() { + new Bill(100000, "one hundred thousand", "one hundred thousands"), + new Bill(10000, "ten thousand", "ten thousands"), + new Bill(5000, "five thousand", "five thousands"), + new Bill(1000, "thousand", "thousands"), + new Bill(500, "five hundred", "five hundreds"), + new Bill(100, "hundred", "hundreds"), + new Bill(50, "fifty", "fifties"), + new Bill(20, "twenty", "twenties"), + new Bill(10, "ten", "tens"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars") + }; + + this._coins = new List() { + new Coin(.25m, "quarter", "quarters"), + new Coin(.10m, "dime", "dimes"), + new Coin(.05m, "nickel", "nickels"), + new Coin(.01m, "penny","pennies") + }; + } + } + #endregion +} diff --git a/CashRegisterTests/MoneyTests.cs b/CashRegisterTests/MoneyTests.cs new file mode 100644 index 00000000..2b4f97d4 --- /dev/null +++ b/CashRegisterTests/MoneyTests.cs @@ -0,0 +1,121 @@ +using CashRegisterConsumer; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace MoneyTests +{ + public class MoneyTests + { + #region Setup + public MoneyTests() + { + + } + #endregion + + [Fact] + public void MoneyConstructionSetsDenominationAccurately() + { + decimal expected = 0.01m; + Money money = new MoneyTestMoney(0.01m, "test", "tests", 3); + + Assert.Equal(expected, money.Denomination); + } + [Fact] + public void MoneyConstructionSetsSingularNameAccurately() + { + string expected = "test"; + Money money = new MoneyTestMoney(0.01m, "test", "tests", 1); + + Assert.Equal(expected, money.Name); + } + [Fact] + public void MoneyConstructionSetsPluralNameAccurately() + { + string expected = "tests"; + Money money = new MoneyTestMoney(0.01m, "test", "tests"); + + Assert.Equal(expected, money.Name); + } + [Fact] + public void MoneyConstructionSetsPluralNameWithZeroCount() + { + string expected = "tests"; + Money money = new MoneyTestMoney(0.01m, "test", "tests"); + + Assert.Equal(expected, money.Name); + } + + [Fact] + public void MoneyAddsCorrectAmount() + { + Money money = new MoneyTestMoney(1, "test", "tests"); + Assert.Equal(0, money.Count); + + money.Add(1); + Assert.Equal(1, money.Count); + + money.Add(-1); + Assert.Equal(0, money.Count); + } + [Fact] + public void MoneySubtractsCorrectAmount() + { + Money money = new MoneyTestMoney(1, "test", "tests",5); + Assert.Equal(5, money.Count); + + money.Subtract(1); + Assert.Equal(4, money.Count); + + money.Subtract(-1); + Assert.Equal(5, money.Count); + } + [Fact] + public void MoneyClearsCount() + { + Money money = new MoneyTestMoney(1, "test", "tests", 5); + Assert.Equal(5, money.Count); + + + money.Clear(); + Assert.Equal(0, money.Count); + } + + [Fact] + public void MoneyCompareToReturnsZeroWhenEqual() + { + Mock moneyToCompare = new Mock(1m, "compare", "compares"); + Mock moneyToCompareTo = new Mock(1m, "compare", "compares"); + + Assert.Equal(0, moneyToCompare.Object.CompareTo(moneyToCompareTo.Object)); + } + [Fact] + public void MoneyCompareToReturnsOneWhenMoreThan() + { + Mock moneyToCompare = new Mock(1m, "compare", "compares"); + Mock moneyToCompareTo = new Mock(0.50m, "compare", "compares"); + + Assert.Equal(1, moneyToCompare.Object.CompareTo(moneyToCompareTo.Object)); + } + [Fact] + public void MoneyCompareToReturnsMinusOneWhenLessThan() + { + Mock moneyToCompare = new Mock(0.5m,"compare","compares"); + Mock moneyToCompareTo = new Mock(1m, "compare", "compares"); + + Assert.Equal(-1, moneyToCompare.Object.CompareTo(moneyToCompareTo.Object)); + } + } + + + #region MoneyTestClass + public class MoneyTestMoney : Money + { + public MoneyTestMoney(decimal denomination, string singleName, string pluralName) : base(denomination, singleName, pluralName) { } + public MoneyTestMoney(decimal denomination, string singleName, string pluralName, int count) : base(denomination, singleName, pluralName, count) { } + } + #endregion +} diff --git a/CashRegisterTests/Random3StrategyTests.cs b/CashRegisterTests/Random3StrategyTests.cs new file mode 100644 index 00000000..53dd1c51 --- /dev/null +++ b/CashRegisterTests/Random3StrategyTests.cs @@ -0,0 +1,196 @@ +using CashRegisterConsumer; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace TenderStrategyTests +{ + public class Random3StrategyTests + { + #region Setup + private readonly Mock mockCurrency; + public Random3StrategyTests() + { + mockCurrency = new Mock(); + } + #endregion + + [Fact] + public void Random3TenderStrategyCalculatesChangeCorrectlyBasedOnPriceAndTenderDifference() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() + { new Bill(10, "ten", "tens"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars"), + new Coin(.10m,"dime","dimes"), + new Coin(.05m,"nickel","nickels"), + new Coin(.01m,"penny","pennies") + }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + decimal expected = 3001.50m; + decimal actual = 0.0m; + string result = tenderStrategy.Calculate(mockCurrency.Object, 123.03m, 3124.53m); // 3001.50 in change + foreach (Money money in mockCurrency.Object.AllDenominations) + { + actual += (money.Count*money.Denomination); + } + Assert.Equal(expected, actual); + + + result = tenderStrategy.Calculate(mockCurrency.Object, 1.03m, 1.03m); // 0 in change + expected = 0.0m; + actual = 0.0m; + foreach (Money money in mockCurrency.Object.AllDenominations) + { + actual += (money.Count * money.Denomination); + } + Assert.Equal(expected, actual); + + } + [Fact] + public void Random3CalculatesExactTenderCorrectly() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() + { new Bill(10, "ten", "tens"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars"), + new Coin(.10m,"dime","dimes"), + new Coin(.05m,"nickel","nickels"), + new Coin(.01m,"penny","pennies") + }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + decimal expected = 0.0m; + decimal actual = 0.0m; + var result = tenderStrategy.Calculate(mockCurrency.Object, 1.03m, 1.03m); // 0 in change + foreach (Money money in mockCurrency.Object.AllDenominations) + { + actual += (money.Count * money.Denomination); + } + Assert.Equal(expected, actual); + } + [Fact] + public void Random3CalculatesZeroTenderWhenTenderIsLessThanPrice() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() + { new Bill(10, "ten", "tens"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars"), + new Coin(.10m,"dime","dimes"), + new Coin(.05m,"nickel","nickels"), + new Coin(.01m,"penny","pennies") + }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + decimal expected = 0.0m; + decimal actual = 0.0m; + var result = tenderStrategy.Calculate(mockCurrency.Object, 10.03m, 1.03m); // 0 in change + foreach (Money money in mockCurrency.Object.AllDenominations) + { + actual += (money.Count * money.Denomination); + } + Assert.Equal(expected, actual); + } + + [Fact] + public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimalDenominationsAndRandomReturn() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars") }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 3.33m); + + // test that our "1 dollar bill" is added 2 times during the process for standard strategy for change + // (meaning that the price is recuded each time accordingly) + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(2, money.Count); // price - tender = 2 dollars in change + } + } + [Fact] + public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDenominationsAndRandomReturn() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.01m, "penny", "pennies") }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 1.43m); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(10, money.Count); // price - tender = 10 pennies in change + } + } + [Fact] + public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimalDenominationsAndRandomReturn() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.25m, "quarter", "quarters") }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 2.33m); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(4, money.Count); // price - tender = 4 quarters in change + } + } + + [Fact] + public void Random3TenderStrategyReturnsCurrentyWithNoMoneyValuesAndNonRandomReturn() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "test", "tests") }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 0, 0); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. + } // Consider refactoring ToString. Hard testing indicates a design flaw. + } + [Fact] + public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimalDenominationsAndNonRandomReturn() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars") }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 3); + + // test that our "1 dollar bill" is added 2 times during the process for standard strategy for change + // (meaning that the price is recuded each time accordingly) + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(2, money.Count); // price - tender = 2 dollars in change + } + } + [Fact] + public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDenominationsAndNonRandomReturn() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.01m, "penny", "pennies") }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 1.10m); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(10, money.Count); // price - tender = 10 pennies in change + } + } + [Fact] + public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimalDenominationsAndNonRandomReturn() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.25m, "quarter", "quarters") }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 2.00m); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(4, money.Count); // price - tender = 4 quarters in change + } + } + + #region Exception Testing + [Fact] + public void Random3TenderStrategyCalculateThrowsInvalidCurrencyExceptionWhenNoDenominationsFound() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { }); + ITenderStrategy tenderStrategy = new Random3Strategy(); + Assert.Throws(() => tenderStrategy.Calculate(mockCurrency.Object, 0, 0)); + } + #endregion + + } +} diff --git a/CashRegisterTests/StandardTenderStrategyTests.cs b/CashRegisterTests/StandardTenderStrategyTests.cs new file mode 100644 index 00000000..a6184bc7 --- /dev/null +++ b/CashRegisterTests/StandardTenderStrategyTests.cs @@ -0,0 +1,103 @@ +using CashRegisterConsumer; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace TenderStrategyTests +{ + public class StandardTenderStrategyTests + { + #region Setup + private readonly Mock mockCurrency; + public StandardTenderStrategyTests() + { + mockCurrency = new Mock(); + } + #endregion + + + [Fact] + public void StandardCalculatesZeroTenderWhenTenderIsLessThanPrice() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() + { new Bill(10, "ten", "tens"), + new Bill(5, "five", "fives"), + new Bill(1, "dollar", "dollars"), + new Coin(.10m,"dime","dimes"), + new Coin(.05m,"nickel","nickels"), + new Coin(.01m,"penny","pennies") + }); + ITenderStrategy tenderStrategy = new StandardTenderStrategy(); + decimal expected = 0.0m; + decimal actual = 0.0m; + var result = tenderStrategy.Calculate(mockCurrency.Object, 10.03m, 1.03m); // 0 in change + foreach (Money money in mockCurrency.Object.AllDenominations) + { + actual += (money.Count * money.Denomination); + } + Assert.Equal(expected, actual); + } + [Fact] + public void StandardTenderStrategyReturnsCurrentyWithNoMoneyValues() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1,"test","tests") }); + ITenderStrategy tenderStrategy = new StandardTenderStrategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 0, 0); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. + } // Consider refactoring ToString. Hard testing indicates a design flaw. + } + [Fact] + public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimalDenominations() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars") }); + ITenderStrategy tenderStrategy = new StandardTenderStrategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 3); + + // test that our "1 dollar bill" is added 2 times during the process for standard strategy for change + // (meaning that the price is recuded each time accordingly) + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(2, money.Count); // price - tender = 2 dollars in change + } + } + [Fact] + public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDenominations() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.01m, "penny", "pennies") }); + ITenderStrategy tenderStrategy = new StandardTenderStrategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 1.10m); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(10, money.Count); // price - tender = 10 pennies in change + } + } + [Fact] + public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimalDenominations() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.25m, "quarter", "quarters") }); + ITenderStrategy tenderStrategy = new StandardTenderStrategy(); + string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 2.00m); + + foreach (Money money in mockCurrency.Object.AllDenominations) + { + Assert.Equal(4, money.Count); // price - tender = 4 quarters in change + } + } + + #region Exception Testing + [Fact] + public void StandardTenderStrategyCalculateThrowsInvalidCurrencyExceptionWhenNoDenominationsFound() + { + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { }); + ITenderStrategy tenderStrategy = new StandardTenderStrategy(); + Assert.Throws(() => tenderStrategy.Calculate(mockCurrency.Object, 0, 0)); + } + #endregion + } +} diff --git a/CashRegisterTests/Test Input Files/EmptyFile.txt b/CashRegisterTests/Test Input Files/EmptyFile.txt new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/CashRegisterTests/Test Input Files/EmptyFile.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/CashRegisterTests/Test Input Files/EmptyLineFile.txt b/CashRegisterTests/Test Input Files/EmptyLineFile.txt new file mode 100644 index 00000000..c6473c1e --- /dev/null +++ b/CashRegisterTests/Test Input Files/EmptyLineFile.txt @@ -0,0 +1,2 @@ + +5.55,6.66 \ No newline at end of file diff --git a/CashRegisterTests/Test Input Files/SingleTransactionTestFile.txt b/CashRegisterTests/Test Input Files/SingleTransactionTestFile.txt new file mode 100644 index 00000000..bfd78cb5 --- /dev/null +++ b/CashRegisterTests/Test Input Files/SingleTransactionTestFile.txt @@ -0,0 +1 @@ +2.50, 5.00 \ No newline at end of file diff --git a/CashRegisterTests/Test Input Files/TenderLessThanPrice.txt b/CashRegisterTests/Test Input Files/TenderLessThanPrice.txt new file mode 100644 index 00000000..6c37fe19 --- /dev/null +++ b/CashRegisterTests/Test Input Files/TenderLessThanPrice.txt @@ -0,0 +1 @@ +6.00, 4.50 \ No newline at end of file diff --git a/CashRegisterTests/Test Input Files/ValueTests/PriceValueTestFile.txt b/CashRegisterTests/Test Input Files/ValueTests/PriceValueTestFile.txt new file mode 100644 index 00000000..cea39776 --- /dev/null +++ b/CashRegisterTests/Test Input Files/ValueTests/PriceValueTestFile.txt @@ -0,0 +1 @@ + 2.45,110.98 \ No newline at end of file diff --git a/CashRegisterTests/Test Input Files/ValueTests/TenderValueTestFile.txt b/CashRegisterTests/Test Input Files/ValueTests/TenderValueTestFile.txt new file mode 100644 index 00000000..cea39776 --- /dev/null +++ b/CashRegisterTests/Test Input Files/ValueTests/TenderValueTestFile.txt @@ -0,0 +1 @@ + 2.45,110.98 \ No newline at end of file diff --git a/CashRegisterTests/UnitTest1.cs b/CashRegisterTests/UnitTest1.cs deleted file mode 100644 index 12fb8464..00000000 --- a/CashRegisterTests/UnitTest1.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Xunit; - -namespace CashRegisterTests -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} From e21079102294a6fee7df5dd0583a3e0573f9099b Mon Sep 17 00:00:00 2001 From: Paul L West Date: Sun, 9 Feb 2020 09:19:27 +0900 Subject: [PATCH 06/15] First refactoring modificiaton: Removed Currency "ToString" override and created "Display" method in the Tender Strategies interface. This eleviates the Currency from doing work that can be handled by the strategy and decouples the response from the model object. --- .../CashRegister/Abstract/CashRegister.cs | 3 +- .../Currencies/Abstract/Currency.cs | 32 ++++---- .../Strategies/Abstract/ITenderStrategy.cs | 4 +- .../Strategies/Abstract/TenderStrategy.cs | 22 ++++- .../Strategies/Concrete/Random3Strategy.cs | 36 +++++++- .../Concrete/StandardTenderStrategy.cs | 25 +++++- CashRegister/_diagramCashRegister.cd | 82 ++++++++++++------- CashRegisterTests/CurrencyTests.cs | 62 +++++++------- CashRegisterTests/Random3StrategyTests.cs | 39 +++++---- .../StandardTenderStrategyTests.cs | 16 ++-- 10 files changed, 212 insertions(+), 109 deletions(-) diff --git a/CashRegister/CashRegister/Abstract/CashRegister.cs b/CashRegister/CashRegister/Abstract/CashRegister.cs index 5513487b..3cec0479 100644 --- a/CashRegister/CashRegister/Abstract/CashRegister.cs +++ b/CashRegister/CashRegister/Abstract/CashRegister.cs @@ -62,7 +62,8 @@ public string Tender(string path) // setup the _price and _tender for this transaction SetTransactionAmounts(sr); // add the transaction calculation based on the strategy to the return string - tenderedValues.Append(_tenderStrategy.Calculate(_currency, _price, _tender) + "\n"); + var results = _tenderStrategy.Calculate(_currency, _price, _tender); + tenderedValues.Append(_tenderStrategy.Display(_currency)); }; } diff --git a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs index f0b1d7ee..5474d2a7 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs @@ -36,21 +36,21 @@ public void Clear() } } - public override string ToString() - { - StringBuilder sr = new StringBuilder(); - foreach (Money money in AllDenominations) - { - if (money.Count > 0) - { - sr.Append(String.Format("{0} {1},", money.Count, money.Name)); - } - } - - if (sr.Length == 0) - return "No Change Due."; // not part of the requirements, yet exact change is a viable value. - else - return sr.ToString().Trim(','); - } + //public override string ToString() + //{ + // StringBuilder sr = new StringBuilder(); + // foreach (Money money in AllDenominations) + // { + // if (money.Count > 0) + // { + // sr.Append(String.Format("{0} {1},", money.Count, money.Name)); + // } + // } + + // if (sr.Length == 0) + // return "No Change Due."; // not part of the requirements, yet exact change is a viable value. + // else + // return sr.ToString().Trim(','); + //} } } diff --git a/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs index a30fe045..9e5555fd 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs @@ -2,6 +2,8 @@ { public interface ITenderStrategy { - string Calculate(ICurrency currency, decimal price, decimal tender); + ICurrency Calculate(ICurrency currency, decimal price, decimal tender); + string Display(ICurrency currency); + } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs index 2be445bf..4e6eeda4 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs @@ -6,6 +6,26 @@ namespace CashRegisterConsumer { public abstract class TenderStrategy : ITenderStrategy { - public abstract string Calculate(ICurrency currency, decimal price, decimal tender); + //public abstract string Calculate(ICurrency currency, decimal price, decimal tender); + + public virtual string Display(ICurrency currency) + { + StringBuilder sr = new StringBuilder(); + foreach (Money money in currency.AllDenominations) + { + if (money.Count > 0) + { + sr.Append(String.Format("{0} {1},", money.Count, money.Name)); + } + } + + if (sr.Length == 0) + return "No Change Due."; // not part of the requirements, yet exact change is a viable value. + else + return sr.ToString().Trim(','); + } + + public abstract ICurrency Calculate(ICurrency currency, decimal price, decimal tender); + } } diff --git a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs index ee5c62bb..eda331a9 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs @@ -7,7 +7,7 @@ namespace CashRegisterConsumer { public class Random3Strategy : TenderStrategy { - public override string Calculate(ICurrency currency, decimal price, decimal tender) + public override ICurrency Calculate(ICurrency currency, decimal price, decimal tender) { if (currency.AllDenominations.Count == 0) throw new InvalidCurrencyException("No currency denominations found"); @@ -30,7 +30,7 @@ public override string Calculate(ICurrency currency, decimal price, decimal tend } } } - return currency.ToString(); + return currency; } else { @@ -39,5 +39,37 @@ public override string Calculate(ICurrency currency, decimal price, decimal tend } } + //public override string Calculate(ICurrency currency, decimal price, decimal tender) + //{ + // if (currency.AllDenominations.Count == 0) + // throw new InvalidCurrencyException("No currency denominations found"); + + // if (Math.Abs(((price * 100) % 10)) == 3) + // { + // Random random = new Random(); + // int count; + // decimal change = tender - price; + + // while (change >= currency.AllDenominations.Min(x => x.Denomination)) + // { + // foreach (Money money in currency.AllDenominations) + // { + // if (money.Denomination <= change) // if the current denomination is less than the change + // { + // count = random.Next(1, (int)Math.Floor(change / money.Denomination)); // get a random count (not bigger than the change) + // money.Add(count); // add the appropriate amount of this denomination based on our random + // change -= (money.Denomination * count); // remove the money denomination(times count) from the change + // } + // } + // } + // return currency.ToString(); + // } + // else + // { + // // when not using the "random", we can just use the standard strategy + // return new StandardTenderStrategy().Calculate(currency, price, tender); + // } + //} + } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs index 1d22782d..6aa8146c 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs @@ -7,7 +7,28 @@ namespace CashRegisterConsumer { public class StandardTenderStrategy : TenderStrategy { - public override string Calculate(ICurrency currency, decimal price, decimal tender) + //public override string Calculate(ICurrency currency, decimal price, decimal tender) + //{ + // if (currency.AllDenominations.Count == 0) + // throw new InvalidCurrencyException("No currency denominations found"); + + // decimal change = tender - price; + // // this is to ensure that coinage less than the smallest denomination will not cause an infinite loop. + // // the extra "change" is ignored.. this can occur because some currencies do not have decimal value denominations (YEN) + // while (change >= currency.AllDenominations.Min(x => x.Denomination)) + // { + // foreach (Money money in currency.AllDenominations) + // { + // while (change >= money.Denomination) + // { + // money.Add(1); + // change -= money.Denomination; + // } + // } + // } + // return currency.ToString(); + //} + public override ICurrency Calculate(ICurrency currency, decimal price, decimal tender) { if (currency.AllDenominations.Count == 0) throw new InvalidCurrencyException("No currency denominations found"); @@ -26,7 +47,7 @@ public override string Calculate(ICurrency currency, decimal price, decimal tend } } } - return currency.ToString(); + return currency; } } } diff --git a/CashRegister/_diagramCashRegister.cd b/CashRegister/_diagramCashRegister.cd index 5d72fd41..f4f6aac6 100644 --- a/CashRegister/_diagramCashRegister.cd +++ b/CashRegister/_diagramCashRegister.cd @@ -1,9 +1,12 @@  - + + + + - AgAAAAAAAAAAEAAAAIAAIABAAQAAAAAAgIAAAAAAAAA= + AgAAAAAAAAAAEAAAAIAAIABBAAAIAAAAgIAAAEAAAAg= CashRegister\Abstract\CashRegister.cs @@ -12,14 +15,14 @@ - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= CashRegister\Concrete\POSCashRegister.cs - + AAAABAAAAAAAAAAEAAAADAAACAAAAAAAAAAACABEAAA= CashRegister\Currencies\Abstract\Currency.cs @@ -30,73 +33,90 @@ + + + + + + + ABIAAAAAAAAAAEQAAAACAAQAAAAAAAAABACAAABBAAA= + CashRegister\Currencies\Abstract\Money.cs + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Concrete\Bill.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CashRegister\Currencies\Concrete\Coin.cs + + - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAA= CashRegister\Currencies\Concrete\USD.cs - + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAA= CashRegister\Currencies\Concrete\YEN.cs - + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= CashRegister\Strategies\Abstract\TenderStrategy.cs - - + + - ABIAAAAAAAAAAEQAAAACAAQAAAAAAAAABACgAABAAAA= - CashRegister\Currencies\Abstract\Money.cs + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + CashRegister\Strategies\Concrete\Random3Strategy.cs - - - + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= - CashRegister\Strategies\Concrete\Random3Stragegy.cs + CashRegister\Strategies\Concrete\StandardTenderStrategy.cs - - + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - CashRegister\Currencies\Concrete\Bill.cs + Custom Exceptions\InvalidCurrencyException.cs - - + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - CashRegister\Currencies\Concrete\Coin.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= - CashRegister\Strategies\Concrete\StandardTenderStrategy.cs + Custom Exceptions\NotEnoughTenderException.cs - + AAAABAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAABAAAA= CashRegister\Currencies\Abstract\ICurrency.cs - - + + AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= CashRegister\Strategies\Abstract\ITenderStrategy.cs diff --git a/CashRegisterTests/CurrencyTests.cs b/CashRegisterTests/CurrencyTests.cs index d0ac5980..1dda478f 100644 --- a/CashRegisterTests/CurrencyTests.cs +++ b/CashRegisterTests/CurrencyTests.cs @@ -62,36 +62,38 @@ public void CurrencyAllDenominationsReturnsConcatForBillsAndCoins() Assert.Equal(currency.Coins[i], currency.AllDenominations[i + currency.Bills.Count]); // coins should start after bills due to the sort/reverse (denomination based) } } - [Fact] - public void CurrencyToStringReturnsExpectedStringWithSingles() - { - Currency currency = new CurrencyTestSingularNameCurrency(); - - string expected = "1 one hundred thousand,1 ten thousand,1 five thousand,1 thousand,1 five hundred,1 hundred,1 fifty,1 twenty,1 ten,1 five,1 dollar,1 quarter,1 dime,1 nickel,1 penny"; - string actual = currency.ToString(); - - Assert.Equal(expected, actual); - } - [Fact] - public void CurrencyToStringReturnsExpectedStringWithPlurals() - { - Currency currency = new CurrencyTestPluralNameCurrency(); - - string expected = "50 one hundred thousands,2 ten thousands,2 five thousands,4 thousands,2 five hundreds,600 hundreds,56 fifties,2 twenties,4 tens,3 fives,1245 dollars,500 quarters,5 dimes,40 nickels,100 pennies"; - string actual = currency.ToString(); - - Assert.Equal(expected, actual); - } - [Fact] - public void CurrencyToStringReturnsExpectedStringWithPluralsWhenZeroMoneyExists() - { - Currency currency = new CurrencyTestPluralNameCurrencyNoMoney(); - - string expected = "No Change Due."; - string actual = currency.ToString(); - - Assert.Equal(expected, actual); - } + #region Removed "ToString" override + //[Fact] + //public void CurrencyToStringReturnsExpectedStringWithSingles() + //{ + // Currency currency = new CurrencyTestSingularNameCurrency(); + + // string expected = "1 one hundred thousand,1 ten thousand,1 five thousand,1 thousand,1 five hundred,1 hundred,1 fifty,1 twenty,1 ten,1 five,1 dollar,1 quarter,1 dime,1 nickel,1 penny"; + // string actual = currency.ToString(); + + // Assert.Equal(expected, actual); + //} + //[Fact] + //public void CurrencyToStringReturnsExpectedStringWithPlurals() + //{ + // Currency currency = new CurrencyTestPluralNameCurrency(); + + // string expected = "50 one hundred thousands,2 ten thousands,2 five thousands,4 thousands,2 five hundreds,600 hundreds,56 fifties,2 twenties,4 tens,3 fives,1245 dollars,500 quarters,5 dimes,40 nickels,100 pennies"; + // string actual = currency.ToString(); + + // Assert.Equal(expected, actual); + //} + //[Fact] + //public void CurrencyToStringReturnsExpectedStringWithPluralsWhenZeroMoneyExists() + //{ + // Currency currency = new CurrencyTestPluralNameCurrencyNoMoney(); + + // string expected = "No Change Due."; + // string actual = currency.ToString(); + + // Assert.Equal(expected, actual); + //} + #endregion } diff --git a/CashRegisterTests/Random3StrategyTests.cs b/CashRegisterTests/Random3StrategyTests.cs index 53dd1c51..896721f9 100644 --- a/CashRegisterTests/Random3StrategyTests.cs +++ b/CashRegisterTests/Random3StrategyTests.cs @@ -31,7 +31,7 @@ public void Random3TenderStrategyCalculatesChangeCorrectlyBasedOnPriceAndTenderD ITenderStrategy tenderStrategy = new Random3Strategy(); decimal expected = 3001.50m; decimal actual = 0.0m; - string result = tenderStrategy.Calculate(mockCurrency.Object, 123.03m, 3124.53m); // 3001.50 in change + var result = tenderStrategy.Calculate(mockCurrency.Object, 123.03m, 3124.53m); // 3001.50 in change foreach (Money money in mockCurrency.Object.AllDenominations) { actual += (money.Count*money.Denomination); @@ -39,9 +39,14 @@ public void Random3TenderStrategyCalculatesChangeCorrectlyBasedOnPriceAndTenderD Assert.Equal(expected, actual); - result = tenderStrategy.Calculate(mockCurrency.Object, 1.03m, 1.03m); // 0 in change + foreach (Money money in mockCurrency.Object.AllDenominations) + { + money.Clear(); + } expected = 0.0m; actual = 0.0m; + + result = tenderStrategy.Calculate(mockCurrency.Object, 1.03m, 1.03m); // 0 in change foreach (Money money in mockCurrency.Object.AllDenominations) { actual += (money.Count * money.Denomination); @@ -85,7 +90,7 @@ public void Random3CalculatesZeroTenderWhenTenderIsLessThanPrice() decimal expected = 0.0m; decimal actual = 0.0m; var result = tenderStrategy.Calculate(mockCurrency.Object, 10.03m, 1.03m); // 0 in change - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in result.AllDenominations) // mockCurrency.Object.AllDenominations) { actual += (money.Count * money.Denomination); } @@ -97,11 +102,11 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimal { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars") }); ITenderStrategy tenderStrategy = new Random3Strategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 3.33m); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 3.33m); // test that our "1 dollar bill" is added 2 times during the process for standard strategy for change // (meaning that the price is recuded each time accordingly) - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(2, money.Count); // price - tender = 2 dollars in change } @@ -111,9 +116,9 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDen { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.01m, "penny", "pennies") }); ITenderStrategy tenderStrategy = new Random3Strategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 1.43m); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 1.43m); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(10, money.Count); // price - tender = 10 pennies in change } @@ -123,9 +128,9 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimal { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.25m, "quarter", "quarters") }); ITenderStrategy tenderStrategy = new Random3Strategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 2.33m); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1.33m, 2.33m); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(4, money.Count); // price - tender = 4 quarters in change } @@ -136,9 +141,9 @@ public void Random3TenderStrategyReturnsCurrentyWithNoMoneyValuesAndNonRandomRet { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "test", "tests") }); ITenderStrategy tenderStrategy = new Random3Strategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 0, 0); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 0, 0); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. } // Consider refactoring ToString. Hard testing indicates a design flaw. @@ -148,11 +153,11 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimal { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars") }); ITenderStrategy tenderStrategy = new Random3Strategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 3); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 3); // test that our "1 dollar bill" is added 2 times during the process for standard strategy for change // (meaning that the price is recuded each time accordingly) - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(2, money.Count); // price - tender = 2 dollars in change } @@ -162,9 +167,9 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDen { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.01m, "penny", "pennies") }); ITenderStrategy tenderStrategy = new Random3Strategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 1.10m); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 1.10m); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(10, money.Count); // price - tender = 10 pennies in change } @@ -174,9 +179,9 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimal { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.25m, "quarter", "quarters") }); ITenderStrategy tenderStrategy = new Random3Strategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 2.00m); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 2.00m); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(4, money.Count); // price - tender = 4 quarters in change } diff --git a/CashRegisterTests/StandardTenderStrategyTests.cs b/CashRegisterTests/StandardTenderStrategyTests.cs index a6184bc7..7eac95eb 100644 --- a/CashRegisterTests/StandardTenderStrategyTests.cs +++ b/CashRegisterTests/StandardTenderStrategyTests.cs @@ -44,9 +44,9 @@ public void StandardTenderStrategyReturnsCurrentyWithNoMoneyValues() { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1,"test","tests") }); ITenderStrategy tenderStrategy = new StandardTenderStrategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 0, 0); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 0, 0); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations)// mockCurrency.Object.AllDenominations) { Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. } // Consider refactoring ToString. Hard testing indicates a design flaw. @@ -56,11 +56,11 @@ public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecima { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars") }); ITenderStrategy tenderStrategy = new StandardTenderStrategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 3); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 3); // test that our "1 dollar bill" is added 2 times during the process for standard strategy for change // (meaning that the price is recuded each time accordingly) - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(2, money.Count); // price - tender = 2 dollars in change } @@ -70,9 +70,9 @@ public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDe { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.01m, "penny", "pennies") }); ITenderStrategy tenderStrategy = new StandardTenderStrategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 1.10m); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 1.10m); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(10, money.Count); // price - tender = 10 pennies in change } @@ -82,9 +82,9 @@ public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecima { mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(.25m, "quarter", "quarters") }); ITenderStrategy tenderStrategy = new StandardTenderStrategy(); - string actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 2.00m); + var actual = tenderStrategy.Calculate(mockCurrency.Object, 1, 2.00m); - foreach (Money money in mockCurrency.Object.AllDenominations) + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { Assert.Equal(4, money.Count); // price - tender = 4 quarters in change } From e61a27b828c6790d98a098840e84bca4d5bfeeba Mon Sep 17 00:00:00 2001 From: Paul L West Date: Sun, 9 Feb 2020 09:56:11 +0900 Subject: [PATCH 07/15] Refactored "ToString" tests from Currency to TenderStrategy tests. --- .../Strategies/Abstract/TenderStrategy.cs | 4 +- CashRegister/Program.cs | 2 +- CashRegisterTests/CashRegisterTests.cs | 39 +-------- CashRegisterTests/CurrencyTests.cs | 34 -------- CashRegisterTests/TenderStrategyTests.cs | 81 +++++++++++++++++++ 5 files changed, 86 insertions(+), 74 deletions(-) create mode 100644 CashRegisterTests/TenderStrategyTests.cs diff --git a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs index 4e6eeda4..17cd4d8e 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs @@ -20,9 +20,9 @@ public virtual string Display(ICurrency currency) } if (sr.Length == 0) - return "No Change Due."; // not part of the requirements, yet exact change is a viable value. + return String.Format("{0}\n", "No Change Due."); // not part of the requirements, yet exact change is a viable value. else - return sr.ToString().Trim(','); + return String.Format("{0}\n", sr.ToString().Trim(',')); } public abstract ICurrency Calculate(ICurrency currency, decimal price, decimal tender); diff --git a/CashRegister/Program.cs b/CashRegister/Program.cs index 714a9a5f..3e56b0e9 100644 --- a/CashRegister/Program.cs +++ b/CashRegister/Program.cs @@ -19,7 +19,7 @@ static void Main(string[] args) CashRegister register = new POSCashRegister(new USD(), new Random3Strategy()); - var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\Input\USD Input File.txt"); + var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\CashRegisterInputFiles\USD Input File.txt"); Console.WriteLine(result); // for example (change the register to use YEN with a Standard Tender Strategy note that the demoniations are different so diff --git a/CashRegisterTests/CashRegisterTests.cs b/CashRegisterTests/CashRegisterTests.cs index 2cfc8135..dbdc7f7c 100644 --- a/CashRegisterTests/CashRegisterTests.cs +++ b/CashRegisterTests/CashRegisterTests.cs @@ -9,7 +9,7 @@ namespace CashRegisterTests { public class CashRegisterTests { -#region SETUP + #region SETUP private readonly Mock currencyMock; private readonly Mock tenderStrategyMock; @@ -24,42 +24,6 @@ public CashRegisterTests() currencyMock = new Mock(); tenderStrategyMock = new Mock(); } - private void SetupUSDMockCurrency() - { - currencyMock.Setup(p => p.Bills).Returns(new List() { - new Bill(100000, "one hundred thousand", "one hundred thousands"), - new Bill(10000, "ten thousand", "ten thousands"), - new Bill(5000, "five thousand", "five thousands"), - new Bill(1000, "thousand", "thousands"), - new Bill(500, "five hundred", "five hundreds"), - new Bill(100, "hundred", "hundreds"), - new Bill(50, "fifty", "fifties"), - new Bill(20, "twenty", "twenties"), - new Bill(5, "five", "fives"), - new Bill(1, "dollar", "dollars") - }); - currencyMock.Setup(p => p.Coins).Returns(new List() { - new Coin(.25m, "quarter", "quarters"), - new Coin(.10m, "dime", "dimes"), - new Coin(.05m, "nickel", "nickels"), - new Coin(.01m, "penny","pennies") - }); - currencyMock.Setup(p => p.AllDenominations).Returns(new List(){new Bill(100000, "one hundred thousand", "one hundred thousands"), - new Bill(10000, "ten thousand", "ten thousands"), - new Bill(5000, "five thousand", "five thousands"), - new Bill(1000, "thousand", "thousands"), - new Bill(500, "five hundred", "five hundreds"), - new Bill(100, "hundred", "hundreds"), - new Bill(50, "fifty", "fifties"), - new Bill(20, "twenty", "twenties"), - new Bill(5, "five", "fives"), - new Bill(1, "dollar", "dollars"), - new Coin(.25m, "quarter", "quarters"), - new Coin(.10m, "dime", "dimes"), - new Coin(.05m, "nickel", "nickels"), - new Coin(.01m, "penny","pennies") - }); - } #endregion [Fact] @@ -91,6 +55,7 @@ public void CashRegisterTenderValueIsAccuratelySetBasedOnTextFileInput() Assert.Equal(expected, register.TenderValue); } + #region Exception Tests [Fact] public void CashRegisterThrowsFileNotFoundExceptionGivenEmptyOrNullPath() diff --git a/CashRegisterTests/CurrencyTests.cs b/CashRegisterTests/CurrencyTests.cs index 1dda478f..68c05d9a 100644 --- a/CashRegisterTests/CurrencyTests.cs +++ b/CashRegisterTests/CurrencyTests.cs @@ -62,42 +62,8 @@ public void CurrencyAllDenominationsReturnsConcatForBillsAndCoins() Assert.Equal(currency.Coins[i], currency.AllDenominations[i + currency.Bills.Count]); // coins should start after bills due to the sort/reverse (denomination based) } } - #region Removed "ToString" override - //[Fact] - //public void CurrencyToStringReturnsExpectedStringWithSingles() - //{ - // Currency currency = new CurrencyTestSingularNameCurrency(); - - // string expected = "1 one hundred thousand,1 ten thousand,1 five thousand,1 thousand,1 five hundred,1 hundred,1 fifty,1 twenty,1 ten,1 five,1 dollar,1 quarter,1 dime,1 nickel,1 penny"; - // string actual = currency.ToString(); - - // Assert.Equal(expected, actual); - //} - //[Fact] - //public void CurrencyToStringReturnsExpectedStringWithPlurals() - //{ - // Currency currency = new CurrencyTestPluralNameCurrency(); - - // string expected = "50 one hundred thousands,2 ten thousands,2 five thousands,4 thousands,2 five hundreds,600 hundreds,56 fifties,2 twenties,4 tens,3 fives,1245 dollars,500 quarters,5 dimes,40 nickels,100 pennies"; - // string actual = currency.ToString(); - - // Assert.Equal(expected, actual); - //} - //[Fact] - //public void CurrencyToStringReturnsExpectedStringWithPluralsWhenZeroMoneyExists() - //{ - // Currency currency = new CurrencyTestPluralNameCurrencyNoMoney(); - - // string expected = "No Change Due."; - // string actual = currency.ToString(); - - // Assert.Equal(expected, actual); - //} - #endregion - } - #region CurrecyTestClass public class CurrencyTestSortReverseCurrency : Currency { diff --git a/CashRegisterTests/TenderStrategyTests.cs b/CashRegisterTests/TenderStrategyTests.cs new file mode 100644 index 00000000..1484bcb6 --- /dev/null +++ b/CashRegisterTests/TenderStrategyTests.cs @@ -0,0 +1,81 @@ +using CashRegisterConsumer; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace TenderStrategyTests +{ + public class TenderStrategyTests + { + #region Setup + private readonly Mock currencyMock; + public TenderStrategyTests() + { + currencyMock = new Mock(); + } + #endregion + + [Fact] + public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForSingularCount() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars", 1) }); + string expected = "1 dollar\n"; + + TenderStrategy tenderStrategy = new TenderStrategyTestMock(); + var actual = tenderStrategy.Display(currencyMock.Object); + + Assert.Equal(expected, actual); + } + [Fact] + public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForMultipleCount() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars", 2) }); + string expected = "2 dollars\n"; + + TenderStrategy tenderStrategy = new TenderStrategyTestMock(); + var actual = tenderStrategy.Display(currencyMock.Object); + + Assert.Equal(expected, actual); + } + [Fact] + public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForZeroCount() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars", 0) }); + string expected = "No Change Due.\n"; + + TenderStrategy tenderStrategy = new TenderStrategyTestMock(); + var actual = tenderStrategy.Display(currencyMock.Object); + + Assert.Equal(expected, actual); + } + [Fact] + public void TenderStrategyDisplayReturnsProperStringValueForMultipleDenominations() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { + new Bill(5, "five dollar", "five dollars", 10), + new Bill(1, "dollar", "dollars", 1), + new Coin(.01m, "dime", "dimes",3), + new Coin(.01m,"penny","pennies",1) + }); + string expected = "10 five dollars,1 dollar,3 dimes,1 penny\n"; + + TenderStrategy tenderStrategy = new TenderStrategyTestMock(); + var actual = tenderStrategy.Display(currencyMock.Object); + + Assert.Equal(expected, actual); + } + + } + + #region MockTenderStrategy Abstract + internal class TenderStrategyTestMock : TenderStrategy + { + public override ICurrency Calculate(ICurrency currency, decimal price, decimal tender) + { + throw new NotImplementedException(); + } + } + #endregion +} From 197596df32080c16538509ee2aff88c75494a892 Mon Sep 17 00:00:00 2001 From: Paul L West Date: Sun, 9 Feb 2020 10:44:54 +0900 Subject: [PATCH 08/15] Removed unneccessary code. Updated TenderStrategy Display method with LINQ for string building. --- .../Currencies/Abstract/Currency.cs | 17 ------------ .../CashRegister/Currencies/Abstract/Money.cs | 6 +---- .../Strategies/Abstract/TenderStrategy.cs | 27 ++++++++++++------- CashRegister/_diagramCashRegister.cd | 6 ++--- 4 files changed, 21 insertions(+), 35 deletions(-) diff --git a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs index 5474d2a7..2e9a5450 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs @@ -35,22 +35,5 @@ public void Clear() money.Clear(); } } - - //public override string ToString() - //{ - // StringBuilder sr = new StringBuilder(); - // foreach (Money money in AllDenominations) - // { - // if (money.Count > 0) - // { - // sr.Append(String.Format("{0} {1},", money.Count, money.Name)); - // } - // } - - // if (sr.Length == 0) - // return "No Change Due."; // not part of the requirements, yet exact change is a viable value. - // else - // return sr.ToString().Trim(','); - //} } } diff --git a/CashRegister/CashRegister/Currencies/Abstract/Money.cs b/CashRegister/CashRegister/Currencies/Abstract/Money.cs index cdb05041..a7f93759 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Money.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Money.cs @@ -15,7 +15,6 @@ public abstract class Money : IComparable public string Name { get { return (_count == 1) ? _singleName : _pluralName; } } public int Count { get { return _count; } } - public Money() { } // for Moq public Money(decimal denomination, string singleName, string pluralName) { this._denomination = denomination; @@ -23,11 +22,8 @@ public Money(decimal denomination, string singleName, string pluralName) this._pluralName = pluralName; this._count = 0; } - public Money(decimal denomination, string singleName, string pluralName, int count) + public Money(decimal denomination, string singleName, string pluralName, int count):this(denomination,singleName,pluralName) { - this._denomination = denomination; - this._singleName = singleName; - this._pluralName = pluralName; this._count = count; } diff --git a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs index 17cd4d8e..cc40f567 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs @@ -1,23 +1,30 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace CashRegisterConsumer { public abstract class TenderStrategy : ITenderStrategy { - //public abstract string Calculate(ICurrency currency, decimal price, decimal tender); - public virtual string Display(ICurrency currency) { - StringBuilder sr = new StringBuilder(); - foreach (Money money in currency.AllDenominations) - { - if (money.Count > 0) - { - sr.Append(String.Format("{0} {1},", money.Count, money.Name)); - } - } + + // METHOD 1 - with foreach loop (did not remove code so reviewers could evaluate both methods) + //StringBuilder sr = new StringBuilder(); + //foreach (Money money in currency.AllDenominations) + //{ + // if (money.Count > 0) + // { + // sr.Append(String.Format("{0} {1},", money.Count, money.Name)); + // } + //} + + // METHOD 2 - Using LINQ.. both one line and 2.. 1 line code commented out due to complexity + var money = currency.AllDenominations.Where(x => x.Count > 0); + var sr = money.Aggregate(new StringBuilder(), (x, y) => x.Append(String.Format("{0} {1},", y.Count, y.Name))); + //var sr = currency.AllDenominations.Where(x => x.Count > 0).Aggregate(new StringBuilder(), (x, y) => x.Append(String.Format("{0} {1},", y.Count, y.Name))); + if (sr.Length == 0) return String.Format("{0}\n", "No Change Due."); // not part of the requirements, yet exact change is a viable value. diff --git a/CashRegister/_diagramCashRegister.cd b/CashRegister/_diagramCashRegister.cd index f4f6aac6..eb6c679a 100644 --- a/CashRegister/_diagramCashRegister.cd +++ b/CashRegister/_diagramCashRegister.cd @@ -24,7 +24,7 @@ - AAAABAAAAAAAAAAEAAAADAAACAAAAAAAAAAACABEAAA= + AAAABAAAAAAAAAAAAAAADAAACAAAAAAAAAAACABEAAA= CashRegister\Currencies\Abstract\Currency.cs @@ -75,7 +75,7 @@ - AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + AAAAAAAAAAAAAAAAAAAAAQAAACAAAAAAAAAAAAAAAAA= CashRegister\Strategies\Abstract\TenderStrategy.cs @@ -118,7 +118,7 @@ - AAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAA= + AAAAAAAAAAAAAAAAAAAAAQAAACAAAAAAAAAAAAAAAAA= CashRegister\Strategies\Abstract\ITenderStrategy.cs From be597a76bb9ba74fd9d5c9546ead5762b0cefbeb Mon Sep 17 00:00:00 2001 From: Paul L West Date: Sun, 9 Feb 2020 10:51:34 +0900 Subject: [PATCH 09/15] Removed more unnecessary code and ran Code Maid to clean up empty space. --- .../CashRegister/Abstract/CashRegister.cs | 10 +++--- .../CashRegister/Concrete/POSCashRegister.cs | 12 +++---- .../Currencies/Abstract/Currency.cs | 6 ++-- .../CashRegister/Currencies/Abstract/Money.cs | 8 +++-- .../Strategies/Abstract/ITenderStrategy.cs | 2 +- .../Strategies/Abstract/TenderStrategy.cs | 6 +--- .../Strategies/Concrete/Random3Strategy.cs | 35 ------------------ .../Concrete/StandardTenderStrategy.cs | 28 ++------------- CashRegister/Program.cs | 12 +++---- CashRegisterTests/CashRegisterTests.cs | 27 +++++++++----- CashRegisterTests/CurrencyTests.cs | 36 ++++++++++--------- CashRegisterTests/MoneyTests.cs | 36 ++++++++++++------- CashRegisterTests/Random3StrategyTests.cs | 27 ++++++++------ .../StandardTenderStrategyTests.cs | 20 +++++++---- CashRegisterTests/TenderStrategyTests.cs | 18 ++++++---- 15 files changed, 128 insertions(+), 155 deletions(-) diff --git a/CashRegister/CashRegister/Abstract/CashRegister.cs b/CashRegister/CashRegister/Abstract/CashRegister.cs index 3cec0479..facd538e 100644 --- a/CashRegister/CashRegister/Abstract/CashRegister.cs +++ b/CashRegister/CashRegister/Abstract/CashRegister.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Text; @@ -15,11 +14,12 @@ public abstract class CashRegister public decimal PriceValue { get { return _price; } set { _price = value; } } public decimal TenderValue { get { return _tender; } set { _tender = value; } } - public CashRegister(ICurrency currency, ITenderStrategy tenderStrategy) + public CashRegister(ICurrency currency, ITenderStrategy tenderStrategy) { RegisterCurrency(currency); RegisterTenderStrategy(tenderStrategy); } + public virtual void RegisterCurrency(ICurrency currency) { if (currency == null) @@ -29,6 +29,7 @@ public virtual void RegisterCurrency(ICurrency currency) this._currency = currency; } + public virtual void RegisterTenderStrategy(ITenderStrategy tenderStrategy) { if (tenderStrategy == null) @@ -56,7 +57,6 @@ public string Tender(string path) while (!sr.EndOfStream) { - // log transaction count (for exception handling) _transactionCount++; // setup the _price and _tender for this transaction @@ -79,8 +79,9 @@ public string Tender(string path) throw; } } + private void SetTransactionAmounts(StreamReader sr) - { + { try { // reset for new transaction @@ -106,6 +107,5 @@ private void SetTransactionAmounts(StreamReader sr) throw new FormatException(String.Format("{0} : Line {1}", e.Message, _transactionCount)); } } - } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Concrete/POSCashRegister.cs b/CashRegister/CashRegister/Concrete/POSCashRegister.cs index 04f2a026..15f1c669 100644 --- a/CashRegister/CashRegister/Concrete/POSCashRegister.cs +++ b/CashRegister/CashRegister/Concrete/POSCashRegister.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CashRegisterConsumer +namespace CashRegisterConsumer { public class POSCashRegister : CashRegister { - public POSCashRegister(ICurrency currency, ITenderStrategy tenderStrategy):base(currency, tenderStrategy) { } + public POSCashRegister(ICurrency currency, ITenderStrategy tenderStrategy) : base(currency, tenderStrategy) + { + } } -} +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs index 2e9a5450..9cb6e5de 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; namespace CashRegisterConsumer { @@ -36,4 +34,4 @@ public void Clear() } } } -} +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Currencies/Abstract/Money.cs b/CashRegister/CashRegister/Currencies/Abstract/Money.cs index a7f93759..08b75469 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Money.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Money.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; namespace CashRegisterConsumer { @@ -22,7 +21,8 @@ public Money(decimal denomination, string singleName, string pluralName) this._pluralName = pluralName; this._count = 0; } - public Money(decimal denomination, string singleName, string pluralName, int count):this(denomination,singleName,pluralName) + + public Money(decimal denomination, string singleName, string pluralName, int count) : this(denomination, singleName, pluralName) { this._count = count; } @@ -31,14 +31,17 @@ public void Add(int count) { _count += count; } + public void Subtract(int count) { _count -= count; } + public void Clear() { _count = 0; } + public int CompareTo(object other) { if (other == null) return 1; @@ -49,6 +52,5 @@ public int CompareTo(object other) else throw new ArgumentException("Object is not Money"); } - } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs index 9e5555fd..9064aba1 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/ITenderStrategy.cs @@ -3,7 +3,7 @@ public interface ITenderStrategy { ICurrency Calculate(ICurrency currency, decimal price, decimal tender); - string Display(ICurrency currency); + string Display(ICurrency currency); } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs index cc40f567..bce08567 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text; @@ -9,7 +8,6 @@ public abstract class TenderStrategy : ITenderStrategy { public virtual string Display(ICurrency currency) { - // METHOD 1 - with foreach loop (did not remove code so reviewers could evaluate both methods) //StringBuilder sr = new StringBuilder(); //foreach (Money money in currency.AllDenominations) @@ -25,7 +23,6 @@ public virtual string Display(ICurrency currency) var sr = money.Aggregate(new StringBuilder(), (x, y) => x.Append(String.Format("{0} {1},", y.Count, y.Name))); //var sr = currency.AllDenominations.Where(x => x.Count > 0).Aggregate(new StringBuilder(), (x, y) => x.Append(String.Format("{0} {1},", y.Count, y.Name))); - if (sr.Length == 0) return String.Format("{0}\n", "No Change Due."); // not part of the requirements, yet exact change is a viable value. else @@ -33,6 +30,5 @@ public virtual string Display(ICurrency currency) } public abstract ICurrency Calculate(ICurrency currency, decimal price, decimal tender); - } -} +} \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs index eda331a9..198e6eaf 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; namespace CashRegisterConsumer { @@ -38,38 +36,5 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t return new StandardTenderStrategy().Calculate(currency, price, tender); } } - - //public override string Calculate(ICurrency currency, decimal price, decimal tender) - //{ - // if (currency.AllDenominations.Count == 0) - // throw new InvalidCurrencyException("No currency denominations found"); - - // if (Math.Abs(((price * 100) % 10)) == 3) - // { - // Random random = new Random(); - // int count; - // decimal change = tender - price; - - // while (change >= currency.AllDenominations.Min(x => x.Denomination)) - // { - // foreach (Money money in currency.AllDenominations) - // { - // if (money.Denomination <= change) // if the current denomination is less than the change - // { - // count = random.Next(1, (int)Math.Floor(change / money.Denomination)); // get a random count (not bigger than the change) - // money.Add(count); // add the appropriate amount of this denomination based on our random - // change -= (money.Denomination * count); // remove the money denomination(times count) from the change - // } - // } - // } - // return currency.ToString(); - // } - // else - // { - // // when not using the "random", we can just use the standard strategy - // return new StandardTenderStrategy().Calculate(currency, price, tender); - // } - //} - } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs index 6aa8146c..3edecc90 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs @@ -1,33 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Linq; namespace CashRegisterConsumer { public class StandardTenderStrategy : TenderStrategy { - //public override string Calculate(ICurrency currency, decimal price, decimal tender) - //{ - // if (currency.AllDenominations.Count == 0) - // throw new InvalidCurrencyException("No currency denominations found"); - - // decimal change = tender - price; - // // this is to ensure that coinage less than the smallest denomination will not cause an infinite loop. - // // the extra "change" is ignored.. this can occur because some currencies do not have decimal value denominations (YEN) - // while (change >= currency.AllDenominations.Min(x => x.Denomination)) - // { - // foreach (Money money in currency.AllDenominations) - // { - // while (change >= money.Denomination) - // { - // money.Add(1); - // change -= money.Denomination; - // } - // } - // } - // return currency.ToString(); - //} public override ICurrency Calculate(ICurrency currency, decimal price, decimal tender) { if (currency.AllDenominations.Count == 0) @@ -50,4 +26,4 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t return currency; } } -} +} \ No newline at end of file diff --git a/CashRegister/Program.cs b/CashRegister/Program.cs index 3e56b0e9..49a61b94 100644 --- a/CashRegister/Program.cs +++ b/CashRegister/Program.cs @@ -2,11 +2,10 @@ namespace CashRegisterConsumer { - class Program + internal class Program { - static void Main(string[] args) + private static void Main(string[] args) { - // Consumer Console... This is for the benefit of the code submission // to help the evaluators of my code to see the CashRegister Module // as it would be used by a consuming class in real world development @@ -17,21 +16,18 @@ static void Main(string[] args) Console.WriteLine(); Console.ReadKey(); - CashRegister register = new POSCashRegister(new USD(), new Random3Strategy()); var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\CashRegisterInputFiles\USD Input File.txt"); Console.WriteLine(result); // for example (change the register to use YEN with a Standard Tender Strategy note that the demoniations are different so - // the same files that use "USD" decimals will be off. There are no decimals in YEN + // the same files that use "USD" decimals will be off. There are no decimals in YEN //register.RegisterCurrency(new YEN()); //register.RegisterTenderStrategy(new StandardTenderStrategy()); //result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\Input\YEN Input File.txt"); //Console.WriteLine(result); - Console.ReadKey(); - } } -} +} \ No newline at end of file diff --git a/CashRegisterTests/CashRegisterTests.cs b/CashRegisterTests/CashRegisterTests.cs index dbdc7f7c..2f435dc6 100644 --- a/CashRegisterTests/CashRegisterTests.cs +++ b/CashRegisterTests/CashRegisterTests.cs @@ -1,30 +1,32 @@ using CashRegisterConsumer; -using System; -using Xunit; using Moq; +using System; using System.Collections.Generic; using System.IO; +using Xunit; namespace CashRegisterTests { public class CashRegisterTests { #region SETUP + private readonly Mock currencyMock; private readonly Mock tenderStrategyMock; - private readonly string TenderValueTestFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\ValueTests\TenderValueTestFile.txt"; private readonly string PriceValueTestFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\ValueTests\PriceValueTestFile.txt"; private readonly string EmptyFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\EmptyFile.txt"; private readonly string EmptyLineFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\EmptyLineFile.txt"; private readonly string NotEnoughTenderFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\TenderLessThanPrice.txt"; + public CashRegisterTests() { currencyMock = new Mock(); tenderStrategyMock = new Mock(); } - #endregion + + #endregion SETUP [Fact] public void CashRegisterPriceValueIsAccuratelySetBasedOnTextFileInput() @@ -40,6 +42,7 @@ public void CashRegisterPriceValueIsAccuratelySetBasedOnTextFileInput() // are they equal???? Assert.Equal(expected, register.PriceValue); } + [Fact] public void CashRegisterTenderValueIsAccuratelySetBasedOnTextFileInput() { @@ -55,8 +58,8 @@ public void CashRegisterTenderValueIsAccuratelySetBasedOnTextFileInput() Assert.Equal(expected, register.TenderValue); } - #region Exception Tests + [Fact] public void CashRegisterThrowsFileNotFoundExceptionGivenEmptyOrNullPath() { @@ -64,6 +67,7 @@ public void CashRegisterThrowsFileNotFoundExceptionGivenEmptyOrNullPath() CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); Assert.Throws(() => register.Tender("")); } + [Fact] public void CashRegisterThrowsFileNotFoundExceptionGivenNullPath() { @@ -71,13 +75,15 @@ public void CashRegisterThrowsFileNotFoundExceptionGivenNullPath() CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); Assert.Throws(() => register.Tender(null)); } + [Fact] - public void CashRegisterThrowsFormatExceptionWhenEmptyFileIsProvided() + public void CashRegisterThrowsFormatExceptionWhenEmptyFileIsProvided() { currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1m, "testMoney", "testMonies") }); CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); Assert.Throws(() => register.Tender(EmptyFile)); } + [Fact] public void CashRegisterThrowsFormatExceptionWhenEmptyLineFoundInFileProvided() { @@ -85,6 +91,7 @@ public void CashRegisterThrowsFormatExceptionWhenEmptyLineFoundInFileProvided() CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); Assert.Throws(() => register.Tender(EmptyLineFile)); } + [Fact] public void CashRegisterThrowsNotEnoughTenderExceptionWhenTenderIsLessThanPrice() { @@ -92,24 +99,26 @@ public void CashRegisterThrowsNotEnoughTenderExceptionWhenTenderIsLessThanPrice( CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); Assert.Throws(() => register.Tender(NotEnoughTenderFile)); } + [Fact] public void CashRegisterThrowsInvalidCurrencyExceptionWhenNoDenominationsFound() { currencyMock.Setup(p => p.AllDenominations).Returns(new List()); Assert.Throws(() => new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object)); } + [Fact] public void CashRegisterThrowsNullReferenceExceptionWhenAttemptingToRegisterNullCurrency() { Assert.Throws(() => new POSCashRegister(null, tenderStrategyMock.Object)); } + [Fact] public void CashRegisterThrowsNullReferenceExceptionWhenAttemptingToRegisterNullTenderStrategy() { Assert.Throws(() => new POSCashRegister(currencyMock.Object, null)); } - #endregion - + #endregion Exception Tests } -} +} \ No newline at end of file diff --git a/CashRegisterTests/CurrencyTests.cs b/CashRegisterTests/CurrencyTests.cs index 68c05d9a..b34da92b 100644 --- a/CashRegisterTests/CurrencyTests.cs +++ b/CashRegisterTests/CurrencyTests.cs @@ -1,21 +1,19 @@ using CashRegisterConsumer; -using System; -using Xunit; -using Moq; using System.Collections.Generic; -using System.IO; - +using Xunit; namespace CurrencyTests { public class CurrencyTests { #region Setup + public CurrencyTests() { - } - #endregion + + #endregion Setup + [Fact] public void CurrencyClearClearsMoneyCountForBills() { @@ -28,8 +26,8 @@ public void CurrencyClearClearsMoneyCountForBills() { Assert.True(bill.Count == 0); } - } + [Fact] public void CurrencyClearClearsMoneyCountForCoins() { @@ -42,22 +40,21 @@ public void CurrencyClearClearsMoneyCountForCoins() { Assert.True(coin.Count == 0); } - } + [Fact] public void CurrencyAllDenominationsReturnsConcatForBillsAndCoins() { - - // this also effectively tests the "sort/reverse" functionality of the InitializeCurrency method + // this also effectively tests the "sort/reverse" functionality of the InitializeCurrency method // so creating a new method for that would be redundent (not necessarily bad though) Currency currency = new CurrencyTestPluralNameCurrencyNoMoney(); - for (int i = 0; i < currency.Bills.Count-1; i++) + for (int i = 0; i < currency.Bills.Count - 1; i++) { Assert.Equal(currency.Bills[i], currency.AllDenominations[i]); } - for (int i = 0; i < currency.Coins.Count-1; i++) + for (int i = 0; i < currency.Coins.Count - 1; i++) { Assert.Equal(currency.Coins[i], currency.AllDenominations[i + currency.Bills.Count]); // coins should start after bills due to the sort/reverse (denomination based) } @@ -65,9 +62,12 @@ public void CurrencyAllDenominationsReturnsConcatForBillsAndCoins() } #region CurrecyTestClass + public class CurrencyTestSortReverseCurrency : Currency { - public CurrencyTestSortReverseCurrency():base() { } + public CurrencyTestSortReverseCurrency() : base() + { + } protected override void InitializeCurrency() { @@ -93,6 +93,7 @@ protected override void InitializeCurrency() }; } } + public class CurrencyTestSingularNameCurrency : Currency { protected override void InitializeCurrency() @@ -119,6 +120,7 @@ protected override void InitializeCurrency() }; } } + public class CurrencyTestPluralNameCurrency : Currency { protected override void InitializeCurrency() @@ -145,6 +147,7 @@ protected override void InitializeCurrency() }; } } + public class CurrencyTestPluralNameCurrencyNoMoney : Currency { protected override void InitializeCurrency() @@ -171,5 +174,6 @@ protected override void InitializeCurrency() }; } } - #endregion -} + + #endregion CurrecyTestClass +} \ No newline at end of file diff --git a/CashRegisterTests/MoneyTests.cs b/CashRegisterTests/MoneyTests.cs index 2b4f97d4..786835e1 100644 --- a/CashRegisterTests/MoneyTests.cs +++ b/CashRegisterTests/MoneyTests.cs @@ -1,8 +1,5 @@ using CashRegisterConsumer; using Moq; -using System; -using System.Collections.Generic; -using System.Text; using Xunit; namespace MoneyTests @@ -10,11 +7,12 @@ namespace MoneyTests public class MoneyTests { #region Setup + public MoneyTests() { - } - #endregion + + #endregion Setup [Fact] public void MoneyConstructionSetsDenominationAccurately() @@ -24,6 +22,7 @@ public void MoneyConstructionSetsDenominationAccurately() Assert.Equal(expected, money.Denomination); } + [Fact] public void MoneyConstructionSetsSingularNameAccurately() { @@ -32,6 +31,7 @@ public void MoneyConstructionSetsSingularNameAccurately() Assert.Equal(expected, money.Name); } + [Fact] public void MoneyConstructionSetsPluralNameAccurately() { @@ -40,6 +40,7 @@ public void MoneyConstructionSetsPluralNameAccurately() Assert.Equal(expected, money.Name); } + [Fact] public void MoneyConstructionSetsPluralNameWithZeroCount() { @@ -61,10 +62,11 @@ public void MoneyAddsCorrectAmount() money.Add(-1); Assert.Equal(0, money.Count); } + [Fact] public void MoneySubtractsCorrectAmount() { - Money money = new MoneyTestMoney(1, "test", "tests",5); + Money money = new MoneyTestMoney(1, "test", "tests", 5); Assert.Equal(5, money.Count); money.Subtract(1); @@ -73,13 +75,13 @@ public void MoneySubtractsCorrectAmount() money.Subtract(-1); Assert.Equal(5, money.Count); } + [Fact] public void MoneyClearsCount() { Money money = new MoneyTestMoney(1, "test", "tests", 5); Assert.Equal(5, money.Count); - money.Clear(); Assert.Equal(0, money.Count); } @@ -92,6 +94,7 @@ public void MoneyCompareToReturnsZeroWhenEqual() Assert.Equal(0, moneyToCompare.Object.CompareTo(moneyToCompareTo.Object)); } + [Fact] public void MoneyCompareToReturnsOneWhenMoreThan() { @@ -100,22 +103,29 @@ public void MoneyCompareToReturnsOneWhenMoreThan() Assert.Equal(1, moneyToCompare.Object.CompareTo(moneyToCompareTo.Object)); } + [Fact] public void MoneyCompareToReturnsMinusOneWhenLessThan() { - Mock moneyToCompare = new Mock(0.5m,"compare","compares"); + Mock moneyToCompare = new Mock(0.5m, "compare", "compares"); Mock moneyToCompareTo = new Mock(1m, "compare", "compares"); Assert.Equal(-1, moneyToCompare.Object.CompareTo(moneyToCompareTo.Object)); } } - #region MoneyTestClass + public class MoneyTestMoney : Money { - public MoneyTestMoney(decimal denomination, string singleName, string pluralName) : base(denomination, singleName, pluralName) { } - public MoneyTestMoney(decimal denomination, string singleName, string pluralName, int count) : base(denomination, singleName, pluralName, count) { } + public MoneyTestMoney(decimal denomination, string singleName, string pluralName) : base(denomination, singleName, pluralName) + { + } + + public MoneyTestMoney(decimal denomination, string singleName, string pluralName, int count) : base(denomination, singleName, pluralName, count) + { + } } - #endregion -} + + #endregion MoneyTestClass +} \ No newline at end of file diff --git a/CashRegisterTests/Random3StrategyTests.cs b/CashRegisterTests/Random3StrategyTests.cs index 896721f9..14867201 100644 --- a/CashRegisterTests/Random3StrategyTests.cs +++ b/CashRegisterTests/Random3StrategyTests.cs @@ -1,8 +1,6 @@ using CashRegisterConsumer; using Moq; -using System; using System.Collections.Generic; -using System.Text; using Xunit; namespace TenderStrategyTests @@ -10,17 +8,20 @@ namespace TenderStrategyTests public class Random3StrategyTests { #region Setup + private readonly Mock mockCurrency; + public Random3StrategyTests() { mockCurrency = new Mock(); } - #endregion + + #endregion Setup [Fact] public void Random3TenderStrategyCalculatesChangeCorrectlyBasedOnPriceAndTenderDifference() { - mockCurrency.Setup(p => p.AllDenominations).Returns(new List() + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(10, "ten", "tens"), new Bill(5, "five", "fives"), new Bill(1, "dollar", "dollars"), @@ -34,11 +35,10 @@ public void Random3TenderStrategyCalculatesChangeCorrectlyBasedOnPriceAndTenderD var result = tenderStrategy.Calculate(mockCurrency.Object, 123.03m, 3124.53m); // 3001.50 in change foreach (Money money in mockCurrency.Object.AllDenominations) { - actual += (money.Count*money.Denomination); + actual += (money.Count * money.Denomination); } Assert.Equal(expected, actual); - foreach (Money money in mockCurrency.Object.AllDenominations) { money.Clear(); @@ -52,8 +52,8 @@ public void Random3TenderStrategyCalculatesChangeCorrectlyBasedOnPriceAndTenderD actual += (money.Count * money.Denomination); } Assert.Equal(expected, actual); - } + [Fact] public void Random3CalculatesExactTenderCorrectly() { @@ -75,6 +75,7 @@ public void Random3CalculatesExactTenderCorrectly() } Assert.Equal(expected, actual); } + [Fact] public void Random3CalculatesZeroTenderWhenTenderIsLessThanPrice() { @@ -111,6 +112,7 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimal Assert.Equal(2, money.Count); // price - tender = 2 dollars in change } } + [Fact] public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDenominationsAndRandomReturn() { @@ -123,6 +125,7 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDen Assert.Equal(10, money.Count); // price - tender = 10 pennies in change } } + [Fact] public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimalDenominationsAndRandomReturn() { @@ -145,9 +148,10 @@ public void Random3TenderStrategyReturnsCurrentyWithNoMoneyValuesAndNonRandomRet foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) { - Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. + Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. } // Consider refactoring ToString. Hard testing indicates a design flaw. } + [Fact] public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimalDenominationsAndNonRandomReturn() { @@ -162,6 +166,7 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimal Assert.Equal(2, money.Count); // price - tender = 2 dollars in change } } + [Fact] public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDenominationsAndNonRandomReturn() { @@ -174,6 +179,7 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDen Assert.Equal(10, money.Count); // price - tender = 10 pennies in change } } + [Fact] public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimalDenominationsAndNonRandomReturn() { @@ -188,6 +194,7 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimal } #region Exception Testing + [Fact] public void Random3TenderStrategyCalculateThrowsInvalidCurrencyExceptionWhenNoDenominationsFound() { @@ -195,7 +202,7 @@ public void Random3TenderStrategyCalculateThrowsInvalidCurrencyExceptionWhenNoDe ITenderStrategy tenderStrategy = new Random3Strategy(); Assert.Throws(() => tenderStrategy.Calculate(mockCurrency.Object, 0, 0)); } - #endregion + #endregion Exception Testing } -} +} \ No newline at end of file diff --git a/CashRegisterTests/StandardTenderStrategyTests.cs b/CashRegisterTests/StandardTenderStrategyTests.cs index 7eac95eb..e35dd0e3 100644 --- a/CashRegisterTests/StandardTenderStrategyTests.cs +++ b/CashRegisterTests/StandardTenderStrategyTests.cs @@ -1,8 +1,6 @@ using CashRegisterConsumer; using Moq; -using System; using System.Collections.Generic; -using System.Text; using Xunit; namespace TenderStrategyTests @@ -10,13 +8,15 @@ namespace TenderStrategyTests public class StandardTenderStrategyTests { #region Setup + private readonly Mock mockCurrency; + public StandardTenderStrategyTests() { mockCurrency = new Mock(); } - #endregion + #endregion Setup [Fact] public void StandardCalculatesZeroTenderWhenTenderIsLessThanPrice() @@ -39,18 +39,20 @@ public void StandardCalculatesZeroTenderWhenTenderIsLessThanPrice() } Assert.Equal(expected, actual); } + [Fact] public void StandardTenderStrategyReturnsCurrentyWithNoMoneyValues() { - mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1,"test","tests") }); + mockCurrency.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "test", "tests") }); ITenderStrategy tenderStrategy = new StandardTenderStrategy(); var actual = tenderStrategy.Calculate(mockCurrency.Object, 0, 0); foreach (Money money in actual.AllDenominations)// mockCurrency.Object.AllDenominations) { - Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. + Assert.Equal(0, money.Count); // due to the "ToString" override being used, we have to test the counts. } // Consider refactoring ToString. Hard testing indicates a design flaw. } + [Fact] public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecimalDenominations() { @@ -65,6 +67,7 @@ public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnNonDecima Assert.Equal(2, money.Count); // price - tender = 2 dollars in change } } + [Fact] public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDenominations() { @@ -77,6 +80,7 @@ public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDe Assert.Equal(10, money.Count); // price - tender = 10 pennies in change } } + [Fact] public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimalDenominations() { @@ -91,6 +95,7 @@ public void StandardTenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecima } #region Exception Testing + [Fact] public void StandardTenderStrategyCalculateThrowsInvalidCurrencyExceptionWhenNoDenominationsFound() { @@ -98,6 +103,7 @@ public void StandardTenderStrategyCalculateThrowsInvalidCurrencyExceptionWhenNoD ITenderStrategy tenderStrategy = new StandardTenderStrategy(); Assert.Throws(() => tenderStrategy.Calculate(mockCurrency.Object, 0, 0)); } - #endregion + + #endregion Exception Testing } -} +} \ No newline at end of file diff --git a/CashRegisterTests/TenderStrategyTests.cs b/CashRegisterTests/TenderStrategyTests.cs index 1484bcb6..0f122bd6 100644 --- a/CashRegisterTests/TenderStrategyTests.cs +++ b/CashRegisterTests/TenderStrategyTests.cs @@ -2,7 +2,6 @@ using Moq; using System; using System.Collections.Generic; -using System.Text; using Xunit; namespace TenderStrategyTests @@ -10,12 +9,15 @@ namespace TenderStrategyTests public class TenderStrategyTests { #region Setup + private readonly Mock currencyMock; + public TenderStrategyTests() { currencyMock = new Mock(); } - #endregion + + #endregion Setup [Fact] public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForSingularCount() @@ -28,6 +30,7 @@ public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForSingularC Assert.Equal(expected, actual); } + [Fact] public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForMultipleCount() { @@ -39,6 +42,7 @@ public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForMultipleC Assert.Equal(expected, actual); } + [Fact] public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForZeroCount() { @@ -50,10 +54,11 @@ public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForZeroCount Assert.Equal(expected, actual); } + [Fact] public void TenderStrategyDisplayReturnsProperStringValueForMultipleDenominations() { - currencyMock.Setup(p => p.AllDenominations).Returns(new List() { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(5, "five dollar", "five dollars", 10), new Bill(1, "dollar", "dollars", 1), new Coin(.01m, "dime", "dimes",3), @@ -66,10 +71,10 @@ public void TenderStrategyDisplayReturnsProperStringValueForMultipleDenomination Assert.Equal(expected, actual); } - } #region MockTenderStrategy Abstract + internal class TenderStrategyTestMock : TenderStrategy { public override ICurrency Calculate(ICurrency currency, decimal price, decimal tender) @@ -77,5 +82,6 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t throw new NotImplementedException(); } } - #endregion -} + + #endregion MockTenderStrategy Abstract +} \ No newline at end of file From 2a0dd083347466a6d840f05cca695a3802739802 Mon Sep 17 00:00:00 2001 From: Paul L West Date: Mon, 10 Feb 2020 02:09:18 +0900 Subject: [PATCH 10/15] Added overflow exception handling. Added Preface text for console upon starting application. --- .../CashRegister/Abstract/CashRegister.cs | 15 ++++++--- .../CashRegisterInputFiles/USD Input File.txt | 8 ++++- CashRegister/Preface.txt | 19 ++++++++++++ CashRegister/Program.cs | 31 ++++++++++++------- CashRegisterTests/CashRegisterTests.cs | 9 +++++- .../OverflowTransactionTestFile.txt | 1 + 6 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 CashRegister/Preface.txt create mode 100644 CashRegisterTests/Test Input Files/ValueTests/OverflowTransactionTestFile.txt diff --git a/CashRegister/CashRegister/Abstract/CashRegister.cs b/CashRegister/CashRegister/Abstract/CashRegister.cs index facd538e..7390fa7a 100644 --- a/CashRegister/CashRegister/Abstract/CashRegister.cs +++ b/CashRegister/CashRegister/Abstract/CashRegister.cs @@ -74,9 +74,9 @@ public string Tender(string path) { throw new FormatException($"The file {path} was not in the correct format", e); } - catch (Exception) // Is something missed? + catch (Exception e) // Is something missed? { - throw; + throw e; } } @@ -94,13 +94,20 @@ private void SetTransactionAmounts(StreamReader sr) _price = Decimal.Parse(input.Split(",")[0]); _tender = Decimal.Parse(input.Split(",")[1]); + if (_tender <= 0 || _price <= 0) + throw new InvalidCurrencyException($"Invalid negative currency. Please review line {_transactionCount} for errors."); + // ensure there is enough tender for the price. (they can be equal) if (_tender < _price) throw new NotEnoughTenderException($"Tender value less than price. Deficiency: {_price - _tender} on line {_transactionCount}"); } - catch (NotEnoughTenderException) + catch (OverflowException e) + { + throw new InvalidCurrencyException($"Invalid input. Please review line {_transactionCount} for errors.", e); + } + catch (NotEnoughTenderException e) { - throw; + throw new NotEnoughTenderException($"Please review line {_transactionCount} for errors", e); } catch (Exception e) { diff --git a/CashRegister/CashRegisterInputFiles/USD Input File.txt b/CashRegister/CashRegisterInputFiles/USD Input File.txt index 5d0bf229..fe2de4c4 100644 --- a/CashRegister/CashRegisterInputFiles/USD Input File.txt +++ b/CashRegister/CashRegisterInputFiles/USD Input File.txt @@ -1,4 +1,10 @@ 2.12,3.00 1.97,2.00 3.33,5.00 -5.00,5.00 \ No newline at end of file +5.00,5.00 +8.83,1470.30 +20.57,76.55 +3.91,71.61 +48.31,78.81 +54.63,85.09 +62.33,95.61 \ No newline at end of file diff --git a/CashRegister/Preface.txt b/CashRegister/Preface.txt new file mode 100644 index 00000000..536cf69b --- /dev/null +++ b/CashRegister/Preface.txt @@ -0,0 +1,19 @@ +I have created this project with the idea that the console is the consumer of a CashRegister system that allows for the injection +of both the Currency type (USD, YEN, etc...) along with the strategy for change calculation. Future development would be able to allow +for different strategies to be injected using IOC containers in a configuration to quickly change the functionality and currency of the +CashRegister system. + +I have put more comments in this code than I normally do in production code as too many comments can be a code smell. I understand that +comments are important, but are best used sparingly. It is more important to use correct naming conventions and clear code that can be read +easily, then to trash code with a bunch of comments that with time may not be updated to reflect the actual working of the software. Code itself +should be the documentation. + +If there are any questions or comments please email me at plwest.axiom@gmail.com and I will respond at the earliest possible time. I appreciate +your evaluation and giving me this oppritunity. Thank you. + + + + +Press any button to continue with using the CashRegister Module. (Simulates POS ""Tender"" actions). + + diff --git a/CashRegister/Program.cs b/CashRegister/Program.cs index 49a61b94..8a0d5899 100644 --- a/CashRegister/Program.cs +++ b/CashRegister/Program.cs @@ -1,24 +1,33 @@ using System; +using System.IO; namespace CashRegisterConsumer { internal class Program { + private static readonly string prefacePath = @"C:\Users\plwes\source\repos\CashRegister\CashRegister\Preface.txt"; private static void Main(string[] args) { - // Consumer Console... This is for the benefit of the code submission - // to help the evaluators of my code to see the CashRegister Module - // as it would be used by a consuming class in real world development - // Consider the console the POS where the "CashRegister" just tenders transactions. - Console.WriteLine(@"The Console is the ""CONSUMER"" of the CashRegister Module."); - Console.WriteLine(); - Console.WriteLine(@"Press any button to continue with using the CashRegister Module. (Simulates POS ""Tender"" actions)"); - Console.WriteLine(); + using (StreamReader sr = new StreamReader(prefacePath, System.Text.Encoding.UTF8)) + { + Console.SetWindowSize(150, 20); + Console.WriteLine(sr.ReadToEnd()); + } + Console.ReadKey(); - CashRegister register = new POSCashRegister(new USD(), new Random3Strategy()); - var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\CashRegisterInputFiles\USD Input File.txt"); - Console.WriteLine(result); + try + { + CashRegister register = new POSCashRegister(new USD(), new Random3Strategy()); + var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\CashRegisterInputFiles\USD Input File.txt"); + Console.WriteLine(result); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + if (e.InnerException != null) + Console.WriteLine(e.InnerException.Message); + } // for example (change the register to use YEN with a Standard Tender Strategy note that the demoniations are different so // the same files that use "USD" decimals will be off. There are no decimals in YEN diff --git a/CashRegisterTests/CashRegisterTests.cs b/CashRegisterTests/CashRegisterTests.cs index 2f435dc6..e5cbdd0f 100644 --- a/CashRegisterTests/CashRegisterTests.cs +++ b/CashRegisterTests/CashRegisterTests.cs @@ -19,7 +19,7 @@ public class CashRegisterTests private readonly string EmptyFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\EmptyFile.txt"; private readonly string EmptyLineFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\EmptyLineFile.txt"; private readonly string NotEnoughTenderFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\TenderLessThanPrice.txt"; - + private readonly string OverflowFile = @"C:\Users\plwes\source\repos\CashRegister\CashRegisterTests\Test Input Files\ValueTests\OverflowTransactionTestFile.txt"; public CashRegisterTests() { currencyMock = new Mock(); @@ -59,6 +59,13 @@ public void CashRegisterTenderValueIsAccuratelySetBasedOnTextFileInput() } #region Exception Tests + [Fact] + public void CashRegisterThrowOverflowExceptionGivenInputLargerThanLargestDecimalValue() + { + currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1m, "testMoney", "testMonies") }); + CashRegister register = new POSCashRegister(currencyMock.Object, tenderStrategyMock.Object); + Assert.Throws(() => register.Tender(OverflowFile)); + } [Fact] public void CashRegisterThrowsFileNotFoundExceptionGivenEmptyOrNullPath() diff --git a/CashRegisterTests/Test Input Files/ValueTests/OverflowTransactionTestFile.txt b/CashRegisterTests/Test Input Files/ValueTests/OverflowTransactionTestFile.txt new file mode 100644 index 00000000..1ee76b03 --- /dev/null +++ b/CashRegisterTests/Test Input Files/ValueTests/OverflowTransactionTestFile.txt @@ -0,0 +1 @@ +7922816251426433759354395033511, 79228162514264337593543950335111 \ No newline at end of file From 77e3593fdaa79a1dc85f3952e066e5d87af66c5d Mon Sep 17 00:00:00 2001 From: Paul L West Date: Mon, 10 Feb 2020 03:06:57 +0900 Subject: [PATCH 11/15] Updated comments Removed period from exact change response string. --- .../CashRegister/Abstract/CashRegister.cs | 2 +- .../Currencies/Abstract/Currency.cs | 7 +++--- .../CashRegister/Currencies/Abstract/Money.cs | 6 +++++ .../Strategies/Abstract/TenderStrategy.cs | 4 ++-- .../Strategies/Concrete/Random3Strategy.cs | 4 ++++ .../Concrete/StandardTenderStrategy.cs | 7 ++++-- .../InvalidCurrencyException.cs | 3 +++ .../NotEnoughTenderException.cs | 3 +++ CashRegister/Preface.txt | 7 +++--- CashRegister/Program.cs | 24 +++++++++++-------- CashRegisterTests/TenderStrategyTests.cs | 2 +- 11 files changed, 47 insertions(+), 22 deletions(-) diff --git a/CashRegister/CashRegister/Abstract/CashRegister.cs b/CashRegister/CashRegister/Abstract/CashRegister.cs index 7390fa7a..e7c34038 100644 --- a/CashRegister/CashRegister/Abstract/CashRegister.cs +++ b/CashRegister/CashRegister/Abstract/CashRegister.cs @@ -8,7 +8,7 @@ public abstract class CashRegister { private ICurrency _currency; private decimal _price, _tender; - private int _transactionCount; + private int _transactionCount; // for future use and current exception handling private ITenderStrategy _tenderStrategy; public decimal PriceValue { get { return _price; } set { _price = value; } } diff --git a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs index 9cb6e5de..607769ad 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Currency.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Currency.cs @@ -14,12 +14,13 @@ public abstract class Currency : ICurrency public Currency() { + // initialize lists this._bills = new List(); this._coins = new List(); - InitializeCurrency(); - this._bills.Sort(); - this._bills.Reverse(); + InitializeCurrency(); // load currency "money" based on inherited implementation + this._bills.Sort(); // ensure that we have the correct order of money + this._bills.Reverse(); // since the sort will make it small to large, we want large to small so foreach works easier. this._coins.Sort(); this._coins.Reverse(); } diff --git a/CashRegister/CashRegister/Currencies/Abstract/Money.cs b/CashRegister/CashRegister/Currencies/Abstract/Money.cs index 08b75469..bcfe509d 100644 --- a/CashRegister/CashRegister/Currencies/Abstract/Money.cs +++ b/CashRegister/CashRegister/Currencies/Abstract/Money.cs @@ -42,6 +42,12 @@ public void Clear() _count = 0; } + /// + /// IComparable override. Used for sorting and reversing the currency to ensure the list of Money is + /// from largest denomination to smallest. + /// + /// + /// public int CompareTo(object other) { if (other == null) return 1; diff --git a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs index bce08567..81028276 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs @@ -18,13 +18,13 @@ public virtual string Display(ICurrency currency) // } //} - // METHOD 2 - Using LINQ.. both one line and 2.. 1 line code commented out due to complexity + // METHOD 2 - Using LINQ.. both one line and 2 line versions. 1 line version commented out due to complexity and clarity var money = currency.AllDenominations.Where(x => x.Count > 0); var sr = money.Aggregate(new StringBuilder(), (x, y) => x.Append(String.Format("{0} {1},", y.Count, y.Name))); //var sr = currency.AllDenominations.Where(x => x.Count > 0).Aggregate(new StringBuilder(), (x, y) => x.Append(String.Format("{0} {1},", y.Count, y.Name))); if (sr.Length == 0) - return String.Format("{0}\n", "No Change Due."); // not part of the requirements, yet exact change is a viable value. + return String.Format("{0}\n", "No Change Due"); // not part of the requirements, yet exact change is a viable value. else return String.Format("{0}\n", sr.ToString().Trim(',')); } diff --git a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs index 198e6eaf..48dafb6b 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs @@ -16,6 +16,10 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t int count; decimal change = tender - price; + // the currency.AllDenominations.Min(x => x.Denomination) is to ensure that if there is a currency that + // has a minimum value that is less then that change, the extra will be "dropped" and no infinite loop will occur. + // This itself has the problem that in a large system, these "dropped" percentages could be significant. This would + // be addressed with the business and the development team to determine the best course of action. while (change >= currency.AllDenominations.Min(x => x.Denomination)) { foreach (Money money in currency.AllDenominations) diff --git a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs index 3edecc90..18cbe7e1 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs @@ -10,8 +10,11 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t throw new InvalidCurrencyException("No currency denominations found"); decimal change = tender - price; - // this is to ensure that coinage less than the smallest denomination will not cause an infinite loop. - // the extra "change" is ignored.. this can occur because some currencies do not have decimal value denominations (YEN) + + // the currency.AllDenominations.Min(x => x.Denomination) is to ensure that if there is a currency that + // has a minimum value that is less then that change, the extra will be "dropped" and no infinite loop will occur. + // This itself has the problem that in a large system, these "dropped" percentages could be significant. This would + // be addressed with the business and the development team to determine the best course of action. while (change >= currency.AllDenominations.Min(x => x.Denomination)) { foreach (Money money in currency.AllDenominations) diff --git a/CashRegister/Custom Exceptions/InvalidCurrencyException.cs b/CashRegister/Custom Exceptions/InvalidCurrencyException.cs index a3efb553..4e564e61 100644 --- a/CashRegister/Custom Exceptions/InvalidCurrencyException.cs +++ b/CashRegister/Custom Exceptions/InvalidCurrencyException.cs @@ -3,6 +3,9 @@ namespace CashRegisterConsumer { + /// + /// Custom exception for clarity of code and exception handling + /// [Serializable] public class InvalidCurrencyException : Exception { diff --git a/CashRegister/Custom Exceptions/NotEnoughTenderException.cs b/CashRegister/Custom Exceptions/NotEnoughTenderException.cs index 8772d825..4a04b778 100644 --- a/CashRegister/Custom Exceptions/NotEnoughTenderException.cs +++ b/CashRegister/Custom Exceptions/NotEnoughTenderException.cs @@ -3,6 +3,9 @@ namespace CashRegisterConsumer { + /// + /// Custom exception for clarity of code and exception handling + /// [Serializable] public class NotEnoughTenderException : Exception { diff --git a/CashRegister/Preface.txt b/CashRegister/Preface.txt index 536cf69b..5a9ce9ff 100644 --- a/CashRegister/Preface.txt +++ b/CashRegister/Preface.txt @@ -8,12 +8,13 @@ comments are important, but are best used sparingly. It is more important to use easily, then to trash code with a bunch of comments that with time may not be updated to reflect the actual working of the software. Code itself should be the documentation. -If there are any questions or comments please email me at plwest.axiom@gmail.com and I will respond at the earliest possible time. I appreciate -your evaluation and giving me this oppritunity. Thank you. +If there are any questions or comments please email me at plwest.axiom@gmail.com and I will respond at the earliest possible time. I can also provide +you with photos of my paperwork that includes my initial design notes, revision notes, and just my thoughts as I was working through this project. +I appreciate your evaluation and giving me this oppritunity. Thank you. -Press any button to continue with using the CashRegister Module. (Simulates POS ""Tender"" actions). +Press any button to continue with using the CashRegister Module. (Simulates POS ""Tender"" action). diff --git a/CashRegister/Program.cs b/CashRegister/Program.cs index 8a0d5899..d992646e 100644 --- a/CashRegister/Program.cs +++ b/CashRegister/Program.cs @@ -5,12 +5,14 @@ namespace CashRegisterConsumer { internal class Program { - private static readonly string prefacePath = @"C:\Users\plwes\source\repos\CashRegister\CashRegister\Preface.txt"; + private static readonly string prefaceFilePath = @"C:\Users\plwes\source\repos\CashRegister\CashRegister\Preface.txt"; + private static readonly string usdInputFilePath = @"C:\Users\plwes\source\repos\CashRegister\CashRegister\CashRegisterInputFiles\USD Input File.txt"; + private static readonly string yenInputFilePath = @"C:\Users\plwes\source\repos\CashRegister\CashRegister\CashRegisterInputFiles\YEN Input File.txt"; private static void Main(string[] args) { - using (StreamReader sr = new StreamReader(prefacePath, System.Text.Encoding.UTF8)) + using (StreamReader sr = new StreamReader(prefaceFilePath, System.Text.Encoding.UTF8)) { - Console.SetWindowSize(150, 20); + Console.SetWindowSize(150, 25); Console.WriteLine(sr.ReadToEnd()); } @@ -19,7 +21,15 @@ private static void Main(string[] args) try { CashRegister register = new POSCashRegister(new USD(), new Random3Strategy()); - var result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\CashRegisterInputFiles\USD Input File.txt"); + var result = register.Tender(usdInputFilePath); + Console.WriteLine(result); + + + // for example (change the register to use YEN with a Standard Tender Strategy note that the demoniations are different so + // the same files that use "USD" decimals will be off. There are no decimals in YEN + register.RegisterCurrency(new YEN()); + register.RegisterTenderStrategy(new StandardTenderStrategy()); + result = register.Tender(yenInputFilePath); Console.WriteLine(result); } catch (Exception e) @@ -29,12 +39,6 @@ private static void Main(string[] args) Console.WriteLine(e.InnerException.Message); } - // for example (change the register to use YEN with a Standard Tender Strategy note that the demoniations are different so - // the same files that use "USD" decimals will be off. There are no decimals in YEN - //register.RegisterCurrency(new YEN()); - //register.RegisterTenderStrategy(new StandardTenderStrategy()); - //result = register.Tender(@"C:\Users\plwes\source\repos\CashRegister\CashRegister\Input\YEN Input File.txt"); - //Console.WriteLine(result); Console.ReadKey(); } diff --git a/CashRegisterTests/TenderStrategyTests.cs b/CashRegisterTests/TenderStrategyTests.cs index 0f122bd6..62914ce8 100644 --- a/CashRegisterTests/TenderStrategyTests.cs +++ b/CashRegisterTests/TenderStrategyTests.cs @@ -47,7 +47,7 @@ public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForMultipleC public void TenderStrategyDisplayReturnsProperlyFormattedStringValueForZeroCount() { currencyMock.Setup(p => p.AllDenominations).Returns(new List() { new Bill(1, "dollar", "dollars", 0) }); - string expected = "No Change Due.\n"; + string expected = "No Change Due\n"; TenderStrategy tenderStrategy = new TenderStrategyTestMock(); var actual = tenderStrategy.Display(currencyMock.Object); From ba621261437ecce7669a84cc979fe22eb52028e0 Mon Sep 17 00:00:00 2001 From: Paul L West Date: Mon, 10 Feb 2020 03:12:04 +0900 Subject: [PATCH 12/15] removed invalid gitignore file --- gitignore.git | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 gitignore.git diff --git a/gitignore.git b/gitignore.git deleted file mode 100644 index e69de29b..00000000 From 594fd8ded9dfabb5eb07d4a80dc7ec433b051c7a Mon Sep 17 00:00:00 2001 From: Paul L West Date: Wed, 12 Feb 2020 03:08:58 +0900 Subject: [PATCH 13/15] Updated equation for divisibility by 3 --- .../CashRegister/Strategies/Concrete/Random3Strategy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs index 48dafb6b..665928a8 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs @@ -10,7 +10,7 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t if (currency.AllDenominations.Count == 0) throw new InvalidCurrencyException("No currency denominations found"); - if (Math.Abs(((price * 100) % 10)) == 3) + if (Math.Abs(((price * 100) % 3)) == 0) { Random random = new Random(); int count; From 2286d8acef0cd67b4da792d58586ac5a953ecbb3 Mon Sep 17 00:00:00 2001 From: Paul L West Date: Wed, 12 Feb 2020 03:45:45 +0900 Subject: [PATCH 14/15] Added more test cases for Random3 Discovered equation for div/3 was incorrect Fixed equation --- .../Strategies/Concrete/Random3Strategy.cs | 2 +- CashRegisterTests/Random3StrategyTests.cs | 44 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs index 665928a8..fbbac36a 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs @@ -10,7 +10,7 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t if (currency.AllDenominations.Count == 0) throw new InvalidCurrencyException("No currency denominations found"); - if (Math.Abs(((price * 100) % 3)) == 0) + if (Math.Abs(((price * 100) % 3)) == 0) // updated to correct algorithm { Random random = new Random(); int count; diff --git a/CashRegisterTests/Random3StrategyTests.cs b/CashRegisterTests/Random3StrategyTests.cs index 14867201..ab62bbce 100644 --- a/CashRegisterTests/Random3StrategyTests.cs +++ b/CashRegisterTests/Random3StrategyTests.cs @@ -1,5 +1,6 @@ using CashRegisterConsumer; using Moq; +using System; using System.Collections.Generic; using Xunit; @@ -39,10 +40,7 @@ public void Random3TenderStrategyCalculatesChangeCorrectlyBasedOnPriceAndTenderD } Assert.Equal(expected, actual); - foreach (Money money in mockCurrency.Object.AllDenominations) - { - money.Clear(); - } + ClearCurrency(result); expected = 0.0m; actual = 0.0m; @@ -124,6 +122,29 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnDecimalDen { Assert.Equal(10, money.Count); // price - tender = 10 pennies in change } + + + ClearCurrency(actual); + actual = tenderStrategy.Calculate(mockCurrency.Object, 3.00m, 5.00m); + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) + { + Assert.Equal(200, money.Count); // price - tender = 200 pennies in change + } + + ClearCurrency(actual); + actual = tenderStrategy.Calculate(mockCurrency.Object, 6.00m, 7.00m); + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) + { + Assert.Equal(100, money.Count); // price - tender = 100 pennies in change + } + } + + private void ClearCurrency(ICurrency currency) + { + foreach (Money money in currency.AllDenominations) + { + money.Clear(); + } } [Fact] @@ -137,6 +158,20 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimal { Assert.Equal(4, money.Count); // price - tender = 4 quarters in change } + + ClearCurrency(actual); + actual = tenderStrategy.Calculate(mockCurrency.Object, 3.00m, 5.00m); + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) + { + Assert.Equal(8, money.Count); // price - tender = 8 quarters in change + } + + ClearCurrency(actual); + actual = tenderStrategy.Calculate(mockCurrency.Object, 6.00m, 7.00m); + foreach (Money money in actual.AllDenominations) // mockCurrency.Object.AllDenominations) + { + Assert.Equal(4, money.Count); // price - tender = 4 quarters in change + } } [Fact] @@ -193,6 +228,7 @@ public void Random3TenderStrategySubtractsCorrectlyFromThePriceBasedOnOddDecimal } } + #region Exception Testing [Fact] From 777edbb7c1252a4b894ded56159b72e119255385 Mon Sep 17 00:00:00 2001 From: Paul L West Date: Wed, 12 Feb 2020 03:55:31 +0900 Subject: [PATCH 15/15] Moved "standard" calculation to base class to remove coupling of inherited classes. --- .../Strategies/Abstract/TenderStrategy.cs | 25 ++++++++++++++++++- .../Strategies/Concrete/Random3Strategy.cs | 3 +-- .../Concrete/StandardTenderStrategy.cs | 22 +--------------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs index 81028276..939e11d7 100644 --- a/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Abstract/TenderStrategy.cs @@ -29,6 +29,29 @@ public virtual string Display(ICurrency currency) return String.Format("{0}\n", sr.ToString().Trim(',')); } - public abstract ICurrency Calculate(ICurrency currency, decimal price, decimal tender); + public virtual ICurrency Calculate(ICurrency currency, decimal price, decimal tender) + { + if (currency.AllDenominations.Count == 0) + throw new InvalidCurrencyException("No currency denominations found"); + + decimal change = tender - price; + + // the currency.AllDenominations.Min(x => x.Denomination) is to ensure that if there is a currency that + // has a minimum value that is less then that change, the extra will be "dropped" and no infinite loop will occur. + // This itself has the problem that in a large system, these "dropped" percentages could be significant. This would + // be addressed with the business and the development team to determine the best course of action. + while (change >= currency.AllDenominations.Min(x => x.Denomination)) + { + foreach (Money money in currency.AllDenominations) + { + while (change >= money.Denomination) + { + money.Add(1); + change -= money.Denomination; + } + } + } + return currency; + } } } \ No newline at end of file diff --git a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs index fbbac36a..0161a7ea 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/Random3Strategy.cs @@ -36,8 +36,7 @@ public override ICurrency Calculate(ICurrency currency, decimal price, decimal t } else { - // when not using the "random", we can just use the standard strategy - return new StandardTenderStrategy().Calculate(currency, price, tender); + return base.Calculate(currency, price, tender); } } } diff --git a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs index 18cbe7e1..2dc77065 100644 --- a/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs +++ b/CashRegister/CashRegister/Strategies/Concrete/StandardTenderStrategy.cs @@ -6,27 +6,7 @@ public class StandardTenderStrategy : TenderStrategy { public override ICurrency Calculate(ICurrency currency, decimal price, decimal tender) { - if (currency.AllDenominations.Count == 0) - throw new InvalidCurrencyException("No currency denominations found"); - - decimal change = tender - price; - - // the currency.AllDenominations.Min(x => x.Denomination) is to ensure that if there is a currency that - // has a minimum value that is less then that change, the extra will be "dropped" and no infinite loop will occur. - // This itself has the problem that in a large system, these "dropped" percentages could be significant. This would - // be addressed with the business and the development team to determine the best course of action. - while (change >= currency.AllDenominations.Min(x => x.Denomination)) - { - foreach (Money money in currency.AllDenominations) - { - while (change >= money.Denomination) - { - money.Add(1); - change -= money.Denomination; - } - } - } - return currency; + return base.Calculate(currency, price, tender); } } } \ No newline at end of file