445 lines
19 KiB
PHP
445 lines
19 KiB
PHP
<?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']);
|
|
}
|
|
|
|
} |