diff --git a/TechTest/AnyCompany.IntegrationTests/AnyCompany.IntegrationTests.csproj b/TechTest/AnyCompany.IntegrationTests/AnyCompany.IntegrationTests.csproj
new file mode 100644
index 0000000..2bd951e
--- /dev/null
+++ b/TechTest/AnyCompany.IntegrationTests/AnyCompany.IntegrationTests.csproj
@@ -0,0 +1,79 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {06DF4A00-EA47-467D-860E-DD0F949ABDC3}
+ Library
+ Properties
+ AnyCompany.IntegrationTests
+ AnyCompany.IntegrationTests
+ v4.7.2
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {c7e15594-7d8f-4c18-9dd7-14f3fbb1572d}
+ AnyCompany
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany.IntegrationTests/CreateSeededDb.Sql b/TechTest/AnyCompany.IntegrationTests/CreateSeededDb.Sql
new file mode 100644
index 0000000..0e743ff
--- /dev/null
+++ b/TechTest/AnyCompany.IntegrationTests/CreateSeededDb.Sql
@@ -0,0 +1,47 @@
+CREATE DATABASE [Customers]
+GO
+
+CREATE TABLE [Customers].[dbo].[Customer](
+ [CustomerId] [int] NOT NULL,
+ [Country] [nvarchar](3) NOT NULL,
+ [DateOfBirth] [date] NOT NULL,
+ [Name] [nvarchar](50) NOT NULL,
+ CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
+(
+ [CustomerId] ASC
+)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
+) ON [PRIMARY]
+GO
+
+INSERT INTO [Customers].[dbo].[Customer]
+([CustomerId],[Country],[DateOfBirth],[Name])
+VALUES
+(1, 'ZA', '1980-04-16', 'bob'),
+(2, 'ZA', '1991-09-23', 'jill'),
+(3, 'UK', '1985-03-01', 'sarah');
+
+
+CREATE DATABASE [Orders]
+GO
+
+CREATE TABLE [Orders].[dbo].[Orders](
+ [OrderId] [int] NOT NULL,
+ [CustomerId] [int] NOT NULL,
+ [Amount] [float] NOT NULL,
+ [VAT] [float] NOT NULL,
+ CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
+(
+ [OrderId] ASC
+)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
+) ON [PRIMARY]
+GO
+
+INSERT INTO [Orders].[dbo].[Orders]
+([OrderId] ,[CustomerId] ,[Amount] ,[VAT])
+VALUES
+(1, 1, 50, 0),
+(2, 1, 60, 0),
+(3, 2, 70, 0),
+(4, 2, 80, 0),
+(5, 3, 90, 0.2),
+(6, 4, 100, 0.2);
\ No newline at end of file
diff --git a/TechTest/AnyCompany.IntegrationTests/CustomerServiceIntegrationTests.cs b/TechTest/AnyCompany.IntegrationTests/CustomerServiceIntegrationTests.cs
new file mode 100644
index 0000000..a651404
--- /dev/null
+++ b/TechTest/AnyCompany.IntegrationTests/CustomerServiceIntegrationTests.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AnyCompany.IntegrationTests
+{
+ [TestClass]
+ public class CustomerServiceIntegrationTests
+ {
+ private ICustomerService _customerService;
+
+ public CustomerServiceIntegrationTests()
+ {
+ Config config = new Config();
+
+ var customerRepository = new CustomerRepositoryProxy(config.CustomerDbConnectionString);
+
+ var orderRepository = new OrderRepository(config.OrdersDbConnectionString);
+
+ _customerService = new CustomerService(orderRepository, customerRepository);
+ }
+
+ [TestMethod]
+ public void GetAllCustomersWithOrders_Successfully()
+ {
+ // Arrange
+
+ // Act
+ var customers = _customerService.GetAllCustomersWithOrders();
+
+ // Assert
+ Assert.IsTrue(customers.Count() > 0);
+ foreach (var customer in customers)
+ {
+ Assert.IsTrue(customer.Orders.Count() > 0);
+ }
+ }
+
+ }
+}
diff --git a/TechTest/AnyCompany.IntegrationTests/OrderServiceIntegrationTests.cs b/TechTest/AnyCompany.IntegrationTests/OrderServiceIntegrationTests.cs
new file mode 100644
index 0000000..9c3c5b7
--- /dev/null
+++ b/TechTest/AnyCompany.IntegrationTests/OrderServiceIntegrationTests.cs
@@ -0,0 +1,41 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AnyCompany.IntegrationTests
+{
+ [TestClass]
+ public class OrderServiceIntegrationTests
+ {
+ private IOrderService _orderService;
+
+ public OrderServiceIntegrationTests()
+ {
+ Config config = new Config();
+
+ var customerRepository = new CustomerRepositoryProxy(config.CustomerDbConnectionString);
+
+ var orderRepository = new OrderRepository(config.OrdersDbConnectionString);
+
+ _orderService = new OrderService(orderRepository, customerRepository);
+ }
+
+ [TestMethod]
+ public void PlaceOrder_Successfully()
+ {
+ // Arrange
+ Order newOrder = new Order()
+ {
+ CustomerId = 1,
+ OrderId = 100,
+ Amount = 100,
+ VAT = 100
+ };
+
+ // Act
+ var isOrderPlaced = _orderService.PlaceOrder(newOrder, 1);
+
+ // Assert
+ Assert.AreEqual(true, isOrderPlaced);
+ }
+ }
+}
diff --git a/TechTest/AnyCompany.IntegrationTests/Properties/AssemblyInfo.cs b/TechTest/AnyCompany.IntegrationTests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..4f0472e
--- /dev/null
+++ b/TechTest/AnyCompany.IntegrationTests/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("AnyCompany.IntegrationTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AnyCompany.IntegrationTests")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("06df4a00-ea47-467d-860e-dd0f949abdc3")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TechTest/AnyCompany.IntegrationTests/Readme.txt b/TechTest/AnyCompany.IntegrationTests/Readme.txt
new file mode 100644
index 0000000..1ffcdd2
--- /dev/null
+++ b/TechTest/AnyCompany.IntegrationTests/Readme.txt
@@ -0,0 +1,13 @@
+
+
+====== Sql Container ======
+docker run -e ACCEPT_EULA=Y -e SA_PASSWORD=zaq1ZAQ! -p 1434:1433 -d --name sqlserver --network testnet --net-alias ss mcr.microsoft.com/mssql/server:2017-CU8-ubuntu
+
+Connect to sql db and Run CreateSeededDb.Sql
+server name: 127.0.0.1,1434
+login: sa
+password: zaq1ZAQ!
+
+The above steps need to be scripted
+Docker compose can start up db, migrations, SUT
+So that everything is in correct state for integration tests
\ No newline at end of file
diff --git a/TechTest/AnyCompany.IntegrationTests/packages.config b/TechTest/AnyCompany.IntegrationTests/packages.config
new file mode 100644
index 0000000..0d63e3f
--- /dev/null
+++ b/TechTest/AnyCompany.IntegrationTests/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj b/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj
deleted file mode 100644
index b537fc2..0000000
--- a/TechTest/AnyCompany.Tests/AnyCompany.Tests.csproj
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- cd5d577e-bdc9-4dfc-ac6a-b1da474995f3
- Library
- Properties
- AnyCompany.Tests
- AnyCompany.Tests
- v4.6.1
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/TechTest/AnyCompany.Tests/Class1.cs b/TechTest/AnyCompany.Tests/Class1.cs
deleted file mode 100644
index 5957505..0000000
--- a/TechTest/AnyCompany.Tests/Class1.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace AnyCompany.Tests
-{
- public class Class1
- {
- }
-}
diff --git a/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs b/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs
deleted file mode 100644
index 726eefa..0000000
--- a/TechTest/AnyCompany.Tests/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("AnyCompany.Tests")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Investec Bank")]
-[assembly: AssemblyProduct("AnyCompany.Tests")]
-[assembly: AssemblyCopyright("Copyright © Investec Bank 2018")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("cd5d577e-bdc9-4dfc-ac6a-b1da474995f3")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TechTest/AnyCompany.UnitTest/AnyCompany.UnitTest.csproj b/TechTest/AnyCompany.UnitTest/AnyCompany.UnitTest.csproj
new file mode 100644
index 0000000..2d36aad
--- /dev/null
+++ b/TechTest/AnyCompany.UnitTest/AnyCompany.UnitTest.csproj
@@ -0,0 +1,75 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {EDE25C74-5461-46EA-8FB0-A53F4AE196C2}
+ Library
+ Properties
+ AnyCompany.UnitTest
+ AnyCompany.UnitTest
+ v4.7.2
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 15.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
+
+
+ ..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {c7e15594-7d8f-4c18-9dd7-14f3fbb1572d}
+ AnyCompany
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany.UnitTest/CustomerServiceTests.cs b/TechTest/AnyCompany.UnitTest/CustomerServiceTests.cs
new file mode 100644
index 0000000..ca60e30
--- /dev/null
+++ b/TechTest/AnyCompany.UnitTest/CustomerServiceTests.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AnyCompany.UnitTest
+{
+ [TestClass]
+ public class CustomerServiceTests
+ {
+ private OrderRepositoryMock _orderRepository;
+ private CustomerRepositoryMock _customerRepository;
+ private ICustomerService _customerService;
+
+ public CustomerServiceTests()
+ {
+ var customerId1 = 1;
+ var customerId2 = 2;
+
+ _customerRepository = new CustomerRepositoryMock
+ {
+ Customers = new List()
+ {
+ new Customer()
+ {
+ CustomerId = customerId1,
+ Country = "ZA",
+ Name = "bob",
+ DateOfBirth = new DateTime(1980, 01, 06)
+ },
+ new Customer()
+ {
+ CustomerId = customerId2,
+ Country = "uk",
+ Name = "jill",
+ DateOfBirth = new DateTime(1980, 01, 06)
+ }
+ }
+ };
+
+ _orderRepository = new OrderRepositoryMock
+ {
+ Orders = new List()
+ {
+ new Order()
+ {
+ CustomerId = customerId1,
+ OrderId = 1,
+ Amount = 110,
+ VAT = 0
+ },
+ new Order()
+ {
+ CustomerId = customerId1,
+ OrderId = 2,
+ Amount = 100,
+ VAT = 0
+ },
+ new Order()
+ {
+ CustomerId = customerId1,
+ OrderId = 3,
+ Amount = 90,
+ VAT = 0.2
+ },
+ new Order()
+ {
+ CustomerId = customerId1,
+ OrderId = 4,
+ Amount = 80,
+ VAT = 0.2
+ },
+ }
+ };
+
+ _customerService = new CustomerService(_orderRepository, _customerRepository);
+ }
+
+ [TestMethod]
+ public void GetAllCustomersWithOrders_Successfully()
+ {
+ // Arrange
+
+ // Act
+ var customers = _customerService.GetAllCustomersWithOrders();
+
+ // Assert
+ Assert.AreEqual(_customerRepository.Customers.Count, customers.Count());
+ Assert.AreEqual(_orderRepository.Orders.Count, customers.Sum(x => x.Orders.Count()));
+ }
+
+ [TestMethod]
+ public void When_NoCustomersExist_Return_EmptyList()
+ {
+ // Arrange
+ _customerRepository.Customers = new List();
+
+ // Act
+ var customers = _customerService.GetAllCustomersWithOrders();
+
+ // Assert
+ Assert.AreEqual(0, customers.Count());
+ }
+
+ [TestMethod]
+ public void When_NoOrdersExist_Return_CustomersWith_EmptyOrdersList()
+ {
+ // Arrange
+ _orderRepository.Orders = new List();
+
+ // Act
+ var customers = _customerService.GetAllCustomersWithOrders();
+
+ // Assert
+ Assert.AreEqual(_customerRepository.Customers.Count, customers.Count());
+ Assert.AreEqual(0, customers.Sum(x => x.Orders.Count()));
+ }
+
+ }
+}
diff --git a/TechTest/AnyCompany.UnitTest/OrderServiceTests.cs b/TechTest/AnyCompany.UnitTest/OrderServiceTests.cs
new file mode 100644
index 0000000..4922340
--- /dev/null
+++ b/TechTest/AnyCompany.UnitTest/OrderServiceTests.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace AnyCompany.UnitTest
+{
+ [TestClass]
+ public class OrderServiceTests
+ {
+ private OrderRepositoryMock _orderRepository;
+ private CustomerRepositoryMock _customerRepository;
+ private IOrderService _orderService;
+
+ public OrderServiceTests()
+ {
+ _orderRepository = new OrderRepositoryMock();
+ _customerRepository = new CustomerRepositoryMock();
+ _orderService = new OrderService(_orderRepository, _customerRepository);
+ }
+
+ [TestMethod]
+ public void PlaceOrder_Successfully()
+ {
+ // Arrange
+ Customer customer = new Customer()
+ {
+ CustomerId = 1,
+ Country = "ZA",
+ Name = "bob",
+ DateOfBirth = new DateTime(1980, 01, 06)
+ };
+ _customerRepository.Customers = new List()
+ {
+ customer
+ };
+
+ Order newOrder = new Order()
+ {
+ CustomerId = customer.CustomerId,
+ OrderId = 1,
+ Amount = 100,
+ VAT = 100
+ };
+
+ // Act
+ var isOrderPlaced = _orderService.PlaceOrder(newOrder, customer.CustomerId);
+
+ // Assert
+ Assert.AreEqual(true, isOrderPlaced);
+
+ var savedOrder = _orderRepository.Orders.SingleOrDefault(x => x.CustomerId == newOrder.CustomerId && x.OrderId == newOrder.OrderId);
+ Assert.AreEqual(newOrder.Amount, savedOrder.Amount);
+ Assert.AreEqual(0, savedOrder.VAT);
+ }
+
+ [TestMethod]
+ public void PlaceOrder_Successfully_UkCustomer()
+ {
+ // Arrange
+ Customer customer = new Customer()
+ {
+ CustomerId = 1,
+ Country = "UK",
+ Name = "bob",
+ DateOfBirth = new DateTime(1980, 01, 06)
+ };
+ _customerRepository.Customers = new List()
+ {
+ customer
+ };
+
+ Order newOrder = new Order()
+ {
+ CustomerId = customer.CustomerId,
+ OrderId = 1,
+ Amount = 100,
+ VAT = 100
+ };
+
+ // Act
+ var isOrderPlaced = _orderService.PlaceOrder(newOrder, customer.CustomerId);
+
+ // Assert
+ Assert.AreEqual(true, isOrderPlaced);
+
+ var savedOrder = _orderRepository.Orders.SingleOrDefault(x => x.CustomerId == newOrder.CustomerId && x.OrderId == newOrder.OrderId);
+ Assert.AreEqual(newOrder.Amount, savedOrder.Amount);
+ Assert.AreEqual(0.2d, savedOrder.VAT);
+ }
+
+ [TestMethod]
+ public void When_OrderAmount_Is0_Return_False()
+ {
+ // Arrange
+ Customer customer = new Customer()
+ {
+ CustomerId = 1,
+ Country = "UK",
+ Name = "bob",
+ DateOfBirth = new DateTime(1980, 01, 06)
+ };
+ _customerRepository.Customers = new List()
+ {
+ customer
+ };
+
+ Order newOrder = new Order()
+ {
+ CustomerId = customer.CustomerId,
+ OrderId = 1,
+ Amount = 0,
+ VAT = 100
+ };
+
+ // Act
+ var isOrderPlaced = _orderService.PlaceOrder(newOrder, customer.CustomerId);
+
+ // Assert
+ Assert.AreEqual(false, isOrderPlaced);
+
+ var savedOrder = _orderRepository.Orders.SingleOrDefault(x => x.CustomerId == newOrder.CustomerId && x.OrderId == newOrder.OrderId);
+ Assert.IsNull(savedOrder);
+ }
+
+ [TestMethod]
+ public void When_Order_IsNull_Return_False()
+ {
+ // Arrange
+ Customer customer = new Customer()
+ {
+ CustomerId = 1,
+ Country = "UK",
+ Name = "bob",
+ DateOfBirth = new DateTime(1980, 01, 06)
+ };
+ _customerRepository.Customers = new List()
+ {
+ customer
+ };
+
+ // Act
+ var isOrderPlaced = _orderService.PlaceOrder(null, customer.CustomerId);
+
+ // Assert
+ Assert.AreEqual(false, isOrderPlaced);
+ }
+
+ [TestMethod]
+ public void When_Customer_NotFound_Return_False()
+ {
+ // Arrange
+ _customerRepository.Customers = new List();
+
+ Order newOrder = new Order()
+ {
+ CustomerId = 1,
+ OrderId = 1,
+ Amount = 100,
+ VAT = 100
+ };
+
+ // Act
+ var isOrderPlaced = _orderService.PlaceOrder(newOrder, newOrder.CustomerId);
+
+ // Assert
+ Assert.AreEqual(false, isOrderPlaced);
+
+ var savedOrder = _orderRepository.Orders.SingleOrDefault(x => x.CustomerId == newOrder.CustomerId && x.OrderId == newOrder.OrderId);
+ Assert.IsNull(savedOrder);
+ }
+ }
+}
diff --git a/TechTest/AnyCompany.UnitTest/Properties/AssemblyInfo.cs b/TechTest/AnyCompany.UnitTest/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..a3e101c
--- /dev/null
+++ b/TechTest/AnyCompany.UnitTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("AnyCompany.UnitTest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AnyCompany.UnitTest")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("ede25c74-5461-46ea-8fb0-a53f4ae196c2")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TechTest/AnyCompany.UnitTest/packages.config b/TechTest/AnyCompany.UnitTest/packages.config
new file mode 100644
index 0000000..0d63e3f
--- /dev/null
+++ b/TechTest/AnyCompany.UnitTest/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TechTest/AnyCompany/AnyCompany.csproj b/TechTest/AnyCompany/AnyCompany.csproj
index 5b0498d..384599f 100644
--- a/TechTest/AnyCompany/AnyCompany.csproj
+++ b/TechTest/AnyCompany/AnyCompany.csproj
@@ -40,11 +40,20 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TechTest/AnyCompany/Config.cs b/TechTest/AnyCompany/Config.cs
new file mode 100644
index 0000000..dc8564c
--- /dev/null
+++ b/TechTest/AnyCompany/Config.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AnyCompany
+{
+ public class Config
+ {
+ public string OrdersDbConnectionString { get; set; } = @"Server=(localdb)\mssqllocaldb;Database=Orders;Integrated Security=true";
+ public string CustomerDbConnectionString { get; set; } = @"Server=(localdb)\mssqllocaldb;Database=Customers;Integrated Security=true";
+ }
+}
diff --git a/TechTest/AnyCompany/CustomerRepository.cs b/TechTest/AnyCompany/CustomerRepository.cs
deleted file mode 100644
index e3de9b7..0000000
--- a/TechTest/AnyCompany/CustomerRepository.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using System.Data.SqlClient;
-
-namespace AnyCompany
-{
- public static class CustomerRepository
- {
- private static string ConnectionString = @"Data Source=(local);Database=Customers;User Id=admin;Password=password;";
-
- public static Customer Load(int customerId)
- {
- Customer customer = new Customer();
-
- SqlConnection connection = new SqlConnection(ConnectionString);
- connection.Open();
-
- SqlCommand command = new SqlCommand("SELECT * FROM Customer WHERE CustomerId = " + customerId,
- connection);
- var reader = command.ExecuteReader();
-
- while (reader.Read())
- {
- customer.Name = reader["Name"].ToString();
- customer.DateOfBirth = DateTime.Parse(reader["DateOfBirth"].ToString());
- customer.Country = reader["Country"].ToString();
- }
-
- connection.Close();
-
- return customer;
- }
- }
-}
diff --git a/TechTest/AnyCompany/Infrastructure/CustomerRepository.cs b/TechTest/AnyCompany/Infrastructure/CustomerRepository.cs
new file mode 100644
index 0000000..beb673d
--- /dev/null
+++ b/TechTest/AnyCompany/Infrastructure/CustomerRepository.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Data.SqlClient;
+
+namespace AnyCompany
+{
+ public static class CustomerRepository
+ {
+ public static string ConnectionString;
+
+ public static Customer Load(int customerId)
+ {
+ Customer customer = new Customer();
+
+ using (SqlConnection connection = new SqlConnection(ConnectionString))
+ using (SqlCommand command = new SqlCommand("SELECT * FROM Customer WHERE CustomerId = @CustomerId", connection))
+ {
+ connection.Open();
+ command.Parameters.AddWithValue("@CustomerId", customerId);
+
+ using (var reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ customer = ExtractCustomer(reader);
+ }
+ }
+
+ connection.Close();
+ }
+
+ return customer;
+ }
+
+ public static IEnumerable LoadAll()
+ {
+ List customers = new List();
+
+ using (SqlConnection connection = new SqlConnection(ConnectionString))
+ using (SqlCommand command = new SqlCommand("SELECT * FROM Customer", connection))
+ {
+ connection.Open();
+ using (var reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ var customer = ExtractCustomer(reader);
+ customers.Add(customer);
+ }
+ }
+ connection.Close();
+ }
+ return customers;
+ }
+
+ private static Customer ExtractCustomer(SqlDataReader reader)
+ {
+ Customer customer = new Customer
+ {
+ CustomerId = (int)reader["CustomerId"],
+ Name = reader["Name"].ToString(),
+ DateOfBirth = DateTime.Parse(reader["DateOfBirth"].ToString()),
+ Country = reader["Country"].ToString()
+ };
+
+ return customer;
+ }
+ }
+}
diff --git a/TechTest/AnyCompany/Infrastructure/CustomerRepositoryProxy.cs b/TechTest/AnyCompany/Infrastructure/CustomerRepositoryProxy.cs
new file mode 100644
index 0000000..2b50714
--- /dev/null
+++ b/TechTest/AnyCompany/Infrastructure/CustomerRepositoryProxy.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AnyCompany
+{
+ public class CustomerRepositoryProxy : ICustomerRepository
+ {
+
+ public CustomerRepositoryProxy(string connectionString)
+ {
+ CustomerRepository.ConnectionString = connectionString;
+ }
+
+ public Customer Load(int customerId)
+ {
+ return CustomerRepository.Load(customerId);
+ }
+
+ public IEnumerable LoadAll()
+ {
+ return CustomerRepository.LoadAll();
+ }
+ }
+}
diff --git a/TechTest/AnyCompany/Infrastructure/Interfaces/ICustomerRepository.cs b/TechTest/AnyCompany/Infrastructure/Interfaces/ICustomerRepository.cs
new file mode 100644
index 0000000..7cdc5ce
--- /dev/null
+++ b/TechTest/AnyCompany/Infrastructure/Interfaces/ICustomerRepository.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace AnyCompany
+{
+ public interface ICustomerRepository
+ {
+ Customer Load(int customerId);
+ IEnumerable LoadAll();
+ }
+}
\ No newline at end of file
diff --git a/TechTest/AnyCompany/Infrastructure/Interfaces/IOrderRepository.cs b/TechTest/AnyCompany/Infrastructure/Interfaces/IOrderRepository.cs
new file mode 100644
index 0000000..c1ae19e
--- /dev/null
+++ b/TechTest/AnyCompany/Infrastructure/Interfaces/IOrderRepository.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace AnyCompany
+{
+ public interface IOrderRepository
+ {
+ void Add(Order order);
+ Dictionary> LoadOrdersForCustomers(IEnumerable customerIds);
+ }
+}
\ No newline at end of file
diff --git a/TechTest/AnyCompany/Infrastructure/Mocks/CustomerRepositoryMock.cs b/TechTest/AnyCompany/Infrastructure/Mocks/CustomerRepositoryMock.cs
new file mode 100644
index 0000000..27b32ed
--- /dev/null
+++ b/TechTest/AnyCompany/Infrastructure/Mocks/CustomerRepositoryMock.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AnyCompany
+{
+ public class CustomerRepositoryMock : ICustomerRepository
+ {
+ public List Customers;
+
+ public CustomerRepositoryMock()
+ {
+ Customers = new List();
+ }
+
+ public Customer Load(int customerId)
+ {
+ return Customers.SingleOrDefault(x => x.CustomerId == customerId);
+ }
+
+ public IEnumerable LoadAll()
+ {
+ return Customers;
+ }
+ }
+}
diff --git a/TechTest/AnyCompany/Infrastructure/Mocks/OrderRepositoryMock.cs b/TechTest/AnyCompany/Infrastructure/Mocks/OrderRepositoryMock.cs
new file mode 100644
index 0000000..c473124
--- /dev/null
+++ b/TechTest/AnyCompany/Infrastructure/Mocks/OrderRepositoryMock.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Linq;
+
+namespace AnyCompany
+{
+ public class OrderRepositoryMock : IOrderRepository
+ {
+ public List Orders;
+
+ public OrderRepositoryMock()
+ {
+ Orders = new List();
+ }
+
+ public void Add(Order order)
+ {
+ Orders.Add(order);
+ }
+
+ public Dictionary> LoadOrdersForCustomers(IEnumerable customerIds)
+ {
+ Dictionary> dic = new Dictionary>();
+ foreach (var customerId in customerIds)
+ {
+ var orders = Orders.Where(x => x.CustomerId == customerId).ToList();
+ dic.Add(customerId, orders);
+ }
+
+ return dic;
+ }
+
+ }
+}
diff --git a/TechTest/AnyCompany/Infrastructure/OrderRepository.cs b/TechTest/AnyCompany/Infrastructure/OrderRepository.cs
new file mode 100644
index 0000000..86f0c41
--- /dev/null
+++ b/TechTest/AnyCompany/Infrastructure/OrderRepository.cs
@@ -0,0 +1,86 @@
+using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Linq;
+
+namespace AnyCompany
+{
+ public class OrderRepository : IOrderRepository
+ {
+ private static string ConnectionString;
+
+ public OrderRepository(string connectionString)
+ {
+ ConnectionString = connectionString;
+ }
+
+ // Changed name from Save to Add
+ // Because the word "Save" infers it may be an idempotent opertaion like an "upsert"
+ // the word "Add" is clearly tells what the method does
+ // Could have used "Insert" as well
+ public void Add(Order order)
+ {
+ using (SqlConnection connection = new SqlConnection(ConnectionString))
+ using (SqlCommand command = new SqlCommand("INSERT INTO Orders ([OrderId],[CustomerId],[Amount],[VAT]) VALUES (@OrderId, @CustomerId, @Amount, @VAT)", connection))
+ {
+ command.Parameters.AddWithValue("@OrderId", order.OrderId);
+ command.Parameters.AddWithValue("@CustomerId", order.CustomerId);
+ command.Parameters.AddWithValue("@Amount", order.Amount);
+ command.Parameters.AddWithValue("@VAT", order.VAT);
+
+ connection.Open();
+
+ command.ExecuteNonQuery();
+
+ connection.Close();
+ }
+ }
+
+ public Dictionary> LoadOrdersForCustomers(IEnumerable customerIds)
+ {
+ Dictionary> orders = new Dictionary>();
+ string customerIdsParam = string.Join("','", customerIds);
+
+ using (SqlConnection connection = new SqlConnection(ConnectionString))
+ using (SqlCommand command = new SqlCommand($"SELECT * FROM Orders WHERE CustomerId IN ('{customerIdsParam}')", connection))
+ {
+ connection.Open();
+
+ using (var reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ var order = ExtractOrder(reader);
+ if (orders.ContainsKey(order.CustomerId))
+ {
+ orders[order.CustomerId].Add(order);
+ }
+ else
+ {
+ orders.Add(order.CustomerId, new List()
+ {
+ order
+ });
+ }
+ }
+ }
+ connection.Close();
+ }
+
+ return orders;
+ }
+
+ private Order ExtractOrder(SqlDataReader reader)
+ {
+ Order order = new Order
+ {
+ OrderId = (int)reader["OrderId"],
+ CustomerId = (int)reader["CustomerId"],
+ Amount = (double)reader["Amount"],
+ VAT = (double)reader["VAT"]
+ };
+
+ return order;
+ }
+
+ }
+}
diff --git a/TechTest/AnyCompany/Customer.cs b/TechTest/AnyCompany/Models/Customer.cs
similarity index 60%
rename from TechTest/AnyCompany/Customer.cs
rename to TechTest/AnyCompany/Models/Customer.cs
index aa994b6..8eb4c50 100644
--- a/TechTest/AnyCompany/Customer.cs
+++ b/TechTest/AnyCompany/Models/Customer.cs
@@ -1,13 +1,19 @@
using System;
+using System.Collections.Generic;
namespace AnyCompany
{
public class Customer
{
+ public int CustomerId { get; set; }
+
public string Country { get; set; }
public DateTime DateOfBirth { get; set; }
public string Name { get; set; }
+
+ public virtual IEnumerable Orders { get; set; }
+
}
}
diff --git a/TechTest/AnyCompany/Order.cs b/TechTest/AnyCompany/Models/Order.cs
similarity index 80%
rename from TechTest/AnyCompany/Order.cs
rename to TechTest/AnyCompany/Models/Order.cs
index fec8e7b..32d68e1 100644
--- a/TechTest/AnyCompany/Order.cs
+++ b/TechTest/AnyCompany/Models/Order.cs
@@ -3,6 +3,7 @@
public class Order
{
public int OrderId { get; set; }
+ public int CustomerId { get; set; }
public double Amount { get; set; }
public double VAT { get; set; }
}
diff --git a/TechTest/AnyCompany/OrderRepository.cs b/TechTest/AnyCompany/OrderRepository.cs
deleted file mode 100644
index 3229885..0000000
--- a/TechTest/AnyCompany/OrderRepository.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Data.SqlClient;
-
-namespace AnyCompany
-{
- internal class OrderRepository
- {
- private static string ConnectionString = @"Data Source=(local);Database=Orders;User Id=admin;Password=password;";
-
- public void Save(Order order)
- {
- SqlConnection connection = new SqlConnection(ConnectionString);
- connection.Open();
-
- SqlCommand command = new SqlCommand("INSERT INTO Orders VALUES (@OrderId, @Amount, @VAT)", connection);
-
- command.Parameters.AddWithValue("@OrderId", order.OrderId);
- command.Parameters.AddWithValue("@Amount", order.Amount);
- command.Parameters.AddWithValue("@VAT", order.VAT);
-
- command.ExecuteNonQuery();
-
- connection.Close();
- }
- }
-}
diff --git a/TechTest/AnyCompany/OrderService.cs b/TechTest/AnyCompany/OrderService.cs
deleted file mode 100644
index ebfb103..0000000
--- a/TechTest/AnyCompany/OrderService.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace AnyCompany
-{
- public class OrderService
- {
- private readonly OrderRepository orderRepository = new OrderRepository();
-
- public bool PlaceOrder(Order order, int customerId)
- {
- Customer customer = CustomerRepository.Load(customerId);
-
- if (order.Amount == 0)
- return false;
-
- if (customer.Country == "UK")
- order.VAT = 0.2d;
- else
- order.VAT = 0;
-
- orderRepository.Save(order);
-
- return true;
- }
- }
-}
diff --git a/TechTest/AnyCompany/Services/CustomerService.cs b/TechTest/AnyCompany/Services/CustomerService.cs
new file mode 100644
index 0000000..ac057df
--- /dev/null
+++ b/TechTest/AnyCompany/Services/CustomerService.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AnyCompany
+{
+ public class CustomerService : ICustomerService
+ {
+ private readonly IOrderRepository _orderRepository;
+ private readonly ICustomerRepository _customerRepository;
+
+ public CustomerService(IOrderRepository orderRepository, ICustomerRepository customerRepository)
+ {
+ _orderRepository = orderRepository;
+ _customerRepository = customerRepository;
+ }
+
+ public IEnumerable GetAllCustomersWithOrders()
+ {
+ var customers = _customerRepository.LoadAll();
+ if (customers == null)
+ {
+ return new List();
+ }
+
+ var ordersDictionary = _orderRepository.LoadOrdersForCustomers(customers.Select(x => x.CustomerId));
+
+ foreach (var customer in customers)
+ {
+ if (ordersDictionary.ContainsKey(customer.CustomerId))
+ {
+ customer.Orders = ordersDictionary[customer.CustomerId];
+ }
+ }
+
+ return customers;
+ }
+
+ }
+}
diff --git a/TechTest/AnyCompany/Services/Interfaces/ICustomerService.cs b/TechTest/AnyCompany/Services/Interfaces/ICustomerService.cs
new file mode 100644
index 0000000..bc8ce55
--- /dev/null
+++ b/TechTest/AnyCompany/Services/Interfaces/ICustomerService.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace AnyCompany
+{
+ public interface ICustomerService
+ {
+ IEnumerable GetAllCustomersWithOrders();
+ }
+}
\ No newline at end of file
diff --git a/TechTest/AnyCompany/Services/Interfaces/IOrderService.cs b/TechTest/AnyCompany/Services/Interfaces/IOrderService.cs
new file mode 100644
index 0000000..87381c9
--- /dev/null
+++ b/TechTest/AnyCompany/Services/Interfaces/IOrderService.cs
@@ -0,0 +1,7 @@
+namespace AnyCompany
+{
+ public interface IOrderService
+ {
+ bool PlaceOrder(Order order, int customerId);
+ }
+}
\ No newline at end of file
diff --git a/TechTest/AnyCompany/Services/OrderService.cs b/TechTest/AnyCompany/Services/OrderService.cs
new file mode 100644
index 0000000..82df1ae
--- /dev/null
+++ b/TechTest/AnyCompany/Services/OrderService.cs
@@ -0,0 +1,36 @@
+namespace AnyCompany
+{
+ public class OrderService : IOrderService
+ {
+ private readonly IOrderRepository _orderRepository;
+ private readonly ICustomerRepository _customerRepository;
+
+ public OrderService(IOrderRepository orderRepository, ICustomerRepository customerRepository)
+ {
+ _orderRepository = orderRepository;
+ _customerRepository = customerRepository;
+ }
+
+ public bool PlaceOrder(Order order, int customerId)
+ {
+ if (order == null || order.Amount == 0)
+ return false;
+
+ Customer customer = _customerRepository.Load(customerId);
+ if (customer == null)
+ {
+ return false;
+ }
+
+ if (customer.Country == "UK")
+ order.VAT = 0.2d;
+ else
+ order.VAT = 0;
+
+ _orderRepository.Add(order);
+
+ return true;
+ }
+
+ }
+}
diff --git a/TechTest/TechTest.sln b/TechTest/TechTest.sln
index 1c1c57a..f4d3b8f 100644
--- a/TechTest/TechTest.sln
+++ b/TechTest/TechTest.sln
@@ -1,17 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27004.2005
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30104.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyCompany", "AnyCompany\AnyCompany.csproj", "{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyCompany.Tests", "AnyCompany.Tests\AnyCompany.Tests.csproj", "{CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B6D3C1BB-2A37-4E17-9EE3-DEF28286E782}"
ProjectSection(SolutionItems) = preProject
Instructions.txt = Instructions.txt
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyCompany.UnitTest", "AnyCompany.UnitTest\AnyCompany.UnitTest.csproj", "{EDE25C74-5461-46EA-8FB0-A53F4AE196C2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnyCompany.IntegrationTests", "AnyCompany.IntegrationTests\AnyCompany.IntegrationTests.csproj", "{06DF4A00-EA47-467D-860E-DD0F949ABDC3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -22,10 +24,14 @@ Global
{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7E15594-7D8F-4C18-9DD7-14F3FBB1572D}.Release|Any CPU.Build.0 = Release|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CD5D577E-BDC9-4DFC-AC6A-B1DA474995F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EDE25C74-5461-46EA-8FB0-A53F4AE196C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EDE25C74-5461-46EA-8FB0-A53F4AE196C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EDE25C74-5461-46EA-8FB0-A53F4AE196C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EDE25C74-5461-46EA-8FB0-A53F4AE196C2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {06DF4A00-EA47-467D-860E-DD0F949ABDC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06DF4A00-EA47-467D-860E-DD0F949ABDC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06DF4A00-EA47-467D-860E-DD0F949ABDC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {06DF4A00-EA47-467D-860E-DD0F949ABDC3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE