initial project

This commit is contained in:
2025-11-06 13:41:06 +08:00
commit 1b08727cb2
209 changed files with 31602 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
<?php
namespace Tests\Unit;
use CodeIgniter\Test\CIUnitTestCase;
use App\Services\CalculateService;
use App\Services\PromoService;
class CalculateServiceTest extends CIUnitTestCase
{
protected $calculateService;
protected function setUp(): void
{
parent::setUp();
// Mock PromoService for testing
/** @var PromoService $promoService */
$promoService = $this->createMock(PromoService::class);
$this->calculateService = new CalculateService($promoService);
}
public function testCalculateDeliveryFee()
{
// Test cases for delivery fee calculation
$testCases = [
['distance' => 0, 'expected' => 0.00],
['distance' => 2.5, 'expected' => 5.00], // Below 5 km: RM 5
['distance' => 5.0, 'expected' => 7.00], // 5-6.99 km: RM 7
['distance' => 6.99, 'expected' => 7.00], // 5-6.99 km: RM 7
['distance' => 7.0, 'expected' => 12.00], // 7-12.99 km: RM 12
['distance' => 10.5, 'expected' => 12.00], // 7-12.99 km: RM 12
['distance' => 12.99, 'expected' => 12.00], // 7-12.99 km: RM 12
['distance' => 13.0, 'expected' => 15.00], // 13-20 km: RM 15
['distance' => 16.5, 'expected' => 15.00], // 13-20 km: RM 15
['distance' => 20.0, 'expected' => 15.00], // 13-20 km: RM 15
['distance' => 22.0, 'expected' => 17.00], // Above 20 km: +RM 2 for every 5 km
['distance' => 25.0, 'expected' => 17.00], // Above 20 km: +RM 2 for every 5 km
['distance' => 27.0, 'expected' => 19.00], // Above 20 km: +RM 2 for every 5 km
['distance' => 30.0, 'expected' => 19.00], // Above 20 km: +RM 2 for every 5 km
];
foreach ($testCases as $testCase) {
$result = $this->calculateService->calculateDeliveryFee($testCase['distance']);
$this->assertEquals(
$testCase['expected'],
$result,
"Distance: {$testCase['distance']} km should cost RM {$testCase['expected']}, but got RM {$result}"
);
}
}
public function testCalculateDeliveryFeeWithInvalidInput()
{
// Test invalid inputs
$invalidInputs = [
-5.0, // Negative distance
'invalid', // String
null, // Null
[], // Array
true, // Boolean
];
foreach ($invalidInputs as $invalidInput) {
$result = $this->calculateService->calculateDeliveryFee($invalidInput);
$this->assertEquals(0, $result, "Invalid input '{$invalidInput}' should return 0");
}
}
public function testCalculateDistanceWithInvalidInput()
{
// Test invalid outlet ID
$result = $this->calculateService->calculateDistance('invalid', 3.123, 101.654);
$this->assertEquals(0, $result, 'Invalid outlet ID should return 0');
// Test invalid coordinates
$result = $this->calculateService->calculateDistance(1, 'invalid_lat', 101.654);
$this->assertEquals(0, $result, 'Invalid latitude should return 0');
$result = $this->calculateService->calculateDistance(1, 3.123, 'invalid_lng');
$this->assertEquals(0, $result, 'Invalid longitude should return 0');
// Test out of range coordinates
$result = $this->calculateService->calculateDistance(1, 100.0, 101.654); // Latitude > 90
$this->assertEquals(0, $result, 'Latitude > 90 should return 0');
$result = $this->calculateService->calculateDistance(1, 3.123, 200.0); // Longitude > 180
$this->assertEquals(0, $result, 'Longitude > 180 should return 0');
}
public function testDeliveryFeeCalculationLogic()
{
// Test edge cases around the boundaries
$this->assertEquals(5.00, $this->calculateService->calculateDeliveryFee(4.99), '4.99 km should be RM 5');
$this->assertEquals(7.00, $this->calculateService->calculateDeliveryFee(5.00), '5.00 km should be RM 7');
$this->assertEquals(7.00, $this->calculateService->calculateDeliveryFee(6.99), '6.99 km should be RM 7');
$this->assertEquals(12.00, $this->calculateService->calculateDeliveryFee(7.00), '7.00 km should be RM 12');
$this->assertEquals(12.00, $this->calculateService->calculateDeliveryFee(12.99), '12.99 km should be RM 12');
$this->assertEquals(15.00, $this->calculateService->calculateDeliveryFee(13.00), '13.00 km should be RM 15');
$this->assertEquals(15.00, $this->calculateService->calculateDeliveryFee(20.00), '20.00 km should be RM 15');
$this->assertEquals(17.00, $this->calculateService->calculateDeliveryFee(21.00), '21.00 km should be RM 17');
$this->assertEquals(17.00, $this->calculateService->calculateDeliveryFee(24.99), '24.99 km should be RM 17');
$this->assertEquals(19.00, $this->calculateService->calculateDeliveryFee(25.00), '25.00 km should be RM 19');
}
}
+832
View File
@@ -0,0 +1,832 @@
<?php
use CodeIgniter\Test\CIUnitTestCase;
use App\Controllers\CustomerController;
use App\Models\Customer;
use App\Models\CustomerAddress;
use CodeIgniter\HTTP\IncomingRequest;
// use App\Controllers\Backend\CustomerController;
class CustomerAddressTest extends CIUnitTestCase
{
protected $controller;
protected $request;
protected $customerModelMock;
protected $customerAddressModelMock;
protected function setUp(): void
{
parent::setUp();
$this->controller = new \App\Controllers\Backend\CustomerController();
$this->controller->initController(
service('request'),
service('response'),
service('logger')
);
$this->request = $this->createMock(IncomingRequest::class);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->request);
// Create and inject model mocks
$this->customerModelMock = $this->createMock(\App\Models\Customer::class);
$this->customerAddressModelMock = $this->createMock(\App\Models\CustomerAddress::class);
\Config\Services::injectMock('customer', $this->customerModelMock);
\Config\Services::injectMock('customeraddress', $this->customerAddressModelMock);
}
protected function setProtectedOrPrivateProperty($object, $property, $value)
{
$reflection = new \ReflectionObject($object);
while (!$reflection->hasProperty($property)) {
$reflection = $reflection->getParentClass();
}
$prop = $reflection->getProperty($property);
$prop->setAccessible(true);
$prop->setValue($object, $value);
}
public function testCreateCustomerAddressReturnsIfCustomerIdMissing()
{
$response = $this->controller->createCustomerAddress();
$this->assertEquals(422, $response->getStatusCode());
$json = json_decode($response->getBody(), true);
$this->assertEquals('error', $json['status']);
$this->assertEquals('Customer ID is required.', $json['message']);
}
public function testCreateCustomerAddressReturnsIfCustomerNotFound()
{
$mockCustomerModel = $this->getMockBuilder(Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$mockCustomerModel->method('where')->willReturnSelf();
$mockCustomerModel->method('first')->willReturn(null);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$response = $this->controller->createCustomerAddress(1);
$this->assertEquals(404, $response->getStatusCode());
$json = json_decode($response->getBody(), true);
$this->assertEquals('error', $json['status']);
$this->assertEquals('Customer with ID 1 not found or not active.', $json['message']);
}
public function testCreateCustomerAddressReturnsOnSuccess()
{
$mockCustomerModel = $this->getMockBuilder(Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$mockCustomerModel->method('where')->willReturnSelf();
$mockCustomerModel->method('first')->willReturn(['id' => 1, 'status' => 'active']);
$mockCustomerAddressModel = $this->getMockBuilder(CustomerAddress::class)
->onlyMethods(['set', 'update', 'insert'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockCustomerAddressModel->method('where')->willReturnSelf();
$mockCustomerAddressModel->method('set')->willReturnSelf();
$mockCustomerAddressModel->method('update')->willReturn(true);
$mockCustomerAddressModel->method('insert')->willReturn(true);
$mockRequest = $this->createMock(IncomingRequest::class);
$mockRequest->method('getVar')->willReturnMap([
['name', null, 'John'],
['phone', null, '123456789'],
['address', null, '123 Main St'],
['unit', null, 'Unit 5'],
['note', null, 'Test note'],
]);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $mockRequest);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockCustomerAddressModel);
$response = $this->controller->createCustomerAddress(1);
$this->assertEquals(201, $response->getStatusCode());
$json = json_decode($response->getBody(), true);
$this->assertEquals('success', $json['status']);
$this->assertEquals('Customer address created successfully.', $json['message']);
}
public function testCreateCustomerAddressReturnsOnInsertFailure()
{
$mockCustomerModel = $this->getMockBuilder(Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$mockCustomerModel->method('where')->willReturnSelf();
$mockCustomerModel->method('first')->willReturn(['id' => 1, 'status' => 'active']);
$mockCustomerAddressModel = $this->getMockBuilder(CustomerAddress::class)
->onlyMethods(['set', 'update', 'insert'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockCustomerAddressModel->method('where')->willReturnSelf();
$mockCustomerAddressModel->method('set')->willReturnSelf();
$mockCustomerAddressModel->method('update')->willReturn(true);
$mockCustomerAddressModel->method('insert')->willReturn(false);
$mockRequest = $this->createMock(IncomingRequest::class);
$mockRequest->method('getVar')->willReturn('Some value');
$this->setProtectedOrPrivateProperty($this->controller, 'request', $mockRequest);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockCustomerAddressModel);
$response = $this->controller->createCustomerAddress(1);
$this->assertEquals(500, $response->getStatusCode());
$json = json_decode($response->getBody(), true);
$this->assertEquals('error', $json['status']);
$this->assertEquals('Failed to create customer address.', $json['message']);
}
public function testCustomerAddressIndexReturnsMultipleAddresses()
{
// Arrange: Prepare a list of 2 fake addresses
$fakeAddresses = [
[
'id' => 1,
'customer_id' => 1,
'name' => 'Home',
'phone' => '0123456789',
'address' => '123 Test Street',
'unit' => 'A-1-1',
'note' => 'Ring bell twice',
'is_default' => 1,
'created_at' => '2025-07-30 10:00:00',
'updated_at' => '2025-07-30 10:00:00',
'deleted_at' => null,
],
[
'id' => 2,
'customer_id' => 1,
'name' => 'Office',
'phone' => '0987654321',
'address' => '456 Work Avenue',
'unit' => 'B-2-2',
'note' => 'Reception 3rd floor',
'is_default' => 0,
'created_at' => '2025-07-30 11:00:00',
'updated_at' => '2025-07-30 11:00:00',
'deleted_at' => null,
],
];
// Create a mock for CustomerAddress model
$mockCustomerAddressModel = $this->createMock(\App\Models\CustomerAddress::class);
$mockCustomerAddressModel
->expects($this->once())
->method('findAll')
->willReturn($fakeAddresses);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockCustomerAddressModel);
$response = $this->controller->customerAddressIndex();
$this->assertIsObject($response);
$this->assertEquals(200, $response->getStatusCode());
$decoded = json_decode($response->getBody(), true);
$this->assertEquals('success', $decoded['status']);
$this->assertEquals('Customer addresses retrieved successfully.', $decoded['message']);
$this->assertCount(2, $decoded['data']); // Ensure two addresses
$this->assertEquals($fakeAddresses, $decoded['data']);
}
public function testShowCustomerAddressSuccess()
{
$addressData = [
'id' => 1,
'customer_id' => 101,
'name' => 'Home',
'phone' => '0123456789',
'address' => '123 Main St',
'unit' => 'A-1-1',
'note' => 'Main house',
'is_default' => 1,
];
$mockCustomerAddressModel = $this->createMock(CustomerAddress::class);
$mockCustomerAddressModel->method('find')->with(1)->willReturn($addressData);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockCustomerAddressModel);
$response = $this->controller->showCustomerAddress(1);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Customer address retrieved successfully', $response->getBody());
}
public function testShowCustomerAddressNotFound()
{
$mockCustomerAddressModel = $this->createMock(CustomerAddress::class);
$mockCustomerAddressModel->method('find')->with(999)->willReturn(null);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockCustomerAddressModel);
$response = $this->controller->showCustomerAddress(999);
$this->assertEquals(404, $response->getStatusCode());
$this->assertStringContainsString('not found', $response->getBody());
}
public function testShowCustomerAddressMissingId()
{
$this->controller = new \App\Controllers\Backend\CustomerController();
$this->setProtectedOrPrivateProperty($this->controller, 'response', \Config\Services::response());
$response = $this->controller->showCustomerAddress(null);
$this->assertEquals(422, $response->getStatusCode());
$this->assertStringContainsString('Address ID is required', $response->getBody());
}
public function testShowAddressesByCustomerSuccess()
{
$mockModel = $this->getMockBuilder(CustomerAddress::class)
->onlyMethods(['findAll'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockModel->method('where')->with('customer_id', 101)->willReturnSelf();
$mockModel->method('findAll')->willReturn([
['id' => 1, 'customer_id' => 101, 'address' => '123 Main St'],
['id' => 2, 'customer_id' => 101, 'address' => '456 Side St']
]);
// $this->controller = new CustomerController();
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockModel);
$response = $this->controller->showAddressesByCustomer(101);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Customer addresses retrieved successfully', $response->getBody());
}
public function testShowAddressesByCustomerEmpty()
{
$mockModel = $this->getMockBuilder(CustomerAddress::class)
->onlyMethods(['findAll'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockModel->method('where')->with('customer_id', 999)->willReturnSelf();
$mockModel->method('findAll')->willReturn([]);
// $this->controller = new CustomerController();
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockModel);
$response = $this->controller->showAddressesByCustomer(999);
$this->assertEquals(404, $response->getStatusCode());
$this->assertStringContainsString('No addresses found', $response->getBody());
}
public function testShowAddressesByCustomerMissingId()
{
$this->controller = new \App\Controllers\Backend\CustomerController();
$this->setProtectedOrPrivateProperty($this->controller, 'response', \Config\Services::response());
$response = $this->controller->showAddressesByCustomer(null);
$this->assertEquals(422, $response->getStatusCode());
$this->assertStringContainsString('Customer ID is required', $response->getBody());
}
public function testUpdateCustomerAddressMissingId()
{
$result = $this->controller->updateCustomerAddress(null);
$this->assertEquals(422, $result->getStatusCode());
$this->assertStringContainsString('Address ID is required', $result->getJSON());
}
public function testUpdateCustomerAddressInvalidId()
{
// Mock CustomerAddress model where 'find' returns null
$mockAddressModel = $this->createMock(CustomerAddress::class);
$mockAddressModel->method('find')->with(99)->willReturn(null);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockAddressModel);
// Customer model might still be called depending on controller flow — mock it
$mockCustomerModel = $this->createMock(Customer::class);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$result = $this->controller->updateCustomerAddress(99);
$this->assertEquals(404, $result->getStatusCode());
$this->assertStringContainsString('not found', $result->getBody());
}
public function testUpdateCustomerAddressCustomerNotFound()
{
$mockAddressModel = $this->getMockBuilder(CustomerAddress::class)
->onlyMethods(['find'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockAddressModel->method('find')->willReturn(['customer_id' => 5]);
$mockCustomerModel = $this->getMockBuilder(Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$mockCustomerModel->method('where')->willReturnSelf();
$mockCustomerModel->method('first')->willReturn(null);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockAddressModel);
$result = $this->controller->updateCustomerAddress(1);
$this->assertEquals(404, $result->getStatusCode());
$this->assertStringContainsString('Customer not found', $result->getJSON());
}
public function testUpdateCustomerAddressValidationFail()
{
$mockAddressModel = $this->createMock(CustomerAddress::class);
$mockAddressModel->method('find')->willReturn(['customer_id' => 1]);
$mockCustomerModel = $this->getMockBuilder(Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$mockCustomerModel->method('where')->willReturnSelf();
$mockCustomerModel->method('first')->willReturn(['id' => 1, 'status' => 'active']);
// Inject into controller directly
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockAddressModel);
// Mock request object
$request = \Config\Services::request();
$request->setGlobal('post', [
'name' => '', // validation fail
'phone' => '',
'address' => '',
'is_default' => ''
]);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $request);
$result = $this->controller->updateCustomerAddress(1);
$this->assertEquals(422, $result->getStatusCode());
$this->assertStringContainsString('Validation failed', $result->getJSON());
}
public function testUpdateCustomerAddressSuccess()
{
$addressId = 1;
$existingAddress = [
'id' => $addressId,
'customer_id' => 1,
'name' => 'Old Address Name',
'phone' => '0123456789',
'address' => 'Old Street',
'unit' => 'B-2-3',
'note' => 'Old note',
'is_default' => 0,
];
$mockCustomerAddressModel = $this->getMockBuilder(\App\Models\CustomerAddress::class)
->onlyMethods(['find', 'update', 'set'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockCustomerAddressModel->method('find')->with($addressId)->willReturn($existingAddress);
$mockCustomerAddressModel->method('update')->willReturn(true);
$mockCustomerAddressModel->method('where')->willReturnSelf(); // For setting others to is_default = 0
$mockCustomerAddressModel->method('set')->willReturnSelf();
// Mock Customer model
$mockCustomerModel = $this->getMockBuilder(\App\Models\Customer::class)
->onlyMethods(['first'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockCustomerModel->method('where')->willReturnSelf();
$mockCustomerModel->method('first')->willReturn([
'id' => 1,
'name' => 'Test Customer',
'status' => 'active',
'deleted_at' => null,
]);
// Simulate POST data
$mockRequest = $this->getMockBuilder(IncomingRequest::class)
->disableOriginalConstructor()
->onlyMethods(['getVar', 'getMethod'])
->getMock();
$mockRequest->method('getVar')->willReturnCallback(function ($key = null) {
$data = [
'name' => 'Updated Address Name',
'phone' => '0199999999',
'address' => 'New Street',
'unit' => 'A-1-1',
'note' => 'New note',
'is_default' => 1,
'customer_id' => 1,
];
return $key ? ($data[$key] ?? null) : $data;
});
$mockRequest->method('getMethod')->willReturn('post');
// Validation mock
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
// Create controller and inject dependencies
$controller = new \App\Controllers\Backend\CustomerController();
$this->setProtectedOrPrivateProperty($controller, 'request', $mockRequest);
$this->setProtectedOrPrivateProperty($controller, 'customerAddressModel', $mockCustomerAddressModel);
$this->setProtectedOrPrivateProperty($controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($controller, 'response', \Config\Services::response());
// Run the controller method
$response = $controller->updateCustomerAddress($addressId);
$statusCode = $response->getStatusCode();
$body = $response->getBody();
// var_dump($statusCode);
// var_dump($body);
// Assert the response
$this->assertSame(200, $response->getStatusCode());
$decodedBody = json_decode($response->getBody(), true);
$this->assertEquals('success', $decodedBody['status']);
$this->assertEquals('Customer address updated successfully.', $decodedBody['message']);
}
public function testUpdateCustomerAddressFailsToSave()
{
$addressId = 1;
$existingAddress = [
'id' => $addressId,
'customer_id' => 1,
'name' => 'Old Address Name',
'phone' => '0123456789',
'address' => 'Old Street',
'unit' => 'B-2-3',
'note' => 'Old note',
'is_default' => 0,
];
$mockCustomerAddressModel = $this->getMockBuilder(\App\Models\CustomerAddress::class)
->onlyMethods(['find', 'update', 'set'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockCustomerAddressModel->method('find')->with($addressId)->willReturn($existingAddress);
$mockCustomerAddressModel->method('update')->willReturn(false); // Simulate update failure
$mockCustomerAddressModel->method('where')->willReturnSelf();
$mockCustomerAddressModel->method('set')->willReturnSelf();
$mockCustomerModel = $this->getMockBuilder(\App\Models\Customer::class)
->onlyMethods(['first'])
->addMethods(['where'])
->disableOriginalConstructor()
->getMock();
$mockCustomerModel->method('where')->willReturnSelf();
$mockCustomerModel->method('first')->willReturn([
'id' => 1,
'name' => 'Test Customer',
'status' => 'active',
'deleted_at' => null,
]);
$mockRequest = $this->getMockBuilder(IncomingRequest::class)
->disableOriginalConstructor()
->onlyMethods(['getVar', 'getMethod'])
->getMock();
$mockRequest->method('getVar')->willReturnCallback(function ($key = null) {
$data = [
'name' => 'Updated Address Name',
'phone' => '0199999999',
'address' => 'New Street',
'unit' => 'A-1-1',
'note' => 'New note',
'is_default' => 1,
'customer_id' => 1,
];
return $key ? ($data[$key] ?? null) : $data;
});
$mockRequest->method('getMethod')->willReturn('post');
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
$controller = new \App\Controllers\Backend\CustomerController();
$this->setProtectedOrPrivateProperty($controller, 'request', $mockRequest);
$this->setProtectedOrPrivateProperty($controller, 'customerAddressModel', $mockCustomerAddressModel);
$this->setProtectedOrPrivateProperty($controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($controller, 'response', \Config\Services::response());
$response = $controller->updateCustomerAddress($addressId);
$this->assertSame(500, $response->getStatusCode());
$decoded = json_decode($response->getBody(), true);
$this->assertEquals('error', $decoded['status']);
$this->assertEquals('Failed to update customer address.', $decoded['message']);
}
public function testDeleteCustomerAddressMissingId()
{
$response = $this->controller->deleteCustomerAddress(null);
$this->assertEquals(422, $response->getStatusCode());
$this->assertStringContainsString('ID is required', $response->getJSON());
}
public function testDeleteCustomerAddressNotFound()
{
$mockCustomerAddressModel = $this->getMockBuilder(\App\Models\CustomerAddress::class)
->onlyMethods(['find'])
->disableOriginalConstructor()
->getMock();
$mockCustomerAddressModel->method('find')->willReturn(null);
// Inject mock into private property
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mockCustomerAddressModel);
// Call the method and assert response
$response = $this->controller->deleteCustomerAddress(999);
// Assert
$this->assertEquals(404, $response->getStatusCode());
$decodedBody = json_decode($response->getBody(), true);
$this->assertEquals('error', $decodedBody['status']);
$this->assertEquals('Customer address with ID 999 not found.', $decodedBody['message']);
}
public function testDeleteCustomerAddressSuccessNonDefault()
{
$addressId = 1;
$address = [
'id' => $addressId,
'customer_id' => 1,
'is_default' => 0,
];
$mock = $this->createMock(CustomerAddress::class);
$mock->method('find')->with($addressId)->willReturn($address);
$mock->method('delete')->with($addressId)->willReturn(true);
// Inject mock using reflection
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mock);
$response = $this->controller->deleteCustomerAddress($addressId);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('deleted successfully', $response->getJSON());
}
public function testDeleteCustomerAddressSuccessDefault()
{
$addressId = 1;
$customerId = 101;
$defaultAddress = [
'id' => $addressId,
'customer_id' => $customerId,
'is_default' => 1,
];
$anotherAddress = [
'id' => 2,
'customer_id' => $customerId,
'is_default' => 0,
];
$mock = $this->getMockBuilder(CustomerAddress::class)
->onlyMethods(['find', 'delete', 'first', 'update'])
->addMethods(['where', 'orderBy'])
->disableOriginalConstructor()
->getMock();
$mock->method('find')->with($addressId)->willReturn($defaultAddress);
$mock->method('delete')->with($addressId)->willReturn(true);
$mock->method('where')->willReturnSelf();
$mock->method('orderBy')->willReturnSelf();
$mock->method('first')->willReturn($anotherAddress);
$mock->method('update')->with($anotherAddress['id'], ['is_default' => 1])->willReturn(true);
// Inject mock using reflection
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mock);
$response = $this->controller->deleteCustomerAddress($addressId);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('deleted successfully', $response->getJSON());
}
public function testDeleteCustomerAddressFailsToDelete()
{
$addressId = 1;
$address = [
'id' => $addressId,
'customer_id' => 1,
'is_default' => 0,
];
$mock = $this->createMock(CustomerAddress::class);
$mock->method('find')->with($addressId)->willReturn($address);
$mock->method('delete')->with($addressId)->willReturn(false);
// Inject mock using reflection
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $mock);
$response = $this->controller->deleteCustomerAddress($addressId);
$this->assertEquals(500, $response->getStatusCode());
$this->assertStringContainsString('Failed to delete', $response->getJSON());
}
public function testSetDefaultCustomerAddressSuccess()
{
$customerId = 123;
$addressId = 456;
// Mock request
$this->request->method('getVar')->with('customer_id')->willReturn($customerId);
$this->customerModelMock = $this->getMockBuilder(\App\Models\Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$this->customerModelMock->method('where')->willReturnSelf();
$this->customerModelMock->method('first')->willReturn([
'id' => $customerId,
'status' => 'active',
]);
// Mock address model
$this->customerAddressModelMock = $this->getMockBuilder(\App\Models\CustomerAddress::class)
->addMethods(['where'])
->onlyMethods(['first', 'set', 'update'])
->disableOriginalConstructor()
->getMock();
$this->customerAddressModelMock->method('where')->willReturnSelf();
$this->customerAddressModelMock->method('first')->willReturn([
'id' => $addressId,
'customer_id' => $customerId,
]);
$this->customerAddressModelMock->method('set')->willReturnSelf();
$this->customerAddressModelMock->method('update')->willReturn(true);
// Inject mocks into service container
\Config\Services::injectMock('customer', $this->customerModelMock);
\Config\Services::injectMock('customeraddress', $this->customerAddressModelMock);
// Set mocked model into the controller before calling the method
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModelMock);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $this->customerAddressModelMock);
// Capture response
$response = $this->controller->setDefaultCustomerAddress($addressId);
$responseArray = json_decode($response->getBody(), true);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('success', $responseArray['status']);
$this->assertEquals("Address ID {$addressId} is now the default for customer ID {$customerId}.", $responseArray['message']);
$this->assertEquals($customerId, $responseArray['data']['customer_id']);
$this->assertEquals($addressId, $responseArray['data']['address_id']);
}
public function testSetDefaultCustomerAddressMissingCustomerId()
{
// Arrange
$addressId = 456;
// Simulate request missing 'customer_id'
$this->request->method('getVar')->with('customer_id')->willReturn(null);
// Act
$response = $this->controller->setDefaultCustomerAddress($addressId);
$responseArray = json_decode($response->getBody(), true);
// Assert
$this->assertEquals(422, $response->getStatusCode());
$this->assertEquals('error', $responseArray['status']);
$this->assertEquals('Customer ID is required.', $responseArray['message']);
$this->assertNull($responseArray['data']);
}
public function testSetDefaultCustomerAddressFailsWhenCustomerNotFound()
{
$customerId = 123;
$addressId = 456;
// Mock request
$this->request->method('getVar')->with('customer_id')->willReturn($customerId);
// Mock customer model to return null
$this->customerModelMock = $this->getMockBuilder(\App\Models\Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$this->customerModelMock->method('where')->willReturnSelf();
$this->customerModelMock->method('first')->willReturn(null); // customer not found
\Config\Services::injectMock('customer', $this->customerModelMock);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModelMock);
$response = $this->controller->setDefaultCustomerAddress($addressId);
$responseArray = json_decode($response->getBody(), true);
$this->assertEquals(404, $response->getStatusCode());
$this->assertEquals('error', $responseArray['status']);
$this->assertEquals('Customer not found or inactive.', $responseArray['message']);
$this->assertNull($responseArray['data']);
}
public function testSetDefaultCustomerAddressFailsWhenAddressNotFound()
{
$customerId = 123;
$addressId = 456;
$this->request->method('getVar')->with('customer_id')->willReturn($customerId);
// Mock valid customer
$this->customerModelMock = $this->getMockBuilder(\App\Models\Customer::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$this->customerModelMock->method('where')->willReturnSelf();
$this->customerModelMock->method('first')->willReturn([
'id' => $customerId,
'status' => 'active',
]);
// Mock address not found
$this->customerAddressModelMock = $this->getMockBuilder(\App\Models\CustomerAddress::class)
->addMethods(['where'])
->onlyMethods(['first'])
->disableOriginalConstructor()
->getMock();
$this->customerAddressModelMock->method('where')->willReturnSelf();
$this->customerAddressModelMock->method('first')->willReturn(null);
\Config\Services::injectMock('customer', $this->customerModelMock);
\Config\Services::injectMock('customeraddress', $this->customerAddressModelMock);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModelMock);
$this->setProtectedOrPrivateProperty($this->controller, 'customerAddressModel', $this->customerAddressModelMock);
$response = $this->controller->setDefaultCustomerAddress($addressId);
$responseArray = json_decode($response->getBody(), true);
$this->assertEquals(404, $response->getStatusCode());
$this->assertEquals('error', $responseArray['status']);
$this->assertEquals('Address not found or does not belong to the customer.', $responseArray['message']);
$this->assertNull($responseArray['data']);
}
}
+629
View File
@@ -0,0 +1,629 @@
<?php
namespace App\Tests\Controllers\Backend;
use App\Controllers\Backend\CustomerController;
use App\Models\Customer;
use App\Models\SettingCustomerType;
use App\Models\SettingMembershipTier;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use Config\App;
use Config\Services;
class CustomerTest extends CIUnitTestCase
{
protected $controller;
protected $request;
protected $response;
protected $customerModel;
protected $customerTypeModel;
protected $tierModel;
protected function setUp(): void
{
parent::setUp();
// Mock models
$this->customerModel = $this->getMockBuilder(Customer::class)
->onlyMethods(['insert', 'getInsertID', 'update'])
->getMock();
$this->customerTypeModel = $this->getMockBuilder(SettingCustomerType::class)
->onlyMethods(['first'])
->addMethods(['where'])
->getMock();
$this->tierModel = $this->getMockBuilder(SettingMembershipTier::class)
->onlyMethods(['first'])
->addMethods(['where'])
->getMock();
// Set global POST input before request is retrieved
service('request')->setGlobal('post', [
'customer_type' => 'Regular',
'customer_tier' => 'Bronze',
'birthday' => '1990-01-01',
'phone' => '0123456789',
'email' => 'test@example.com',
'password_hash' => 'secret1234',
'name' => 'John Doe',
]);
// Get request service AFTER setting global input
$this->request = service('request');
$this->response = service('response');
// Now init the controller with the correct request/response
$this->controller = new \App\Controllers\Backend\CustomerController();
$this->controller->initController($this->request, $this->response, service('logger'));
// Inject mocks
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModel);
$this->setProtectedOrPrivateProperty($this->controller, 'customerTypeModel', $this->customerTypeModel);
$this->setProtectedOrPrivateProperty($this->controller, 'membershipTierModel', $this->tierModel);
}
//Have an issue
protected function setProtectedOrPrivateProperty($object, $property, $value)
{
$reflection = new \ReflectionObject($object);
while (!$reflection->hasProperty($property)) {
$reflection = $reflection->getParentClass();
if (!$reflection) {
throw new \Exception("Property {$property} does not exist");
}
}
$prop = $reflection->getProperty($property);
$prop->setAccessible(true);
$prop->setValue($object, $value);
}
public function testCreateFailsValidation()
{
$this->request->setGlobal('post', [
'customer_type' => 'Regular',
'customer_tier' => 'Bronze',
'birthday' => '1990-01-01',
'phone' => '0123456789',
'email' => 'invalid-email', // Invalid email format
'password_hash' => 'secret1234',
// 'name' is missing
]);
$this->customerTypeModel->method('where')->willReturnSelf();
$this->customerTypeModel->method('first')->willReturn(['id' => 1, 'name' => 'Regular']);
$this->tierModel->method('where')->willReturnSelf();
$this->tierModel->method('first')->willReturn(['id' => 2, 'name' => 'Bronze']);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->request);
$response = $this->controller->create();
$this->assertEquals(400, $response->getStatusCode());
}
public function testCreateFailsIfCustomerTypeNotFound()
{
// Inject mock validation to bypass default validation rules
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
// $this->request = service('request');
$this->request->setGlobal('post', [
'customer_type' => 'NonexistentType',
// 'customer_tier' => 'Bronze',
'birthday' => '1990-01-01',
'phone' => '0123456789',
'email' => 'test@example.com',
'password_hash' => 'secret1234',
'name' => 'John Doe',
]);
$this->customerTypeModel->method('where')->willReturnSelf();
$this->customerTypeModel->method('first')->willReturn(null);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->request);
$response = $this->controller->create();
$this->assertEquals(404, $response->getStatusCode());
// $this->assertStringContainsString('Customer type', $response->getBody());
}
public function testCreateFailsIfMembershipTierNotFound()
{
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
$this->request->setGlobal('post', [
'customer_type' => 'Regular',
'customer_tier' => 'NonexistentTier',
'birthday' => '1990-01-01',
'phone' => '0123456789',
'email' => 'test@example.com',
'password_hash' => 'secret1234',
'name' => 'John Doe',
]);
$this->customerTypeModel->method('where')->willReturnSelf();
$this->customerTypeModel->method('first')->willReturn(['id' => 1, 'name' => 'Regular']);
$this->tierModel->method('where')->willReturnSelf();
$this->tierModel->method('first')->willReturn(null);
$response = $this->controller->create();
$this->assertEquals(404, $response->getStatusCode());
}
public function testCreateCustomerSuccess()
{
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
$this->request->setGlobal('post', [
'customer_type' => 'Regular',
'customer_tier' => 'Bronze',
'birthday' => '1990-01-01',
'phone' => '0123456789',
'email' => 'test@example.com',
'password_hash' => 'secret1234',
'name' => 'John Doe',
]);
$this->customerTypeModel->method('where')->willReturnSelf();
$this->customerTypeModel->method('first')->willReturn(['id' => 1, 'name' => 'Regular']);
$this->tierModel->method('where')->willReturnSelf();
$this->tierModel->method('first')->willReturn(['id' => 2, 'name' => 'Bronze']);
$this->customerModel->method('insert')->willReturn(true);
$this->customerModel->method('getInsertID')->willReturn(5);
$this->customerModel->method('update')->willReturn(true);
// Inject a real response object
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModel);
$this->setProtectedOrPrivateProperty($this->controller, 'customerTypeModel', $this->customerTypeModel);
$this->setProtectedOrPrivateProperty($this->controller, 'membershipTierModel', $this->tierModel);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->request);
$this->setProtectedOrPrivateProperty($this->controller, 'response', service('response'));
$response = $this->controller->create();
// var_dump($response->getStatusCode());
// var_dump($response->getBody());
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Customer created successfully', $response->getBody());
$_POST = [];
}
public function testCreateFailsOnCustomerInsert()
{
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
$this->request->setGlobal('post', [
'customer_type' => 'Regular',
'customer_tier' => 'Bronze',
'birthday' => '1990-01-01',
'phone' => '0123456789',
'email' => 'test@example.com',
'password_hash' => 'secret1234',
'name' => 'John Doe',
]);
// Mock success on lookups
$this->customerTypeModel->method('where')->willReturnSelf();
$this->customerTypeModel->method('first')->willReturn(['id' => 1, 'name' => 'Regular']);
$this->tierModel->method('where')->willReturnSelf();
$this->tierModel->method('first')->willReturn(['id' => 2, 'name' => 'Bronze']);
// Simulate failure in DB insert
$this->customerModel->method('insert')->willReturn(false);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->request);
$response = $this->controller->create();
$this->assertEquals(500, $response->getStatusCode());
$this->assertStringContainsString('Failed to create customer', $response->getBody());
}
public function testIndexReturnsCustomers()
{
$mockData = [
[
'id' => 1,
'name' => 'Test Customer',
'customer_tier' => 'Gold',
],
[
'id' => 2,
'name' => 'Second Customer',
'customer_tier' => 'Silver',
]
];
$this->customerModel = $this->getMockBuilder(Customer::class)
->onlyMethods(['findAll'])
->addMethods(['select', 'join'])
->getMock();
$this->customerModel->method('select')->willReturnSelf();
$this->customerModel->method('join')->willReturnSelf();
$this->customerModel->method('findAll')->willReturn($mockData);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModel);
$response = $this->controller->index();
$this->assertSame(200, $response->getStatusCode());
$output = json_decode($response->getBody(), true);
$this->assertEquals('success', $output['status']);
$this->assertEquals('Customer list retrieved successfully.', $output['message']);
$this->assertCount(2, $output['data']);
$this->assertEquals('Test Customer', $output['data'][0]['name']);
}
public function testShowReturnsCustomerSuccess()
{
$mockCustomer = [
'id' => 1,
'name' => 'Test Customer',
'email' => 'test@example.com',
'profile_picture' => null,
'customer_tier' => 'Gold',
];
$this->customerModel = $this->getMockBuilder(Customer::class)
->onlyMethods(['first'])
->addMethods(['select', 'join', 'where'])
->getMock();
$this->customerModel->method('select')->willReturnSelf();
$this->customerModel->method('join')->willReturnSelf();
$this->customerModel->method('where')->willReturnSelf();
$this->customerModel->method('first')->willReturn($mockCustomer);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModel);
$response = $this->controller->show(1);
$this->assertSame(200, $response->getStatusCode());
$output = json_decode($response->getBody(), true);
$this->assertEquals('success', $output['status']);
$this->assertEquals('Customer retrieved successfully.', $output['message']);
$this->assertEquals('Test Customer', $output['data']['name']);
}
public function testShowReturnsValidationErrorWhenIdMissing()
{
$this->setProtectedOrPrivateProperty($this->controller, 'response', service('response'));
$response = $this->controller->show(null);
$this->assertEquals(422, $response->getStatusCode());
$this->assertStringContainsString('Customer ID is required.', $response->getBody());
}
public function testShowReturnsNotFoundWhenCustomerMissing()
{
$this->customerModel = $this->getMockBuilder(Customer::class)
->onlyMethods(['first'])
->addMethods(['select', 'join', 'where'])
->getMock();
$this->customerModel->method('select')->willReturnSelf();
$this->customerModel->method('join')->willReturnSelf();
$this->customerModel->method('where')->willReturnSelf();
$this->customerModel->method('first')->willReturn(null);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModel);
$response = $this->controller->show(99); // non-existing
$this->assertSame(404, $response->getStatusCode());
$output = json_decode($response->getBody(), true);
$this->assertEquals('error', $output['status']);
$this->assertEquals('Customer with ID 99 not found.', $output['message']);
}
public function testDeleteCustomerFailsWithoutId()
{
$this->controller = new CustomerController();
$this->controller->initController(service('request'), service('response'), service('logger'));
$result = $this->controller->delete(null);
$output = json_decode($result->getJSON(), true);
$this->assertEquals(422, $result->getStatusCode());
$this->assertEquals('error', $output['status']);
$this->assertEquals('Customer ID is required.', $output['message']);
$this->assertNull($output['data']);
}
public function testDeleteCustomerFailsWhenNotFound()
{
$this->customerModel = $this->getMockBuilder(Customer::class)
->onlyMethods(['find'])
->getMock();
$this->customerModel->method('find')->willReturn(null);
$this->controller = new CustomerController();
$this->controller->initController(service('request'), service('response'), service('logger'));
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModel);
$result = $this->controller->delete(999);
$output = json_decode($result->getJSON(), true);
$this->assertEquals(404, $result->getStatusCode());
$this->assertEquals('error', $output['status']);
$this->assertEquals('Customer with ID 999 not found.', $output['message']);
$this->assertNull($output['data']);
}
public function testDeleteCustomerSuccess()
{
$mockCustomer = ['id' => 1, 'name' => 'Test User'];
$this->customerModel = $this->getMockBuilder(Customer::class)
->onlyMethods(['find', 'delete'])
->getMock();
$this->customerModel->method('find')->with(1)->willReturn($mockCustomer);
$this->customerModel->method('delete')->with(1)->willReturn(true);
$this->controller = new CustomerController();
$this->controller->initController(service('request'), service('response'), service('logger'));
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $this->customerModel);
$result = $this->controller->delete(1);
$output = json_decode($result->getJSON(), true);
$this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('success', $output['status']);
$this->assertEquals('Customer with ID 1 deleted successfully.', $output['message']);
$this->assertNull($output['data']);
}
public function testUpdateFailsOnValidationError()
{
$id = 1;
$mockCustomerModel = $this->createMock(\App\Models\Customer::class);
$mockCustomerModel->method('find')->willReturn([
'id' => $id,
'phone' => '0123456789',
'email' => 'test@example.com'
]);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$this->request->setGlobal('post', [
'customer_type' => 'Regular',
'customer_tier' => 'Bronze',
'birthday' => '1990-01-01',
'phone' => '0123456789',
'email' => 'test@example.com',
'name' => '', // Empty name = fail
]);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->request);
$response = $this->controller->update($id);
$this->assertEquals(422, $response->getStatusCode());
$this->assertStringContainsString('Validation failed', $response->getBody());
}
public function testUpdateCustomerSuccess()
{
// Mock customer ID
$customerId = 1;
// Expected customer data returned by find()
$existingCustomer = [
'id' => $customerId,
'name' => 'Old Name',
'phone' => '0111111111',
'email' => 'old@example.com',
'customer_type' => 'Regular',
'membership_tier' => 'Silver',
'birthday' => '1990-01-01',
];
// Create mock for Customer model
$mockCustomerModel = $this->createMock(Customer::class);
$mockCustomerModel->method('find')->with($customerId)->willReturn($existingCustomer);
$mockCustomerModel->method('update')->willReturn(true);
// Mock SettingCustomerType model
$mockCustomerTypeModel = $this->getMockBuilder(SettingCustomerType::class)
->onlyMethods(['first']) // first() exists — use onlyMethods()
->addMethods(['where']) // where() is dynamic — use addMethods()
->getMock();
$mockCustomerTypeModel->method('where')->willReturnSelf();
$mockCustomerTypeModel->method('first')->willReturn(['name' => 'Regular']);
// Mock SettingMembershipTier model
$mockTierModel = $this->getMockBuilder(SettingMembershipTier::class)
->onlyMethods(['first']) // first() exists — use onlyMethods()
->addMethods(['where']) // where() is dynamic — use addMethods()
->getMock();
$mockTierModel->method('where')->willReturnSelf();
$mockTierModel->method('first')->willReturn([
'id' => 1,
'name' => 'Gold'
]);
// Mock the IncomingRequest
$mockRequest = $this->getMockBuilder(IncomingRequest::class)
->disableOriginalConstructor()
->onlyMethods(['getVar', 'getMethod'])
->getMock();
$mockRequest->method('getVar')->willReturnCallback(function ($key) {
$data = [
'id' => 1,
'phone' => '0123456789',
'name' => 'Updated Name',
'email' => 'updated@example.com',
'customer_type' => 'Regular',
'membership_tier' => 'Gold',
'birthday' => '1990-01-01',
];
return $data[$key] ?? null;
});
$mockRequest->method('getMethod')->willReturn('post');
// Mock validation to return true (skip validation errors)
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
// Create controller and inject mocks
$controller = new CustomerController();
$this->setProtectedOrPrivateProperty($controller, 'request', $mockRequest);
$this->setProtectedOrPrivateProperty($controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($controller, 'customerTypeModel', $mockCustomerTypeModel);
$this->setProtectedOrPrivateProperty($controller, 'membershipTierModel', $mockTierModel);
$this->setProtectedOrPrivateProperty($controller, 'response', \Config\Services::response());
// Run update
$response = $controller->update($customerId);
// $statusCode = $response->getStatusCode();
// $body = $response->getBody();
// var_dump($statusCode);
// var_dump($body);
// Assert success
$this->assertSame(200, $response->getStatusCode());
}
public function testUpdateFailsWhenCustomerNotFound()
{
$mockCustomerModel = $this->createMock(\App\Models\Customer::class);
$mockCustomerModel->method('find')->willReturn(null);
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$response = $this->controller->update(999); // Non-existent ID
$this->assertEquals(404, $response->getStatusCode());
$this->assertStringContainsString('Customer with ID 999 not found', $response->getBody());
}
public function testUpdateFailsIfTierNotFound()
{
$id = 1;
// Mock customerModel
$mockCustomerModel = $this->createMock(\App\Models\Customer::class);
$mockCustomerModel->method('find')->willReturn([
'id' => $id,
'phone' => '0123456789',
'email' => 'test@example.com',
]);
// Mock membershipTierModel to return null (tier not found)
$mockTierModel = $this->getMockBuilder(SettingMembershipTier::class)
->onlyMethods(['first'])
->addMethods(['where'])
->getMock();
$mockTierModel->method('where')->willReturnSelf();
$mockTierModel->method('first')->willReturn(null);
// Set mocked models into the controller
$this->setProtectedOrPrivateProperty($this->controller, 'customerModel', $mockCustomerModel);
$this->setProtectedOrPrivateProperty($this->controller, 'membershipTierModel', $mockTierModel);
// Create and mock a request with POST data
$mockRequest = $this->getMockBuilder(IncomingRequest::class)
->disableOriginalConstructor()
->onlyMethods(['getVar', 'getMethod'])
->getMock();
$mockRequest->method('getVar')->willReturnMap([
['customer_type', null, 'Regular'],
['customer_tier', null, 'NonexistentTier'],
['birthday', null, '1990-01-01'],
['phone', null, '0123456789'],
['email', null, 'test@example.com'],
['name', null, 'John Doe'],
]);
$mockRequest->method('getMethod')->willReturn('post');
// Set the mocked request into the controller
$this->setProtectedOrPrivateProperty($this->controller, 'request', $mockRequest);
// Mock validation to return true (skip validation errors)
$mockValidation = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->onlyMethods(['run', 'getErrors'])
->getMock();
$mockValidation->method('run')->willReturn(true);
$mockValidation->method('getErrors')->willReturn([]);
\Config\Services::injectMock('validation', $mockValidation);
// Call the update method
$response = $this->controller->update($id);
// Assertions
$this->assertEquals(404, $response->getStatusCode());
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
use CodeIgniter\Test\CIUnitTestCase;
use Config\App;
use Tests\Support\Libraries\ConfigReader;
/**
* @internal
*/
final class HealthTest extends CIUnitTestCase
{
public function testIsDefinedAppPath(): void
{
$this->assertTrue(defined('APPPATH'));
}
public function testBaseUrlHasBeenSet(): void
{
$validation = service('validation');
$env = false;
// Check the baseURL in .env
if (is_file(HOMEPATH . '.env')) {
$env = preg_grep('/^app\.baseURL = ./', file(HOMEPATH . '.env')) !== false;
}
if ($env) {
// BaseURL in .env is a valid URL?
// phpunit.xml.dist sets app.baseURL in $_SERVER
// So if you set app.baseURL in .env, it takes precedence
$config = new App();
$this->assertTrue(
$validation->check($config->baseURL, 'valid_url'),
'baseURL "' . $config->baseURL . '" in .env is not valid URL',
);
}
// Get the baseURL in app/Config/App.php
// You can't use Config\App, because phpunit.xml.dist sets app.baseURL
$reader = new ConfigReader();
// BaseURL in app/Config/App.php is a valid URL?
$this->assertTrue(
$validation->check($reader->baseURL, 'valid_url'),
'baseURL "' . $reader->baseURL . '" in app/Config/App.php is not valid URL',
);
}
}
+445
View File
@@ -0,0 +1,445 @@
<?php
namespace App\Tests\Controllers\Backend;
use App\Controllers\Backend\OutletController;
use App\Models\Outlet;
use App\Models\OutletTax;
use App\Models\OutletImage;
use App\Models\OutletOperatingDay;
use App\Models\OutletOperatingHour;
use App\Models\OutletOperatingHoursException;
use App\Models\SettingTax;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\Database\ConnectionInterface;
use CodeIgniter\Database\QueryBuilder;
use Mockery;
use Config\Database as CI4Database;
class OutletTest extends CIUnitTestCase
{
protected $mockOutlet;
protected $mockOutletTax;
protected $mockOutletImage;
protected $mockOutletOperatingDay;
protected $mockOutletOperatingHour;
protected $mockOutletOperatingHoursException;
protected $mockSettingTax;
protected $controller;
protected function setProtectedOrPrivateProperty($object, $property, $value)
{
$reflection = new \ReflectionObject($object);
while (!$reflection->hasProperty($property)) {
$reflection = $reflection->getParentClass();
if (!$reflection) {
throw new \Exception("Property {$property} does not exist");
}
}
$prop = $reflection->getProperty($property);
$prop->setAccessible(true);
$prop->setValue($object, $value);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
protected function setUp(): void
{
parent::setUp();
$this->mockOutlet = $this->createMock(Outlet::class);
$this->mockOutletTax = $this->getMockBuilder(OutletTax::class)
->addMethods(['where'])
->getMock();
$this->mockOutletImage = $this->createMock(OutletImage::class);
$this->mockOutletOperatingDay = $this->getMockBuilder(OutletOperatingDay::class)
->addMethods(['where'])
->getMock();
$this->mockOutletOperatingHour = $this->getMockBuilder(OutletOperatingHour::class)
->addMethods(['where'])
->getMock();
$this->mockOutletOperatingHoursException = $this->getMockBuilder(OutletOperatingHoursException::class)
->addMethods(['where'])
->getMock();
$this->mockSettingTax = $this->createMock(SettingTax::class);
$this->controller = new OutletController();
$this->setProtectedOrPrivateProperty($this->controller, 'outlet', $this->mockOutlet);
$this->setProtectedOrPrivateProperty($this->controller, 'outletTax', $this->mockOutletTax);
$this->setProtectedOrPrivateProperty($this->controller, 'outletImage', $this->mockOutletImage);
$this->setProtectedOrPrivateProperty($this->controller, 'outletOperatingDays', $this->mockOutletOperatingDay);
$this->setProtectedOrPrivateProperty($this->controller, 'outletOperatingHours', $this->mockOutletOperatingHour);
$this->setProtectedOrPrivateProperty($this->controller, 'outletOperatingHoursExceptions', $this->mockOutletOperatingHoursException);
$this->setProtectedOrPrivateProperty($this->controller, 'settingTax', $this->mockSettingTax);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->createMock(IncomingRequest::class));
$this->setProtectedOrPrivateProperty($this->controller, 'response', service('response'));
}
public function testIndexReturnsOutlets()
{
$outlets = [
['id' => 1, 'title' => 'Outlet 1'],
['id' => 2, 'title' => 'Outlet 2'],
];
$this->mockOutlet->method('getOperatingHoursWithDaysList')->willReturn($outlets);
$result = $this->controller->index();
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(200, $data['status']);
$this->assertEquals($outlets, $data['result']);
}
public function testIndexReturnsNoOutlets()
{
$this->mockOutlet->method('getOperatingHoursWithDaysList')->willReturn([]);
$result = $this->controller->index();
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(400, $data['status']);
$this->assertEquals('No outlet data found.', $data['result']);
}
public function testShowReturnsOutlet()
{
$outlet = ['id' => 1, 'title' => 'Outlet 1'];
$this->mockOutlet->method('getOperatingHoursWithDays')->with(1)->willReturn($outlet);
$result = $this->controller->show(1);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(200, $data['status']);
$this->assertEquals($outlet, $data['result']);
}
public function testShowReturnsNotFound()
{
$this->mockOutlet->method('getOperatingHoursWithDays')->with(99)->willReturn(null);
$result = $this->controller->show(99);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(400, $data['status']);
$this->assertEquals('No outlet data found.', $data['result']);
}
public function testDeleteOutletNotFound()
{
$this->mockOutlet->method('find')->with(99)->willReturn(null);
$result = $this->controller->delete(99);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(400, $data['status']);
$this->assertEquals('No outlet data found.', $data['result']);
}
public function testDeleteSuccess()
{
// Create mock builder objects for chainable methods
$mockTaxBuilder = $this->getMockBuilder(\CodeIgniter\Database\BaseBuilder::class)
->disableOriginalConstructor()
->onlyMethods(['delete'])
->getMock();
$mockDayBuilder = $this->getMockBuilder(\CodeIgniter\Database\BaseBuilder::class)
->disableOriginalConstructor()
->onlyMethods(['delete'])
->getMock();
$mockHourBuilder = $this->getMockBuilder(\CodeIgniter\Database\BaseBuilder::class)
->disableOriginalConstructor()
->onlyMethods(['delete'])
->getMock();
$mockExceptionBuilder = $this->getMockBuilder(\CodeIgniter\Database\BaseBuilder::class)
->disableOriginalConstructor()
->onlyMethods(['delete'])
->getMock();
$this->mockOutlet->method('find')->with(1)->willReturn(['id' => 1]);
$this->mockOutlet->expects($this->once())->method('delete')->with(1);
// Setup chainable method expectations
$this->mockOutletTax->method('where')->with('outlet_id', 1)->willReturn($mockTaxBuilder);
$mockTaxBuilder->expects($this->once())->method('delete')->willReturn(true);
$this->mockOutletOperatingDay->method('where')->with('outlet_id', 1)->willReturn($mockDayBuilder);
$mockDayBuilder->expects($this->once())->method('delete')->willReturn(true);
$this->mockOutletOperatingHour->method('where')->with('outlet_id', 1)->willReturn($mockHourBuilder);
$mockHourBuilder->expects($this->once())->method('delete')->willReturn(true);
$this->mockOutletOperatingHoursException->method('where')->with('outlet_id', 1)->willReturn($mockExceptionBuilder);
$mockExceptionBuilder->expects($this->once())->method('delete')->willReturn(true);
$result = $this->controller->delete(1);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(200, $data['status']);
$this->assertEquals('Outlet deleted successfully.', $data['result']);
}
public function testUpdatePasswordOutletNotFound()
{
$this->mockOutlet->method('find')->with(99)->willReturn(null);
$result = $this->controller->updatePassword(99);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(400, $data['status']);
$this->assertEquals('No outlet data found.', $data['result']);
}
public function testCreateValidationFails()
{
$controller = $this->getMockBuilder(OutletController::class)
->onlyMethods(['validate'])
->enableOriginalConstructor()
->getMock();
$controller->method('validate')->willReturn(false);
$validator = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->getMock();
$validator->method('getErrors')->willReturn(['title' => 'Required']);
$this->setProtectedOrPrivateProperty($controller, 'validator', $validator);
$this->setProtectedOrPrivateProperty($controller, 'outlet', $this->mockOutlet);
$this->setProtectedOrPrivateProperty($controller, 'outletTax', $this->mockOutletTax);
$this->setProtectedOrPrivateProperty($controller, 'outletImage', $this->mockOutletImage);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingDays', $this->mockOutletOperatingDay);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingHours', $this->mockOutletOperatingHour);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingHoursExceptions', $this->mockOutletOperatingHoursException);
$this->setProtectedOrPrivateProperty($controller, 'settingTax', $this->mockSettingTax);
$this->setProtectedOrPrivateProperty($controller, 'request', $this->createMock(IncomingRequest::class));
$this->setProtectedOrPrivateProperty($controller, 'response', service('response'));
$result = $controller->create();
$this->assertEquals(400, $result->getStatusCode()); // 400 is CodeIgniter's default
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('messages', $data);
$this->assertArrayHasKey('title', $data['messages']);
}
public function testCreateSuccess()
{
// 1. Mock IncomingRequest
$request = $this->createMock(\CodeIgniter\HTTP\IncomingRequest::class);
$request->method('getVar')->willReturnMap([
['title', 'Outlet 1'],
['email', 'outlet1@example.com'],
['phone', '123456789'],
['address', '123 Street'],
['state', 'State'],
['postal_code', '12345'],
['country', 'Country'],
['latitude', '1.234'],
['longitude', '5.678'],
['password', 'password'],
['outlet_delivery_coverage', '10'],
['outlet_tax', json_encode([1, 2])],
['outlet_operating_days', json_encode([['0' => ['is_operated' => 1]]])],
['outlet_operating_hours', json_encode([['0' => [['start_time' => '09:00', 'end_time' => '18:00']]]])],
['outlet_operating_hours_exceptions', json_encode([])],
]);
$request->method('getFiles')->willReturn([]);
// 2. Mock Controller with validate() overridden
$controller = $this->getMockBuilder(\App\Controllers\Backend\OutletController::class)
->onlyMethods(['validate'])
->getMock();
$controller->method('validate')->willReturn(true);
// 3. Mock all Models and insert returns
$mockOutlet = $this->createMock(\App\Models\Outlet::class);
$mockOutlet->method('insert')->willReturn(1);
$mockOutlet->method('find')->willReturn(['id' => 1, 'title' => 'Outlet 1']);
$mockOutletTax = $this->createMock(\App\Models\OutletTax::class);
$mockOutletTax->method('insert')->willReturn(true);
$mockOutletOperatingDay = $this->createMock(\App\Models\OutletOperatingDay::class);
$mockOutletOperatingDay->method('insert')->willReturn(true);
$mockOutletOperatingHour = $this->createMock(\App\Models\OutletOperatingHour::class);
$mockOutletOperatingHour->method('insert')->willReturn(true);
$mockOutletOperatingHoursException = $this->createMock(\App\Models\OutletOperatingHoursException::class);
$mockOutletOperatingHoursException->method('insert')->willReturn(true);
$mockOutletImage = $this->createMock(\App\Models\OutletImage::class);
$mockSettingTax = $this->createMock(\App\Models\SettingTax::class);
// 4. Mock DB transaction flow
// Mock global DB connection
$mockDB = Mockery::mock(\CodeIgniter\Database\BaseConnection::class);
$mockDB->shouldReceive('transBegin')->andReturn(true);
$mockDB->shouldReceive('transCommit')->andReturn(true);
$mockDB->shouldReceive('transRollback')->andReturn(true);
$this->setProtectedOrPrivateProperty($controller, 'db', $mockDB);
// 5. Inject all mocked dependencies
$this->setProtectedOrPrivateProperty($controller, 'request', $request);
$this->setProtectedOrPrivateProperty($controller, 'outlet', $mockOutlet);
$this->setProtectedOrPrivateProperty($controller, 'outletTax', $mockOutletTax);
$this->setProtectedOrPrivateProperty($controller, 'outletImage', $mockOutletImage);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingDays', $mockOutletOperatingDay);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingHours', $mockOutletOperatingHour);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingHoursExceptions', $mockOutletOperatingHoursException);
$this->setProtectedOrPrivateProperty($controller, 'settingTax', $mockSettingTax);
$this->setProtectedOrPrivateProperty($controller, 'response', service('response'));
$this->setProtectedOrPrivateProperty($controller, 'db', $mockDB);
// 6. Call the method
$response = $controller->create();
// 7. Assert response
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getBody(), true);
$this->assertEquals('success', $data['status']);
$this->assertArrayHasKey('result', $data);
$this->assertEquals('Outlet 1', $data['result']['title']);
}
public function testUpdateValidationFails()
{
// Create controller mock that allows original validateData method to be called
$controller = $this->getMockBuilder(OutletController::class)
->onlyMethods([]) // Don't mock any methods - we want real validateData behavior
->enableOriginalConstructor()
->getMock();
// Create a proper validator mock
$validator = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->getMock();
$validator->method('getErrors')->willReturn([
'title' => 'The title field is required.',
'email' => 'The email field is required.',
'phone' => 'The phone field is required.',
'address' => 'The address field is required.',
'state' => 'The state field is required.',
'postal_code' => 'The postal_code field is required.',
'country' => 'The country field is required.',
'latitude' => 'The latitude field is required.',
'longitude' => 'The longitude field is required.',
'outlet_delivery_coverage' => 'The outlet_delivery_coverage field is required.',
'outlet_tax' => 'The outlet_tax field is required.',
'outlet_operating_days' => 'The outlet_operating_days field is required.',
'outlet_operating_hours' => 'The outlet_operating_hours field is required.',
]);
// Create request mock with invalid data
$mockRequest = $this->getMockBuilder(IncomingRequest::class)
->disableOriginalConstructor()
->getMock();
$invalidData = [
'title' => '',
'email' => 'invalid-email',
'phone' => '3o4i234234',
'address' => 'dfdgdgdgerfsd',
'state' => 'Fkdfnskf',
'postal_code' => 'fsdf',
'country' => 'fsdfsd',
'latitude' => 'not-number',
'longitude' => 'not-number',
'outlet_delivery_coverage' => 'not-number',
'outlet_tax' => 'sdfsdfs',
'outlet_operating_days' => 'sfsfsd',
'outlet_operating_hours' => 'dsfsdfds',
'outlet_operating_hours_exceptions' => null,
'outlet_images' => null
];
// Fix: getVar() must return full array when called with no argument
$mockRequest->method('getVar')
->willReturnCallback(function ($key = null) use ($invalidData) {
if ($key === null) {
return $invalidData; // full array for validateData()
}
return $invalidData[$key] ?? null; // or specific key
});
// getJSON fallback (optional - may not be used in controller)
$mockRequest->method('getJSON')
->willReturn($invalidData);
// Inject our mocks
$this->setProtectedOrPrivateProperty($controller, 'validator', $validator);
$this->setProtectedOrPrivateProperty($controller, 'outlet', $this->mockOutlet);
$this->setProtectedOrPrivateProperty($controller, 'outletTax', $this->mockOutletTax);
$this->setProtectedOrPrivateProperty($controller, 'outletImage', $this->mockOutletImage);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingDays', $this->mockOutletOperatingDay);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingHours', $this->mockOutletOperatingHour);
$this->setProtectedOrPrivateProperty($controller, 'outletOperatingHoursExceptions', $this->mockOutletOperatingHoursException);
$this->setProtectedOrPrivateProperty($controller, 'settingTax', $this->mockSettingTax);
$this->setProtectedOrPrivateProperty($controller, 'request', $mockRequest);
$this->setProtectedOrPrivateProperty($controller, 'response', service('response'));
// Mock outlet exists
$this->mockOutlet->method('find')->with(1)->willReturn(['id' => 1]);
// Call the update method
$result = $controller->update(1);
// Assertions
$this->assertEquals(400, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertEquals('400', $data['status']);
// $this->assertArrayHasKey('message', $data);
// $this->assertEquals('The title field is required.', $data['message']);
}
public function testUpdateOutletNotFound()
{
$this->mockOutlet->method('find')->with(99)->willReturn(null);
$result = $this->controller->update(99);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertEquals(400, $data['status']);
$this->assertEquals('No outlet data found.', $data['result']);
}
}
+288
View File
@@ -0,0 +1,288 @@
<?php
namespace App\Tests\Controllers\Backend;
use App\Controllers\Backend\UserController;
use App\Models\User;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\HTTP\IncomingRequest;
class UserTest extends CIUnitTestCase
{
protected $mockUser;
protected $controller;
protected function setProtectedOrPrivateProperty($object, $property, $value)
{
$reflection = new \ReflectionObject($object);
while (!$reflection->hasProperty($property)) {
$reflection = $reflection->getParentClass();
if (!$reflection) {
throw new \Exception("Property {$property} does not exist");
}
}
$prop = $reflection->getProperty($property);
$prop->setAccessible(true);
$prop->setValue($object, $value);
}
protected function setUp(): void
{
parent::setUp();
$this->mockUser = $this->createMock(User::class);
$this->controller = new UserController();
$this->setProtectedOrPrivateProperty($this->controller, 'user', $this->mockUser);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $this->createMock(IncomingRequest::class));
$this->setProtectedOrPrivateProperty($this->controller, 'response', service('response'));
}
public function testIndexReturnsUsers()
{
$users = [
['id' => 1, 'username' => 'john', 'name' => 'John Doe'],
['id' => 2, 'username' => 'jane', 'name' => 'Jane Doe'],
];
$this->mockUser->method('findAll')->willReturn($users);
$result = $this->controller->index();
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertArrayHasKey('data', $data);
$this->assertEquals('success', $data['status']);
$this->assertEquals('User data retrieved successfully.', $data['message']);
$this->assertEquals($users, $data['data']);
}
public function testIndexReturnsNoUsers()
{
$this->mockUser->method('findAll')->willReturn([]);
$result = $this->controller->index();
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertArrayHasKey('data', $data);
$this->assertEquals('error', $data['status']);
$this->assertEquals('No user data found.', $data['message']);
$this->assertEquals(null, $data['data']);
}
public function testShowReturnsUser()
{
$user = ['id' => 1, 'username' => 'john', 'name' => 'John Doe'];
$this->mockUser->method('find')->with(1)->willReturn($user);
$result = $this->controller->show(1);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertArrayHasKey('data', $data);
$this->assertEquals('success', $data['status']);
$this->assertEquals('User retrieved successfully.', $data['message']);
$this->assertEquals($user, $data['data']);
}
public function testShowReturnsNotFound()
{
$this->mockUser->method('find')->with(99)->willReturn(null);
$result = $this->controller->show(99);
$this->assertEquals(404, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertEquals('error', $data['status']);
$this->assertEquals('User not found.', $data['message']);
}
public function testCreateValidationFails()
{
$controller = $this->getMockBuilder(UserController::class)
->onlyMethods(['validate'])
->enableOriginalConstructor()
->getMock();
$controller->method('validate')->willReturn(false);
$validator = $this->getMockBuilder(\CodeIgniter\Validation\Validation::class)
->disableOriginalConstructor()
->getMock();
$validator->method('getErrors')->willReturn(['username' => 'Required']);
$this->setProtectedOrPrivateProperty($controller, 'validator', $validator);
$this->setProtectedOrPrivateProperty($controller, 'user', $this->mockUser);
$this->setProtectedOrPrivateProperty($controller, 'request', $this->createMock(\CodeIgniter\HTTP\IncomingRequest::class));
$this->setProtectedOrPrivateProperty($controller, 'response', service('response'));
$result = $controller->create();
$this->assertEquals(422, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertArrayHasKey('error', $data);
$this->assertEquals('errors', $data['status']);
$this->assertEquals('Validation failed', $data['message']);
$this->assertArrayHasKey('username', $data['errors']);
}
public function testCreateSuccess()
{
$this->mockUser->method('insert')->willReturn(1);
$this->mockUser->method('find')->with(1)->willReturn([
'id' => 1, 'username' => 'john', 'name' => 'John Doe'
]);
$request = $this->createMock(IncomingRequest::class);
$request->method('getVar')->willReturnMap([
['username', 'john'],
['name', 'John Doe'],
['password_hash', 'password'],
['role', 'admin'],
['status', 'active'],
]);
$controller = $this->getMockBuilder(UserController::class)
->onlyMethods(['validate'])
->enableOriginalConstructor()
->getMock();
$controller->method('validate')->willReturn(true);
$this->setProtectedOrPrivateProperty($controller, 'request', $request);
$this->setProtectedOrPrivateProperty($controller, 'user', $this->mockUser);
$this->setProtectedOrPrivateProperty($controller, 'response', service('response'));
$result = $controller->create();
$this->assertEquals(201, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertArrayHasKey('data', $data);
$this->assertEquals('success', $data['status']);
$this->assertEquals('User created successfully.', $data['message']);
$this->assertEquals([
'id' => 1, 'username' => 'john', 'name' => 'John Doe'
], $data['data']);
}
public function testUpdateUserNotFound()
{
$this->mockUser->method('find')->with(99)->willReturn(null);
$result = $this->controller->update(99);
$this->assertEquals(404, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertEquals('error', $data['status']);
$this->assertEquals('User not found.', $data['message']);
}
public function testUpdateNoDataProvided()
{
$this->mockUser->method('find')->with(1)->willReturn(['id' => 1]);
$request = $this->createMock(IncomingRequest::class);
$request->method('getRawInput')->willReturn([]);
$request->method('getPost')->willReturn([]);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $request);
$result = $this->controller->update(1);
$this->assertEquals(400, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertEquals('error', $data['status']);
$this->assertEquals('No data provided to update.', $data['message']);
}
public function testUpdateSuccess()
{
$this->mockUser->method('find')->with(1)->willReturn(['id' => 1]);
$this->mockUser->expects($this->once())->method('update')->with(1, ['username' => 'john']);
$this->mockUser->method('find')->with(1)->willReturn(['id' => 1, 'username' => 'john']);
$request = $this->createMock(IncomingRequest::class);
$request->method('getRawInput')->willReturn(['username' => 'john']);
$request->method('getPost')->willReturn([]);
$this->setProtectedOrPrivateProperty($this->controller, 'request', $request);
$result = $this->controller->update(1);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertArrayHasKey('data', $data);
$this->assertEquals('success', $data['status']);
$this->assertEquals('User updated successfully.', $data['message']);
$this->assertEquals(['id' => 1, 'username' => 'john'], $data['data']);
}
public function testDeleteUserNotFound()
{
$this->mockUser->method('find')->with(99)->willReturn(null);
$result = $this->controller->delete(99);
$this->assertEquals(404, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertEquals('error', $data['status']);
$this->assertEquals('User not found.', $data['message']);
}
public function testDeleteSuccess()
{
$this->mockUser->method('find')->with(1)->willReturn(['id' => 1]);
$this->mockUser->expects($this->once())->method('delete')->with(1);
$result = $this->controller->delete(1);
$this->assertEquals(200, $result->getStatusCode());
$data = json_decode($result->getBody(), true);
$this->assertIsArray($data);
$this->assertArrayHasKey('status', $data);
$this->assertArrayHasKey('message', $data);
$this->assertEquals('success', $data['status']);
$this->assertEquals('User deleted successfully.', $data['message']);
}
}