mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-19 00:28:47 +02:00
✅ Add tests for lock acquisition functionality (#865)
This commit is contained in:
@@ -1470,4 +1470,19 @@ abstract class ItopDataTestCase extends ItopTestCase
|
||||
@chmod($sConfigPath, 0440);
|
||||
@unlink($this->sConfigTmpBackupFile);
|
||||
}
|
||||
protected function AddLoginModeAndSaveConfiguration(string $sLoginMode): void
|
||||
{
|
||||
$aAllowedLoginTypes = $this->oiTopConfig->GetAllowedLoginTypes();
|
||||
if (!in_array($sLoginMode, $aAllowedLoginTypes)) {
|
||||
$aAllowedLoginTypes[] = $sLoginMode;
|
||||
$this->oiTopConfig->SetAllowedLoginTypes($aAllowedLoginTypes);
|
||||
$this->SaveItopConfFile();
|
||||
}
|
||||
}
|
||||
protected function SaveItopConfFile(): void
|
||||
{
|
||||
@chmod($this->oiTopConfig->GetLoadedFile(), 0770);
|
||||
$this->oiTopConfig->WriteToFile();
|
||||
@chmod($this->oiTopConfig->GetLoadedFile(), 0440);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,7 +668,7 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, $sUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_POST, $aCurlOptions[CURLOPT_POST] ?? 1);// set post data to true
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
// Force disable of certificate check as most of dev / test env have a self-signed certificate
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
@@ -676,7 +676,7 @@ abstract class ItopTestCase extends KernelTestCase
|
||||
curl_setopt_array($ch, $aCurlOptions);
|
||||
if ($this->IsArrayOfArray($aPostFields)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($aPostFields));
|
||||
} else {
|
||||
} elseif (!empty($aPostFields)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $aPostFields);
|
||||
}
|
||||
|
||||
|
||||
180
tests/php-unit-tests/unitary-tests/pages/AjaxRenderTest.php
Normal file
180
tests/php-unit-tests/unitary-tests/pages/AjaxRenderTest.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Pages;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use Dict;
|
||||
use UserLocal;
|
||||
use UserRequest;
|
||||
|
||||
class AjaxRenderTest extends ItopDataTestCase
|
||||
{
|
||||
public const USE_TRANSACTION = false;
|
||||
public const AUTHENTICATION_PASSWORD = "tagada-Secret,007";
|
||||
|
||||
private static string $sLogin;
|
||||
private static int $iTicketId;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->BackupConfiguration();
|
||||
$this->oiTopConfig->Set('log_level_min', 'Error');
|
||||
$this->oiTopConfig->Set('login_debug', true);
|
||||
|
||||
$this->CreateTestOrganization();
|
||||
|
||||
// Add URL authentication mode
|
||||
$this->AddLoginModeAndSaveConfiguration('url');
|
||||
|
||||
// Create ticket
|
||||
$description = date('dmY H:i:s');
|
||||
$oTicket = $this->createObject('UserRequest', [
|
||||
'org_id' => $this->getTestOrgId(),
|
||||
"title" => "Houston, got a problem",
|
||||
"description" => $description,
|
||||
]);
|
||||
self::$iTicketId = $oTicket->GetKey();
|
||||
}
|
||||
|
||||
// Test that if a user with the right permissions tries to acquire the lock on a ticket, it succeeds and returns the correct success message
|
||||
public function testAcquireLockSuccess(): void
|
||||
{
|
||||
$sOutput = $this->CreateSupportAgentUserAndAcquireLock();
|
||||
$this->assertStringContainsString('"success":true', $sOutput);
|
||||
}
|
||||
|
||||
// Test that if a user tries to acquire the lock on an object that does not exist, it fails and logs the correct error message
|
||||
public function testAcquireLockFailsIfObjectDoesNotExist(): void
|
||||
{
|
||||
// Create a user with Support Agent Profile
|
||||
$this->CreateUserWithProfile(self::$aURP_Profiles['Support Agent']);
|
||||
|
||||
// Try to acquire the lock on a non-existent object
|
||||
$sOutput = $this->AcquireLockAsUser(self::$sLogin, 99999999);
|
||||
|
||||
// The output should indicate a fatal error because we hide the existence of the object when it does not exist or is not accessible by the user
|
||||
$this->assertEquals(Dict::S('UI:PageTitle:FatalError'), $sOutput);
|
||||
|
||||
// Check that the error log contains the expected error message about the object not existing
|
||||
$sLastErrorLogLines = $this->GetErrorLogLastLines(APPROOT.'log/error.log', 10);
|
||||
$this->assertStringContainsString(Dict::S('UI:ObjectDoesNotExist'), $sLastErrorLogLines);
|
||||
}
|
||||
|
||||
// Test that if a user tries to acquire the lock on an object for which they don't have modification rights, it fails and logs the correct error message
|
||||
public function testAcquireLockFailsIfUserHasNoModifyRights(): void
|
||||
{
|
||||
// Create a user with a profile without modification rights on UserRequest
|
||||
$this->CreateUserWithProfile(self::$aURP_Profiles['Configuration Manager']);
|
||||
|
||||
// Try to acquire the lock on the ticket
|
||||
$sOutput = $this->AcquireLockAsUser(self::$sLogin, self::$iTicketId);
|
||||
|
||||
// The output should indicate a fatal error because we hide the existence of the object when it does not exist or is not accessible by the user
|
||||
$this->assertEquals(Dict::S('UI:PageTitle:FatalError'), $sOutput);
|
||||
|
||||
// The user should not have the rights to acquire the lock, and an error should be logged
|
||||
$sLastErrorLogLines = $this->GetErrorLogLastLines(APPROOT.'log/error.log', 10);
|
||||
$this->assertStringContainsString(Dict::S('UI:ObjectDoesNotExist'), $sLastErrorLogLines);
|
||||
}
|
||||
|
||||
// Test that if a user tries to acquire the lock on an object that belongs to another organization, it fails and logs the correct error message
|
||||
public function testAcquireLockFailsIfObjectInOtherOrg(): void
|
||||
{
|
||||
// Create an organization and a ticket in this organization
|
||||
$iOtherOrgId = $this->createObject('Organization', ['name' => 'OtherOrg'])->GetKey();
|
||||
$oTicket = $this->createObject('UserRequest', [
|
||||
'org_id' => $iOtherOrgId,
|
||||
'title' => 'Ticket autre org',
|
||||
'description' => 'Test',
|
||||
]);
|
||||
|
||||
// Create a user who only has access to the main test organization
|
||||
$oUser = $this->CreateUserWithProfile(self::$aURP_Profiles['Support Agent']);
|
||||
$oAllowedOrgList = $oUser->Get('allowed_org_list');
|
||||
$oUserOrg = \MetaModel::NewObject('URP_UserOrg', ['allowed_org_id' => $this->getTestOrgId()]);
|
||||
$oAllowedOrgList->AddItem($oUserOrg);
|
||||
$oUser->Set('allowed_org_list', $oAllowedOrgList);
|
||||
$oUser->DBWrite();
|
||||
|
||||
// Try to acquire the lock on the ticket of the other organization
|
||||
$sOutput = $this->AcquireLockAsUser(self::$sLogin, $oTicket->GetKey());
|
||||
|
||||
// The output should indicate a fatal error because we hide the existence of the object when it does not exist or is not accessible by the user
|
||||
$this->assertEquals(Dict::S('UI:PageTitle:FatalError'), $sOutput);
|
||||
|
||||
// The user should not have access to the ticket of the other organization, so an error should be logged
|
||||
$sLastErrorLogLines = $this->GetErrorLogLastLines(APPROOT.'log/error.log', 10);
|
||||
$this->assertStringContainsString(Dict::S('UI:ObjectDoesNotExist'), $sLastErrorLogLines);
|
||||
}
|
||||
|
||||
// Test that if a user has already acquired the lock on an object, another user cannot acquire it and gets the correct error message
|
||||
public function testAcquireLockFailsIfAlreadyLockedByAnotherUser(): void
|
||||
{
|
||||
// First, acquire the lock with a user (User A)
|
||||
$this->CreateSupportAgentUserAndAcquireLock();
|
||||
$sUserALogin = self::$sLogin;
|
||||
|
||||
// Create a second user (User B) who tries to acquire the lock
|
||||
$sOutput = $this->CreateSupportAgentUserAndAcquireLock();
|
||||
|
||||
// The second user should not be able to acquire the lock, and the output should contain the correct error message indicating that the object is already locked by User A
|
||||
$this->assertStringContainsString('"success":false', $sOutput);
|
||||
$this->assertStringContainsString('"message":"'.Dict::Format('UI:CurrentObjectIsSoftLockedBy_User', $sUserALogin).'"', $sOutput);
|
||||
}
|
||||
|
||||
// Helper method to create a user with Support Agent profile and acquire the lock on the ticket
|
||||
private function CreateSupportAgentUserAndAcquireLock(): string
|
||||
{
|
||||
// Create a user with Support Agent Profile
|
||||
$this->CreateUserWithProfile(self::$aURP_Profiles['Support Agent']);
|
||||
|
||||
return $this->AcquireLockAsUser(self::$sLogin, self::$iTicketId);
|
||||
}
|
||||
|
||||
// Helper method to create a user with a specific profile
|
||||
private function CreateUserWithProfile(int $iProfileId): UserLocal
|
||||
{
|
||||
self::$sLogin = uniqid('AjaxRenderTest');
|
||||
return $this->CreateContactlessUser(self::$sLogin, $iProfileId, self::AUTHENTICATION_PASSWORD);
|
||||
}
|
||||
|
||||
// Helper method to acquire the lock on a ticket as a specific user
|
||||
private function AcquireLockAsUser(string $sLogin, int $iTicketId): string
|
||||
{
|
||||
$aGetFields = [
|
||||
'operation' => 'acquire_lock',
|
||||
'auth_user' => $sLogin,
|
||||
'auth_pwd' => self::AUTHENTICATION_PASSWORD,
|
||||
'obj_class' => UserRequest::class,
|
||||
'obj_key' => $iTicketId,
|
||||
];
|
||||
|
||||
return $this->CallItopUri(
|
||||
"pages/ajax.render.php?".http_build_query($aGetFields),
|
||||
[],
|
||||
[
|
||||
CURLOPT_HTTPHEADER => ['X-Combodo-Ajax:1'],
|
||||
CURLOPT_POST => 0,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Returns the last lines of the error log containing only errors (Error level)
|
||||
private function GetErrorLogLastLines(string $sErrorLogPath, int $iLineNumbers = 1): string
|
||||
{
|
||||
if (!file_exists($sErrorLogPath)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$aLines = file($sErrorLogPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
// Keep only lines containing '| Error |'
|
||||
$aErrorLines = array_filter($aLines, function ($line) {
|
||||
return preg_match('/\|\s*Error\s*\|/', $line);
|
||||
});
|
||||
|
||||
// Return the last requested lines
|
||||
return implode("\n", array_slice($aErrorLines, -$iLineNumbers));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user