feat: edit history visibility settings

This commit is contained in:
2026-03-24 18:04:27 +02:00
parent b8a5ae5e04
commit a93f1fee1e
22 changed files with 608 additions and 31 deletions

View File

@@ -81,6 +81,93 @@ class AdminControllerTest extends TestCase {
);
}
// ── Settings tests ───────────────────────────────────────────────
public function testGetSettingsReturnsAllSettings(): void {
$allSettings = [
'title' => 'My Forum',
'subtitle' => 'Welcome!',
'allow_guest_access' => false,
'is_initialized' => true,
'public_edit_history' => true,
'allow_edit_history_user_override' => false,
];
$this->settingsService->expects($this->once())
->method('getAllSettings')
->willReturn($allSettings);
$response = $this->controller->getSettings();
$this->assertEquals(200, $response->getStatus());
$this->assertEquals($allSettings, $response->getData());
}
public function testUpdateSettingsPassesAllFieldsToService(): void {
$expectedUpdate = [
AdminSettingsService::SETTING_TITLE => 'New Title',
AdminSettingsService::SETTING_SUBTITLE => 'New Subtitle',
AdminSettingsService::SETTING_ALLOW_GUEST_ACCESS => true,
AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY => false,
AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE => true,
];
$this->settingsService->expects($this->once())
->method('updateSettings')
->with($expectedUpdate)
->willReturn(array_merge($expectedUpdate, ['is_initialized' => true]));
$response = $this->controller->updateSettings(
'New Title',
'New Subtitle',
true,
false,
true,
);
$this->assertEquals(200, $response->getStatus());
}
public function testUpdateSettingsOmitsNullValues(): void {
$this->settingsService->expects($this->once())
->method('updateSettings')
->with($this->callback(function (array $settings) {
// Only public_edit_history and allow_edit_history_user_override should be present
return count($settings) === 2
&& array_key_exists(AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY, $settings)
&& array_key_exists(AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE, $settings)
&& $settings[AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY] === true
&& $settings[AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE] === true;
}))
->willReturn([]);
$response = $this->controller->updateSettings(
null,
null,
null,
true,
true,
);
$this->assertEquals(200, $response->getStatus());
}
public function testUpdateSettingsHandlesException(): void {
$this->settingsService->expects($this->once())
->method('updateSettings')
->willThrowException(new \Exception('DB error'));
$this->logger->expects($this->once())
->method('error')
->with($this->stringContains('Error updating settings'));
$response = $this->controller->updateSettings('Title');
$this->assertEquals(500, $response->getStatus());
}
// ── Dashboard tests ─────────────────────────────────────────────
public function testDashboardEnrichesContributorsWithDisplayNames(): void {
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('admin');

View File

@@ -6,6 +6,10 @@ namespace OCA\Forum\Tests\Controller;
use OCA\Forum\AppInfo\Application;
use OCA\Forum\Controller\PostHistoryController;
use OCA\Forum\Db\Post;
use OCA\Forum\Db\PostMapper;
use OCA\Forum\Service\EditHistoryVisibilityService;
use OCA\Forum\Service\PermissionService;
use OCA\Forum\Service\PostHistoryService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
@@ -18,6 +22,12 @@ class PostHistoryControllerTest extends TestCase {
private PostHistoryController $controller;
/** @var PostHistoryService&MockObject */
private PostHistoryService $postHistoryService;
/** @var PostMapper&MockObject */
private PostMapper $postMapper;
/** @var PermissionService&MockObject */
private PermissionService $permissionService;
/** @var EditHistoryVisibilityService&MockObject */
private EditHistoryVisibilityService $editHistoryVisibilityService;
/** @var LoggerInterface&MockObject */
private LoggerInterface $logger;
/** @var IRequest&MockObject */
@@ -26,18 +36,37 @@ class PostHistoryControllerTest extends TestCase {
protected function setUp(): void {
$this->request = $this->createMock(IRequest::class);
$this->postHistoryService = $this->createMock(PostHistoryService::class);
$this->postMapper = $this->createMock(PostMapper::class);
$this->permissionService = $this->createMock(PermissionService::class);
$this->editHistoryVisibilityService = $this->createMock(EditHistoryVisibilityService::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->controller = new PostHistoryController(
Application::APP_ID,
$this->request,
$this->postHistoryService,
$this->logger
$this->postMapper,
$this->permissionService,
$this->editHistoryVisibilityService,
$this->logger,
'user1',
);
}
private function setUpPermissionCheck(int $postId, string $authorId, int $categoryId, bool $canView): void {
$post = new Post();
$post->setId($postId);
$post->setAuthorId($authorId);
$this->postMapper->method('find')->with($postId)->willReturn($post);
$this->permissionService->method('getCategoryIdFromPost')->with($postId)->willReturn($categoryId);
$this->editHistoryVisibilityService->method('canViewEditHistory')
->with('user1', $authorId, $categoryId)
->willReturn($canView);
}
public function testGetHistoryReturnsHistorySuccessfully(): void {
$postId = 1;
$this->setUpPermissionCheck($postId, 'user1', 10, true);
$historyData = [
'current' => [
@@ -83,11 +112,19 @@ class PostHistoryControllerTest extends TestCase {
$this->assertCount(2, $data['history']);
}
public function testGetHistoryReturnsForbiddenWhenNotAllowed(): void {
$postId = 1;
$this->setUpPermissionCheck($postId, 'other_user', 10, false);
$response = $this->controller->getHistory($postId);
$this->assertEquals(Http::STATUS_FORBIDDEN, $response->getStatus());
}
public function testGetHistoryReturnsNotFoundWhenPostDoesNotExist(): void {
$postId = 999;
$this->postHistoryService->expects($this->once())
->method('getPostHistory')
$this->postMapper->method('find')
->with($postId)
->willThrowException(new DoesNotExistException('Post not found'));
@@ -99,6 +136,7 @@ class PostHistoryControllerTest extends TestCase {
public function testGetHistoryReturnsEmptyHistoryForNeverEditedPost(): void {
$postId = 1;
$this->setUpPermissionCheck($postId, 'user1', 10, true);
$historyData = [
'current' => [
@@ -127,6 +165,7 @@ class PostHistoryControllerTest extends TestCase {
public function testGetHistoryHandlesException(): void {
$postId = 1;
$this->setUpPermissionCheck($postId, 'user1', 10, true);
$this->postHistoryService->expects($this->once())
->method('getPostHistory')
@@ -145,6 +184,7 @@ class PostHistoryControllerTest extends TestCase {
public function testGetHistoryShowsModeratorEdits(): void {
$postId = 1;
$this->setUpPermissionCheck($postId, 'user1', 10, true);
$historyData = [
'current' => [

View File

@@ -49,12 +49,14 @@ class AdminSettingsServiceTest extends TestCase {
};
});
$this->config->expects($this->exactly(2))
$this->config->expects($this->exactly(4))
->method('getAppValueBool')
->willReturnCallback(function ($key, $default, $lazy) {
return match ($key) {
AdminSettingsService::SETTING_ALLOW_GUEST_ACCESS => true,
AdminSettingsService::SETTING_IS_INITIALIZED => false,
AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY => true,
AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE => false,
default => $default,
};
});
@@ -62,11 +64,13 @@ class AdminSettingsServiceTest extends TestCase {
$result = $this->service->getAllSettings();
$this->assertIsArray($result);
$this->assertCount(4, $result);
$this->assertCount(6, $result);
$this->assertEquals('My Forum', $result[AdminSettingsService::SETTING_TITLE]);
$this->assertEquals('Welcome!', $result[AdminSettingsService::SETTING_SUBTITLE]);
$this->assertTrue($result[AdminSettingsService::SETTING_ALLOW_GUEST_ACCESS]);
$this->assertFalse($result[AdminSettingsService::SETTING_IS_INITIALIZED]);
$this->assertTrue($result[AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY]);
$this->assertFalse($result[AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE]);
}
public function testGetSettingReturnsCorrectStringValue(): void {
@@ -183,12 +187,14 @@ class AdminSettingsServiceTest extends TestCase {
};
});
$this->config->expects($this->exactly(2))
$this->config->expects($this->exactly(4))
->method('getAppValueBool')
->willReturnCallback(function ($key, $default, $lazy) {
return match ($key) {
AdminSettingsService::SETTING_ALLOW_GUEST_ACCESS => true,
AdminSettingsService::SETTING_IS_INITIALIZED => false,
AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY => true,
AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE => false,
default => $default,
};
});
@@ -232,12 +238,14 @@ class AdminSettingsServiceTest extends TestCase {
};
});
$this->config->expects($this->exactly(2))
$this->config->expects($this->exactly(4))
->method('getAppValueBool')
->willReturnCallback(function ($key, $default, $lazy) {
return match ($key) {
AdminSettingsService::SETTING_ALLOW_GUEST_ACCESS => false,
AdminSettingsService::SETTING_IS_INITIALIZED => false,
AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY => true,
AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE => false,
default => $default,
};
});

View File

@@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace OCA\Forum\Tests\Service;
use OCA\Forum\Service\AdminSettingsService;
use OCA\Forum\Service\EditHistoryVisibilityService;
use OCA\Forum\Service\PermissionService;
use OCA\Forum\Service\UserPreferencesService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class EditHistoryVisibilityServiceTest extends TestCase {
private EditHistoryVisibilityService $service;
/** @var AdminSettingsService&MockObject */
private AdminSettingsService $adminSettingsService;
/** @var UserPreferencesService&MockObject */
private UserPreferencesService $userPreferencesService;
/** @var PermissionService&MockObject */
private PermissionService $permissionService;
private const CATEGORY_ID = 10;
protected function setUp(): void {
$this->adminSettingsService = $this->createMock(AdminSettingsService::class);
$this->userPreferencesService = $this->createMock(UserPreferencesService::class);
$this->permissionService = $this->createMock(PermissionService::class);
$this->service = new EditHistoryVisibilityService(
$this->adminSettingsService,
$this->userPreferencesService,
$this->permissionService,
);
}
public function testOwnerCanAlwaysSeeOwnHistory(): void {
// No admin settings or permissions needed — owner always sees own history
$this->assertTrue(
$this->service->canViewEditHistory('user1', 'user1', self::CATEGORY_ID)
);
}
public function testModeratorCanAlwaysSeeHistory(): void {
$this->permissionService->method('hasCategoryPermission')
->with('mod1', self::CATEGORY_ID, 'canModerate')
->willReturn(true);
$this->permissionService->method('hasAdminOrModeratorRole')
->willReturn(false);
$this->assertTrue(
$this->service->canViewEditHistory('mod1', 'user1', self::CATEGORY_ID)
);
}
public function testAdminRoleCanAlwaysSeeHistory(): void {
$this->permissionService->method('hasCategoryPermission')
->willReturn(false);
$this->permissionService->method('hasAdminOrModeratorRole')
->with('admin1')
->willReturn(true);
$this->assertTrue(
$this->service->canViewEditHistory('admin1', 'user1', self::CATEGORY_ID)
);
}
public function testPublicEditHistoryOffBlocksOtherUsers(): void {
$this->permissionService->method('hasCategoryPermission')->willReturn(false);
$this->permissionService->method('hasAdminOrModeratorRole')->willReturn(false);
$this->adminSettingsService->method('getSetting')
->with(AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY)
->willReturn(false);
$this->assertFalse(
$this->service->canViewEditHistory('viewer1', 'author1', self::CATEGORY_ID)
);
}
public function testPublicEditHistoryOnAllowsOtherUsers(): void {
$this->permissionService->method('hasCategoryPermission')->willReturn(false);
$this->permissionService->method('hasAdminOrModeratorRole')->willReturn(false);
$this->adminSettingsService->method('getSetting')
->willReturnMap([
[AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY, true],
[AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE, false],
]);
$this->assertTrue(
$this->service->canViewEditHistory('viewer1', 'author1', self::CATEGORY_ID)
);
}
public function testUserOverrideHidesHistoryFromOthers(): void {
$this->permissionService->method('hasCategoryPermission')->willReturn(false);
$this->permissionService->method('hasAdminOrModeratorRole')->willReturn(false);
$this->adminSettingsService->method('getSetting')
->willReturnMap([
[AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY, true],
[AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE, true],
]);
$this->userPreferencesService->method('getPreference')
->with('author1', UserPreferencesService::PREF_HIDE_EDIT_HISTORY)
->willReturn(true);
$this->assertFalse(
$this->service->canViewEditHistory('viewer1', 'author1', self::CATEGORY_ID)
);
}
public function testUserOverrideDoesNotAffectOwner(): void {
// Owner always sees own history regardless of override setting
$this->assertTrue(
$this->service->canViewEditHistory('author1', 'author1', self::CATEGORY_ID)
);
}
public function testUserOverrideDoesNotAffectModerator(): void {
$this->permissionService->method('hasCategoryPermission')
->with('mod1', self::CATEGORY_ID, 'canModerate')
->willReturn(true);
$this->permissionService->method('hasAdminOrModeratorRole')
->willReturn(false);
// Moderator can see history even when author has hidden it
$this->assertTrue(
$this->service->canViewEditHistory('mod1', 'author1', self::CATEGORY_ID)
);
}
public function testGuestCannotSeeHistoryWhenPublicOff(): void {
$this->adminSettingsService->method('getSetting')
->with(AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY)
->willReturn(false);
$this->assertFalse(
$this->service->canViewEditHistory(null, 'author1', self::CATEGORY_ID)
);
}
public function testGuestCanSeeHistoryWhenPublicOn(): void {
$this->adminSettingsService->method('getSetting')
->willReturnMap([
[AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY, true],
[AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE, false],
]);
$this->assertTrue(
$this->service->canViewEditHistory(null, 'author1', self::CATEGORY_ID)
);
}
public function testUserOverrideDisabledDoesNotHideHistory(): void {
$this->permissionService->method('hasCategoryPermission')->willReturn(false);
$this->permissionService->method('hasAdminOrModeratorRole')->willReturn(false);
$this->adminSettingsService->method('getSetting')
->willReturnMap([
[AdminSettingsService::SETTING_PUBLIC_EDIT_HISTORY, true],
[AdminSettingsService::SETTING_ALLOW_EDIT_HISTORY_USER_OVERRIDE, false],
]);
// Even if user has hide_edit_history pref set, when override is disabled, history is visible
$this->assertTrue(
$this->service->canViewEditHistory('viewer1', 'author1', self::CATEGORY_ID)
);
}
}

View File

@@ -42,7 +42,7 @@ class UserPreferencesServiceTest extends TestCase {
$userId = 'user1';
// Only config-based preferences (signature is from forum_users)
$this->config->expects($this->exactly(3))
$this->config->expects($this->exactly(4))
->method('getUserValue')
->willReturnCallback(function ($uid, $appId, $key, $default) use ($userId) {
$this->assertEquals($userId, $uid);
@@ -52,6 +52,7 @@ class UserPreferencesServiceTest extends TestCase {
UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS => 'true',
UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS => 'false',
UserPreferencesService::PREF_UPLOAD_DIRECTORY => 'Forum',
UserPreferencesService::PREF_HIDE_EDIT_HISTORY => 'false',
default => $default,
};
});
@@ -59,11 +60,12 @@ class UserPreferencesServiceTest extends TestCase {
$result = $this->service->getAllPreferences($userId);
$this->assertIsArray($result);
$this->assertCount(4, $result);
$this->assertCount(5, $result);
$this->assertTrue($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS]);
$this->assertFalse($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS]);
$this->assertEquals('Forum', $result[UserPreferencesService::PREF_UPLOAD_DIRECTORY]);
$this->assertEquals('', $result[UserPreferencesService::PREF_SIGNATURE]);
$this->assertFalse($result[UserPreferencesService::PREF_HIDE_EDIT_HISTORY]);
}
public function testGetPreferenceReturnsCorrectValue(): void {
@@ -146,7 +148,7 @@ class UserPreferencesServiceTest extends TestCase {
}
});
$this->config->expects($this->exactly(3))
$this->config->expects($this->exactly(4))
->method('getUserValue')
->willReturnCallback(function ($uid, $appId, $key, $default) use ($userId) {
$this->assertEquals($userId, $uid);
@@ -156,6 +158,7 @@ class UserPreferencesServiceTest extends TestCase {
UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS => 'false',
UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS => 'false',
UserPreferencesService::PREF_UPLOAD_DIRECTORY => 'Documents',
UserPreferencesService::PREF_HIDE_EDIT_HISTORY => 'false',
default => $default,
};
});
@@ -163,11 +166,12 @@ class UserPreferencesServiceTest extends TestCase {
$result = $this->service->updatePreferences($userId, $preferences);
$this->assertIsArray($result);
$this->assertCount(4, $result);
$this->assertCount(5, $result);
$this->assertFalse($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_CREATED_THREADS]);
$this->assertFalse($result[UserPreferencesService::PREF_AUTO_SUBSCRIBE_REPLIED_THREADS]);
$this->assertEquals('Documents', $result[UserPreferencesService::PREF_UPLOAD_DIRECTORY]);
$this->assertEquals('', $result[UserPreferencesService::PREF_SIGNATURE]);
$this->assertFalse($result[UserPreferencesService::PREF_HIDE_EDIT_HISTORY]);
}
public function testUpdatePreferencesThrowsExceptionForInvalidKey(): void {