diff --git a/Modules/Exercise03/Tests/Feature/Http/Controllers/ProductControllerTest.php b/Modules/Exercise03/Tests/Feature/Http/Controllers/ProductControllerTest.php
new file mode 100644
index 0000000..44caced
--- /dev/null
+++ b/Modules/Exercise03/Tests/Feature/Http/Controllers/ProductControllerTest.php
@@ -0,0 +1,49 @@
+productService = $this->mock(ProductService::class);
+ $this->controller = new ProductController($this->productService);
+ }
+
+ public function test_index_return_view_success()
+ {
+ $products = ['name' => 'name'];
+ $this->productService->shouldReceive('getAllProducts')
+ ->andReturn($products);
+ $response = $this->controller->index();
+
+ $this->assertEquals('exercise03::index', $response->getName());
+ $this->assertEquals(compact('products'), $response->getData());
+ }
+
+ public function test_checkout_return_view_success()
+ {
+ $inputs = [
+ 'total_products' => 1,
+ ];
+
+ $request = CheckoutRequest::create('', 'post', $inputs);
+ $this->productService->shouldReceive('calculateDiscount')
+ ->with($inputs['total_products'])
+ ->andReturn(1);
+ $response = $this->controller->checkout($request);
+
+ $this->assertEquals(['discount' => 1], $response->getOriginalContent());
+ $this->assertEquals(200, $response->status());
+ }
+}
diff --git a/Modules/Exercise03/Tests/Feature/Http/Requests/CheckoutRequestTest.php b/Modules/Exercise03/Tests/Feature/Http/Requests/CheckoutRequestTest.php
new file mode 100644
index 0000000..5fda2c3
--- /dev/null
+++ b/Modules/Exercise03/Tests/Feature/Http/Requests/CheckoutRequestTest.php
@@ -0,0 +1,41 @@
+checkoutRequest = new CheckoutRequest();
+ }
+
+ public function test_validate_success()
+ {
+ $input = [
+ 'total_products' => [
+ 1 => null,
+ 2 => 2,
+ 3 => 3,
+ ],
+ ];
+
+ $validator = Validator::make($input, $this->checkoutRequest->rules());
+ $this->assertTrue($validator->passes());
+ }
+
+ public function test_validate_with_totals_product_error()
+ {
+ $input = ['total_product' => 'fail'];
+ $validator = Validator::make($input, $this->checkoutRequest->rules());
+
+ $this->assertFalse($validator->passes());
+ }
+}
diff --git a/Modules/Exercise03/Tests/Feature/Models/ProductModelTest.php b/Modules/Exercise03/Tests/Feature/Models/ProductModelTest.php
new file mode 100644
index 0000000..36d2bab
--- /dev/null
+++ b/Modules/Exercise03/Tests/Feature/Models/ProductModelTest.php
@@ -0,0 +1,20 @@
+make();
+
+ $this->assertInstanceOf(Product::class, $product);
+ }
+}
diff --git a/Modules/Exercise03/Tests/Unit/Models/ProductTest.php b/Modules/Exercise03/Tests/Unit/Models/ProductTest.php
new file mode 100644
index 0000000..92ada7b
--- /dev/null
+++ b/Modules/Exercise03/Tests/Unit/Models/ProductTest.php
@@ -0,0 +1,31 @@
+ 'name',
+ 'type' => Product::CRAVAT_TYPE,
+ ];
+
+ $product = Product::create($inputs);
+
+ $this->assertEquals($inputs['name'], $product->name);
+ $this->assertEquals($inputs['type'], $product->type);
+ }
+}
diff --git a/Modules/Exercise03/Tests/Unit/Repositories/ProductRepositoryTest.php b/Modules/Exercise03/Tests/Unit/Repositories/ProductRepositoryTest.php
new file mode 100644
index 0000000..afd8821
--- /dev/null
+++ b/Modules/Exercise03/Tests/Unit/Repositories/ProductRepositoryTest.php
@@ -0,0 +1,29 @@
+productModel = new Product();
+ $this->repository = new ProductRepository($this->productModel);
+ }
+
+ public function test_all()
+ {
+ $response = $this->repository->all();
+
+ $this->assertInstanceOf(Collection::class, $response);
+ }
+}
diff --git a/Modules/Exercise03/Tests/Unit/Services/ProductServiceTest.php b/Modules/Exercise03/Tests/Unit/Services/ProductServiceTest.php
new file mode 100644
index 0000000..99f6ce0
--- /dev/null
+++ b/Modules/Exercise03/Tests/Unit/Services/ProductServiceTest.php
@@ -0,0 +1,74 @@
+productRepository = $this->mock(ProductRepository::class);
+ $this->service = new ProductService($this->productRepository);
+
+ }
+
+ public function test_get_all_products()
+ {
+ $this->productRepository->shouldReceive('all')
+ ->andReturn([]);
+ $products = $this->service->getAllProducts();
+
+ $this->assertEquals($products, []);
+ }
+
+ /**
+ * @param $totalProducts
+ * @param $expectedValue
+ * @dataProvider provide_total_products_data
+ * */
+ public function test_calculate_discount_with_valid_data($totalProducts, $expectedValue)
+ {
+ $discount = $this->service->calculateDiscount($totalProducts);
+ $this->assertEquals($expectedValue, $discount);
+ }
+
+ public function provide_total_products_data()
+ {
+ return [
+ [
+ [
+ Product::CRAVAT_TYPE => 1,
+ Product::WHITE_SHIRT_TYPE => 2,
+ Product::OTHER_TYPE => 0,
+ ], ProductService::CRAVAT_WHITE_SHIRT_DISCOUNT
+ ],
+ [
+ [
+ Product::CRAVAT_TYPE => 2,
+ Product::WHITE_SHIRT_TYPE => 3,
+ Product::OTHER_TYPE => 5,
+ ], 12
+ ],
+ ];
+ }
+
+ public function test_calculate_discount_throw_exception()
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->service->calculateDiscount([
+ Product::CRAVAT_TYPE => -1,
+ Product::WHITE_SHIRT_TYPE => -2,
+ Product::OTHER_TYPE => -3,
+ ]);
+ }
+}
diff --git a/Modules/Exercise04/Tests/Feature/Http/Controllers/CalendarControllerTest.php b/Modules/Exercise04/Tests/Feature/Http/Controllers/CalendarControllerTest.php
new file mode 100644
index 0000000..eead686
--- /dev/null
+++ b/Modules/Exercise04/Tests/Feature/Http/Controllers/CalendarControllerTest.php
@@ -0,0 +1,31 @@
+calendarService = $this->mock(CalendarService::class);
+ $this->controller = new CalendarController($this->calendarService);
+ }
+
+ public function test_index()
+ {
+ $this->calendarService->shouldReceive('getDateClass')
+ ->andReturn(CalendarService::COLOR_BLACK);
+ $response = $this->controller->index();
+
+ $this->assertEquals('exercise04::calendar', $response->getName());
+ $this->assertArrayHasKey('calendars', $response->getData());
+ }
+}
diff --git a/Modules/Exercise04/Tests/Unit/Services/CalendarServiceTest.php b/Modules/Exercise04/Tests/Unit/Services/CalendarServiceTest.php
new file mode 100644
index 0000000..dd4b6a0
--- /dev/null
+++ b/Modules/Exercise04/Tests/Unit/Services/CalendarServiceTest.php
@@ -0,0 +1,51 @@
+calendarService = new CalendarService();
+ }
+
+ /**
+ * @dataProvider provider_date_input_data
+ */
+ public function test_get_date_class($date, $expected)
+ {
+ $response = $this->calendarService->getDateClass($date, ['2021-04-30']);
+
+ $this->assertEquals($expected, $response);
+ }
+
+ public function provider_date_input_data()
+ {
+ return [
+ [
+ Carbon::createFromDate(2021, 5, 5),
+ CalendarService::COLOR_BLACK
+ ],
+ [
+ Carbon::createFromDate(2021, 5, 23),
+ CalendarService::COLOR_RED
+ ],
+ [
+ Carbon::createFromDate(2021, 5, 22),
+ CalendarService::COLOR_BLUE
+ ],
+ [
+ Carbon::createFromDate(2021, 4, 30),
+ CalendarService::COLOR_RED
+ ],
+ ];
+ }
+}
diff --git a/Modules/Exercise05/Tests/Feature/Http/Controllers/Exercise05ControllerTest.php b/Modules/Exercise05/Tests/Feature/Http/Controllers/Exercise05ControllerTest.php
new file mode 100644
index 0000000..831be63
--- /dev/null
+++ b/Modules/Exercise05/Tests/Feature/Http/Controllers/Exercise05ControllerTest.php
@@ -0,0 +1,51 @@
+orderService = $this->mock(OrderService::class);
+ $this->controller = new Exercise05Controller($this->orderService);
+ }
+
+ public function test_index_return_view_success()
+ {
+ $response = $this->controller->index();
+
+ $this->assertEquals('exercise05::index', $response->getName());
+ $this->assertArrayHasKey('optionCoupons', $response->getData());
+ $this->assertArrayHasKey('optionReceives', $response->getData());
+ }
+
+ public function test_store_success()
+ {
+ $inputs = [
+ 'price' => 1,
+ 'option_receive' => 1,
+ 'option_coupon' => 1
+ ];
+
+ $request = OrderRequest::create('', 'post', $inputs);
+ $this->orderService->shouldReceive('handleDiscount')
+ ->with($inputs)
+ ->andReturn(1);
+ $response = $this->controller->store($request);
+ $data = $response->getData();
+
+ $this->assertEquals('exercise05::detail', $response->getName());
+ $this->assertEquals(1, $data['resultOrder']);
+ $this->assertEquals($inputs, $data['detailOrder']);
+ }
+}
diff --git a/Modules/Exercise05/Tests/Feature/Http/Requests/OrderRequestTest.php b/Modules/Exercise05/Tests/Feature/Http/Requests/OrderRequestTest.php
new file mode 100644
index 0000000..62c1e2e
--- /dev/null
+++ b/Modules/Exercise05/Tests/Feature/Http/Requests/OrderRequestTest.php
@@ -0,0 +1,71 @@
+request = new OrderRequest();
+ }
+
+ /**
+ * @dataProvider provider_input_fail
+ * @param $input
+ */
+ public function test_validation_fails($input)
+ {
+ $validator = Validator::make($input, $this->request->rules());
+
+ $this->assertTrue($validator->fails());
+ }
+
+ public function provider_input_fail()
+ {
+ return [
+ [
+ [
+ 'price' => '',
+ 'option_receive' => 1,
+ 'option_coupon' => 1
+ ],
+ [
+ 'price' => '!@!#!',
+ 'option_receive' => 1,
+ 'option_coupon' => 1
+ ],
+ [
+ 'price' => 1,
+ 'option_receive' => 0,
+ 'option_coupon' => 1
+ ],
+ [
+ 'price' => 1,
+ 'option_receive' => 1,
+ 'option_coupon' => 0
+ ]
+ ],
+ ];
+ }
+
+ public function test_validation_success()
+ {
+ $request = new OrderRequest();
+ $validator = Validator::make([
+ 'price' => 1,
+ 'option_receive' => config('exercise05.receive_at_store'),
+ 'option_coupon' => config('exercise05.no_coupon')
+ ], $request->rules());
+
+ $this->assertTrue($validator->passes());
+ }
+}
diff --git a/Modules/Exercise05/Tests/Unit/Services/OrderServiceTest.php b/Modules/Exercise05/Tests/Unit/Services/OrderServiceTest.php
new file mode 100644
index 0000000..ac2c753
--- /dev/null
+++ b/Modules/Exercise05/Tests/Unit/Services/OrderServiceTest.php
@@ -0,0 +1,84 @@
+service = new OrderService();
+ }
+
+ /**
+ * @dataProvider provide_detail_order_data
+ * @param $detailOrder
+ * @param $expected
+ */
+ public function test_handle_discount($detailOrder, $expected)
+ {
+ $response = $this->service->handleDiscount($detailOrder);
+
+ $this->assertEquals($expected, $response);
+ }
+
+ public function provide_detail_order_data()
+ {
+ return [
+ [
+ [
+ 'price' => 1000,
+ 'option_receive' => 2,
+ 'option_coupon' => 1
+ ],
+ [
+ 'price' => 800,
+ 'discount_pizza' => null,
+ 'discount_potato' => null
+ ],
+ ],
+ [
+ [
+ 'price' => 10000,
+ 'option_receive' => 2,
+ 'option_coupon' => 2,
+ ],
+ [
+ 'price' => 10000,
+ 'discount_pizza' => null,
+ 'discount_potato' => 'Miễn phí khoai tây',
+ ]
+ ],
+ [
+ [
+ 'price' => 10000,
+ 'option_receive' => 2,
+ 'option_coupon' => 1,
+ ],
+ [
+ 'price' => 8000,
+ 'discount_pizza' => null,
+ 'discount_potato' => 'Miễn phí khoai tây',
+ ]
+ ],
+ [
+ [
+ 'price' => 10000,
+ 'option_receive' => 1,
+ 'option_coupon' => 1,
+ ],
+ [
+ 'price' => 10000,
+ 'discount_pizza' => 'Khuyến mại pizza thứ 2',
+ 'discount_potato' => 'Miễn phí khoai tây',
+ ]
+ ],
+ ];
+ }
+}
diff --git a/Modules/Exercise06/Tests/Feature/Http/Controllers/Exercise06ControllerTest.php b/Modules/Exercise06/Tests/Feature/Http/Controllers/Exercise06ControllerTest.php
new file mode 100644
index 0000000..f55813e
--- /dev/null
+++ b/Modules/Exercise06/Tests/Feature/Http/Controllers/Exercise06ControllerTest.php
@@ -0,0 +1,57 @@
+calculateService = $this->mock(CalculateService::class);
+ $this->controller = new Exercise06Controller($this->calculateService);
+ }
+
+ public function test_index_return_view_success()
+ {
+ $expected = [
+ 'case1' => CalculateService::CASE_1,
+ 'case2' => CalculateService::CASE_2,
+ 'freeTimeForMovie' => CalculateService::FREE_TIME_FOR_MOVIE,
+ ];
+ $response = $this->controller->index();
+
+ $this->assertEquals('exercise06::index', $response->getName());
+ $this->assertEquals($expected, $response->getData());
+ }
+
+ public function test_calculate_return_success()
+ {
+ $inputs = [
+ 'bill' => 1,
+ 'has_watch' => 1
+ ];
+ $request = $this->mock(Exercise06Request::class);
+ $request->shouldReceive('validated')
+ ->once()
+ ->andReturn($inputs);
+
+ $this->calculateService->shouldReceive('calculate')
+ ->with($inputs['bill'], isset($inputs['has_watch']))
+ ->andReturn(1);
+
+ $response = $this->controller->calculate($request);
+
+ $this->assertInstanceOf(RedirectResponse::class, $response);
+ $this->assertEquals(['time' => 1], $response->getSession()->get('result'));
+ }
+}
diff --git a/Modules/Exercise06/Tests/Feature/Http/Requests/Exercise06RequestTest.php b/Modules/Exercise06/Tests/Feature/Http/Requests/Exercise06RequestTest.php
new file mode 100644
index 0000000..a6cdd92
--- /dev/null
+++ b/Modules/Exercise06/Tests/Feature/Http/Requests/Exercise06RequestTest.php
@@ -0,0 +1,60 @@
+request = new Exercise06Request();
+ }
+
+ public function test_validate_success()
+ {
+ $input = [
+ 'bill' => 1,
+ 'has_watch' => false,
+ ];
+
+ $validator = Validator::make($input, $this->request->rules());
+ $this->assertTrue($validator->passes());
+ }
+
+ /**
+ * @dataProvider provider_input
+ * @param $input
+ */
+ public function test_validation_failed($input)
+ {
+ $validator = Validator::make($input, $this->request->rules());
+
+ $this->assertTrue($validator->fails());
+ }
+
+ public function provider_input()
+ {
+ return [
+ [
+ [],
+ [
+ 'has_watch' => true,
+ ],
+ [
+ 'bill' => 1.2,
+ ],
+ [
+ 'bill' => 1200,
+ 'has_watch' => 'test',
+ ],
+ ]
+ ];
+ }
+}
diff --git a/Modules/Exercise06/Tests/Unit/Services/CalculateServiceTest.php b/Modules/Exercise06/Tests/Unit/Services/CalculateServiceTest.php
new file mode 100644
index 0000000..332c750
--- /dev/null
+++ b/Modules/Exercise06/Tests/Unit/Services/CalculateServiceTest.php
@@ -0,0 +1,85 @@
+service = new CalculateService();
+ }
+
+ public function test_calculate_throw_exception()
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->service->calculate(0, false);
+ }
+
+ /**
+ * @param $data
+ * @param $expected
+ * @dataProvider provide_input_data
+ * */
+ public function test_calculate_return_data($data, $expected)
+ {
+ $time = $this->service->calculate($data['bill'], $data['has_watch']);
+
+ $this->assertEquals($expected, $time);
+ }
+
+ public function provide_input_data()
+ {
+ return [
+ [
+ [
+ 'bill' => 1,
+ 'has_watch' => false,
+ ],
+ 'time' => 0
+ ],
+ [
+ [
+ 'bill' => 1,
+ 'has_watch' => true,
+ ],
+ 'time' => 180
+ ],
+ [
+ [
+ 'bill' => 10000,
+ 'has_watch' => false,
+ ],
+ 'time' => 120
+ ],
+ [
+ [
+ 'bill' => 3000,
+ 'has_watch' => false,
+ ],
+ 'time' => 60
+ ],
+ [
+ [
+ 'bill' => 3000,
+ 'has_watch' => true,
+ ],
+ 'time' => 240
+ ],
+ [
+ [
+ 'bill' => 10000,
+ 'has_watch' => true,
+ ],
+ 'time' => 300
+ ],
+ ];
+ }
+}
diff --git a/Modules/Exercise07/Tests/Feature/Http/Controllers/CheckoutControllerTest.php b/Modules/Exercise07/Tests/Feature/Http/Controllers/CheckoutControllerTest.php
new file mode 100644
index 0000000..e54a1b5
--- /dev/null
+++ b/Modules/Exercise07/Tests/Feature/Http/Controllers/CheckoutControllerTest.php
@@ -0,0 +1,54 @@
+checkoutService = $this->mock(CheckoutService::class);
+ $this->controller = new CheckoutController($this->checkoutService);
+ }
+
+ public function test_index()
+ {
+ $response = $this->controller->index();
+
+ $this->assertEquals('exercise07::checkout.index', $response->getName());
+ }
+
+ public function test_store()
+ {
+ $inputs = [
+ 'amount' => 99,
+ ];
+ $expected = ['shipping_fee' => 10];
+
+ $request = CheckoutRequest::create('', 'post', $inputs);
+
+ $this->checkoutService->shouldReceive('calculateShippingFee')
+ ->with($inputs)
+ ->once()
+ ->andReturn($expected);
+
+ $response = $this->controller->store($request);
+
+ $this->assertInstanceOf(RedirectResponse::class, $response);
+ $this->assertEquals($expected, $response->getSession()->get('order'));
+ }
+}
diff --git a/Modules/Exercise07/Tests/Feature/Http/Requests/CheckoutRequestTest.php b/Modules/Exercise07/Tests/Feature/Http/Requests/CheckoutRequestTest.php
new file mode 100644
index 0000000..f4381b4
--- /dev/null
+++ b/Modules/Exercise07/Tests/Feature/Http/Requests/CheckoutRequestTest.php
@@ -0,0 +1,60 @@
+request = new CheckoutRequest();
+ }
+
+ /**
+ * @dataProvider provider_input_fail
+ * @param $input
+ */
+ public function test_validation_fails($input)
+ {
+ $validator = Validator::make($input, $this->request->rules());
+
+ $this->assertTrue($validator->fails());
+ }
+
+ public function provider_input_fail()
+ {
+ return [
+ [
+ [
+ 'amount' => 0,
+ ],
+ [],
+ [
+ 'amount' => 1,2,
+ ],
+ [
+ 'amount' => '',
+ ],
+ [
+ 'amount' => '#@$@#',
+ ],
+ ],
+ ];
+ }
+
+ public function test_validation_success()
+ {
+ $validator = Validator::make([
+ 'amount' => 1,
+ ], $this->request->rules());
+
+ $this->assertTrue($validator->passes());
+ }
+}
diff --git a/Modules/Exercise07/Tests/Unit/Services/CheckoutServiceTest.php b/Modules/Exercise07/Tests/Unit/Services/CheckoutServiceTest.php
new file mode 100644
index 0000000..991d170
--- /dev/null
+++ b/Modules/Exercise07/Tests/Unit/Services/CheckoutServiceTest.php
@@ -0,0 +1,65 @@
+checkoutService = new CheckoutService();
+ }
+
+ /**
+ * @param $order
+ * @param $expected
+ * @dataProvider provide_order_data
+ * */
+ public function test_calculate_shipping_fee($order, $expected)
+ {
+ $response = $this->checkoutService->calculateShippingFee($order);
+
+ $this->assertEquals($expected, $response);
+ }
+
+ public function provide_order_data()
+ {
+ return [
+ [
+ [
+ 'amount' => 10000,
+ ],
+ [
+ 'shipping_fee' => 0,
+ 'amount' => 10000,
+ ]
+ ],
+ [
+ [
+ 'amount' => 1000,
+ ],
+ [
+ 'shipping_fee' => 500,
+ 'amount' => 1000,
+ ]
+ ],
+ [
+ [
+ 'amount' => 1000,
+ 'premium_member' => 1,
+ ],
+ [
+ 'shipping_fee' => 0,
+ 'amount' => 1000,
+ 'premium_member' => 1,
+ ]
+ ],
+ ];
+ }
+}
diff --git a/infection.json b/infection.json
index e93241b..bb88daa 100644
--- a/infection.json
+++ b/infection.json
@@ -1,7 +1,12 @@
{
"source": {
"directories": [
- "Modules/"
+ "Modules/Exercise02/",
+ "Modules/Exercise03/",
+ "Modules/Exercise04/",
+ "Modules/Exercise05/",
+ "Modules/Exercise06/",
+ "Modules/Exercise07/"
],
"excludes": [
"Config",
diff --git a/phpunit.xml b/phpunit.xml
index 77442f0..25d222f 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -6,9 +6,12 @@
>
- ./tests/Unit
- ./tests/Feature
- ./Modules/**/Tests
+ ./Modules/Exercise02/Tests
+ ./Modules/Exercise03/Tests
+ ./Modules/Exercise04/Tests
+ ./Modules/Exercise05/Tests
+ ./Modules/Exercise06/Tests
+ ./Modules/Exercise07/Tests
./Modules/Exercise01/Tests
@@ -43,8 +46,11 @@
- ./app
- ./Modules
+ ./Modules/Exercise03
+ ./Modules/Exercise04
+ ./Modules/Exercise05
+ ./Modules/Exercise06
+ ./Modules/Exercise07
./Modules/**/Config