123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- <?php
- declare(strict_types=1);
- namespace Test\Settings;
- use OC\AppFramework\Bootstrap\Coordinator;
- use OC\AppFramework\Bootstrap\RegistrationContext;
- use OC\AppFramework\Bootstrap\ServiceRegistration;
- use OC\Settings\DeclarativeManager;
- use OCP\EventDispatcher\IEventDispatcher;
- use OCP\IAppConfig;
- use OCP\IConfig;
- use OCP\IGroupManager;
- use OCP\IUser;
- use OCP\Settings\DeclarativeSettingsTypes;
- use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
- use OCP\Settings\IDeclarativeManager;
- use OCP\Settings\IDeclarativeSettingsForm;
- use OCP\Settings\IDeclarativeSettingsFormWithHandlers;
- use PHPUnit\Framework\MockObject\MockObject;
- use Psr\Log\LoggerInterface;
- use Test\TestCase;
- class DeclarativeManagerTest extends TestCase {
-
- private $declarativeManager;
-
- private $eventDispatcher;
-
- private $groupManager;
-
- private $coordinator;
-
- private $config;
-
- private $appConfig;
-
- private $logger;
-
- private $user;
-
- private $adminUser;
- private IDeclarativeSettingsForm&MockObject $closureForm;
- public const validSchemaAllFields = [
- 'id' => 'test_form_1',
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN,
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL,
- 'title' => 'Test declarative settings',
- 'description' => 'These fields are rendered dynamically from declarative schema',
- 'doc_url' => '',
- 'fields' => [
- [
- 'id' => 'test_field_7',
- 'title' => 'Multi-selection',
- 'description' => 'Select some option setting',
- 'type' => DeclarativeSettingsTypes::MULTI_SELECT,
- 'options' => ['foo', 'bar', 'baz'],
- 'placeholder' => 'Select some multiple options',
- 'default' => ['foo', 'bar'],
- ],
- [
- 'id' => 'some_real_setting',
- 'title' => 'Select single option',
- 'description' => 'Single option radio buttons',
- 'type' => DeclarativeSettingsTypes::RADIO,
- 'placeholder' => 'Select single option, test interval',
- 'default' => '40m',
- 'options' => [
- [
- 'name' => 'Each 40 minutes',
- 'value' => '40m'
- ],
- [
- 'name' => 'Each 60 minutes',
- 'value' => '60m'
- ],
- [
- 'name' => 'Each 120 minutes',
- 'value' => '120m'
- ],
- [
- 'name' => 'Each day',
- 'value' => 60 * 24 . 'm'
- ],
- ],
- ],
- [
- 'id' => 'test_field_1',
- 'title' => 'Default text field',
- 'description' => 'Set some simple text setting',
- 'type' => DeclarativeSettingsTypes::TEXT,
- 'placeholder' => 'Enter text setting',
- 'default' => 'foo',
- ],
- [
- 'id' => 'test_field_1_1',
- 'title' => 'Email field',
- 'description' => 'Set email config',
- 'type' => DeclarativeSettingsTypes::EMAIL,
- 'placeholder' => 'Enter email',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_1_2',
- 'title' => 'Tel field',
- 'description' => 'Set tel config',
- 'type' => DeclarativeSettingsTypes::TEL,
- 'placeholder' => 'Enter your tel',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_1_3',
- 'title' => 'Url (website) field',
- 'description' => 'Set url config',
- 'type' => 'url',
- 'placeholder' => 'Enter url',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_1_4',
- 'title' => 'Number field',
- 'description' => 'Set number config',
- 'type' => DeclarativeSettingsTypes::NUMBER,
- 'placeholder' => 'Enter number value',
- 'default' => 0,
- ],
- [
- 'id' => 'test_field_2',
- 'title' => 'Password',
- 'description' => 'Set some secure value setting',
- 'type' => 'password',
- 'placeholder' => 'Set secure value',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_3',
- 'title' => 'Selection',
- 'description' => 'Select some option setting',
- 'type' => DeclarativeSettingsTypes::SELECT,
- 'options' => ['foo', 'bar', 'baz'],
- 'placeholder' => 'Select some option setting',
- 'default' => 'foo',
- ],
- [
- 'id' => 'test_field_4',
- 'title' => 'Toggle something',
- 'description' => 'Select checkbox option setting',
- 'type' => DeclarativeSettingsTypes::CHECKBOX,
- 'label' => 'Verify something if enabled',
- 'default' => false,
- ],
- [
- 'id' => 'test_field_5',
- 'title' => 'Multiple checkbox toggles, describing one setting, checked options are saved as an JSON object {foo: true, bar: false}',
- 'description' => 'Select checkbox option setting',
- 'type' => DeclarativeSettingsTypes::MULTI_CHECKBOX,
- 'default' => ['foo' => true, 'bar' => true],
- 'options' => [
- [
- 'name' => 'Foo',
- 'value' => 'foo',
- ],
- [
- 'name' => 'Bar',
- 'value' => 'bar',
- ],
- [
- 'name' => 'Baz',
- 'value' => 'baz',
- ],
- [
- 'name' => 'Qux',
- 'value' => 'qux',
- ],
- ],
- ],
- [
- 'id' => 'test_field_6',
- 'title' => 'Radio toggles, describing one setting like single select',
- 'description' => 'Select radio option setting',
- 'type' => DeclarativeSettingsTypes::RADIO,
- 'label' => 'Select single toggle',
- 'default' => 'foo',
- 'options' => [
- [
- 'name' => 'First radio',
- 'value' => 'foo'
- ],
- [
- 'name' => 'Second radio',
- 'value' => 'bar'
- ],
- [
- 'name' => 'Second radio',
- 'value' => 'baz'
- ],
- ],
- ],
- ],
- ];
- public static bool $testSetInternalValueAfterChange = false;
- protected function setUp(): void {
- parent::setUp();
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->coordinator = $this->createMock(Coordinator::class);
- $this->config = $this->createMock(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->declarativeManager = new DeclarativeManager(
- $this->eventDispatcher,
- $this->groupManager,
- $this->coordinator,
- $this->config,
- $this->appConfig,
- $this->logger
- );
- $this->user = $this->createMock(IUser::class);
- $this->user->expects($this->any())
- ->method('getUID')
- ->willReturn('test_user');
- $this->adminUser = $this->createMock(IUser::class);
- $this->adminUser->expects($this->any())
- ->method('getUID')
- ->willReturn('admin_test_user');
- $this->groupManager->expects($this->any())
- ->method('isAdmin')
- ->willReturnCallback(function ($userId) {
- return $userId === 'admin_test_user';
- });
- }
- public function testRegisterSchema(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- }
-
- public function testRegisterDuplicateSchema(): void {
- $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields);
- $this->expectException(\Exception::class);
- $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields);
- }
-
- public function testRegisterSchemaWithDuplicateFields(): void {
-
- $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields);
-
- $this->expectException(\Exception::class);
- $schema = self::validSchemaAllFields;
- $schema['id'] = 'test_form_2';
- $this->declarativeManager->registerSchema('testing', $schema);
- }
- public function testRegisterMultipleSchemasAndDuplicate(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
-
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- $app = 'testing2';
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
-
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- $app = 'testing';
- $this->expectException(\Exception::class);
- $this->declarativeManager->registerSchema($app, $schema);
- $schemaDuplicateFields = self::validSchemaAllFields;
- $schemaDuplicateFields['id'] = 'test_form_2';
- $this->declarativeManager->registerSchema($app, $schemaDuplicateFields);
-
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schemaDuplicateFields['section_type'], $schemaDuplicateFields['section_id']);
- $this->assertFalse(isset($formIds[$app]) && in_array($schemaDuplicateFields['id'], $formIds[$app]));
- }
-
- public function testValidateSchema(bool $expected, bool $expectException, string $app, array $schema): void {
- if ($expectException) {
- $this->expectException(\Exception::class);
- }
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertEquals($expected, isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- }
- public static function dataValidateSchema(): array {
- return [
- 'valid schema with all supported fields' => [
- true,
- false,
- 'testing',
- self::validSchemaAllFields,
- ],
- 'invalid schema with missing id' => [
- false,
- true,
- 'testing',
- [
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN,
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL,
- 'title' => 'Test declarative settings',
- 'description' => 'These fields are rendered dynamically from declarative schema',
- 'doc_url' => '',
- 'fields' => [
- [
- 'id' => 'test_field_7',
- 'title' => 'Multi-selection',
- 'description' => 'Select some option setting',
- 'type' => DeclarativeSettingsTypes::MULTI_SELECT,
- 'options' => ['foo', 'bar', 'baz'],
- 'placeholder' => 'Select some multiple options',
- 'default' => ['foo', 'bar'],
- ],
- ],
- ],
- ],
- 'invalid schema with invalid field' => [
- false,
- true,
- 'testing',
- [
- 'id' => 'test_form_1',
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN,
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL,
- 'title' => 'Test declarative settings',
- 'description' => 'These fields are rendered dynamically from declarative schema',
- 'doc_url' => '',
- 'fields' => [
- [
- 'id' => 'test_invalid_field',
- 'title' => 'Invalid field',
- 'description' => 'Some invalid setting description',
- 'type' => 'some_invalid_type',
- 'placeholder' => 'Some invalid field placeholder',
- 'default' => null,
- ],
- ],
- ],
- ],
- ];
- }
- public function testGetFormIDs(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- $app = 'testing2';
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- }
-
- public function testGetFormsWithDefaultValues(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(fn ($app, $configkey, $default) => $default);
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertNotEmpty($forms);
- $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false);
-
- $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $schemaSomeRealSettingField = array_values(array_filter($schema['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $this->assertEquals($schemaSomeRealSettingField['default'], $someRealSettingField['default']);
- }
-
- public function testGetFormsWithDefaultValuesJson(): void {
- $app = 'testing';
- $schema = [
- 'id' => 'test_form_1',
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL,
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL,
- 'title' => 'Test declarative settings',
- 'description' => 'These fields are rendered dynamically from declarative schema',
- 'doc_url' => '',
- 'fields' => [
- [
- 'id' => 'test_field_json',
- 'title' => 'Multi-selection',
- 'description' => 'Select some option setting',
- 'type' => DeclarativeSettingsTypes::MULTI_SELECT,
- 'options' => ['foo', 'bar', 'baz'],
- 'placeholder' => 'Select some multiple options',
- 'default' => ['foo', 'bar'],
- ],
- ],
- ];
- $this->declarativeManager->registerSchema($app, $schema);
-
- $this->config->expects($this->once())
- ->method('getUserValue')
- ->with($this->adminUser->getUID(), $app, 'test_field_json', json_encode($schema['fields'][0]['default']))
- ->willReturn(json_encode($schema['fields'][0]['default']));
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertNotEmpty($forms);
- $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false);
- $testFieldJson = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'test_field_json'))[0];
- $this->assertEquals(json_encode($schema['fields'][0]['default']), $testFieldJson['value']);
- }
-
- public function testSetInternalValue(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- self::$testSetInternalValueAfterChange = false;
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(function ($app, $configkey, $default) {
- if ($configkey === 'some_real_setting' && self::$testSetInternalValueAfterChange) {
- return '120m';
- }
- return $default;
- });
- $this->appConfig->expects($this->once())
- ->method('setValueString')
- ->with($app, 'some_real_setting', '120m');
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $this->assertEquals('40m', $someRealSettingField['value']);
-
- $this->declarativeManager->setValue($this->adminUser, $app, $schema['id'], 'some_real_setting', '120m');
- self::$testSetInternalValueAfterChange = true;
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertNotEmpty($forms);
- $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false);
-
- $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $this->assertEquals('120m', $someRealSettingField['value']);
- }
- public function testSetExternalValue(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
-
- $schema['storage_type'] = DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL;
- $schema['section_type'] = DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL;
- $this->declarativeManager->registerSchema($app, $schema);
- $setDeclarativeSettingsValueEvent = new DeclarativeSettingsSetValueEvent(
- $this->adminUser,
- $app,
- $schema['id'],
- 'some_real_setting',
- '120m'
- );
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with($setDeclarativeSettingsValueEvent);
- $this->declarativeManager->setValue($this->adminUser, $app, $schema['id'], 'some_real_setting', '120m');
- }
- public function testAdminFormUserUnauthorized(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $this->expectException(\Exception::class);
- $this->declarativeManager->getFormsWithValues($this->user, $schema['section_type'], $schema['section_id']);
- }
-
- public function testSetValueWithHandler(): void {
- $schema = self::validSchemaAllFields;
- $schema['storage_type'] = DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL;
- $form = $this->createMock(IDeclarativeSettingsFormWithHandlers::class);
- $form->expects(self::atLeastOnce())
- ->method('getSchema')
- ->willReturn($schema);
-
- $form->expects(self::once())
- ->method('setValue')
- ->with('test_field_2', 'some password', $this->adminUser);
- \OC::$server->registerService('OCA\\Testing\\Settings\\DeclarativeForm', fn () => $form, false);
- $context = $this->createMock(RegistrationContext::class);
- $context->expects(self::atLeastOnce())
- ->method('getDeclarativeSettings')
- ->willReturn([new ServiceRegistration('testing', 'OCA\\Testing\\Settings\\DeclarativeForm')]);
- $this->coordinator->expects(self::atLeastOnce())
- ->method('getRegistrationContext')
- ->willReturn($context);
- $this->declarativeManager->loadSchemas();
- $this->eventDispatcher->expects(self::never())
- ->method('dispatchTyped');
- $this->declarativeManager->setValue($this->adminUser, 'testing', 'test_form_1', 'test_field_2', 'some password');
- }
- public function testGetValueWithHandler(): void {
- $schema = self::validSchemaAllFields;
- $schema['storage_type'] = DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL;
- $form = $this->createMock(IDeclarativeSettingsFormWithHandlers::class);
- $form->expects(self::atLeastOnce())
- ->method('getSchema')
- ->willReturn($schema);
-
- $form->expects(self::once())
- ->method('getValue')
- ->with('test_field_2', $this->adminUser)
- ->willReturn('very secret password');
- \OC::$server->registerService('OCA\\Testing\\Settings\\DeclarativeForm', fn () => $form, false);
- $context = $this->createMock(RegistrationContext::class);
- $context->expects(self::atLeastOnce())
- ->method('getDeclarativeSettings')
- ->willReturn([new ServiceRegistration('testing', 'OCA\\Testing\\Settings\\DeclarativeForm')]);
- $this->coordinator->expects(self::atLeastOnce())
- ->method('getRegistrationContext')
- ->willReturn($context);
- $this->declarativeManager->loadSchemas();
- $this->eventDispatcher->expects(self::never())
- ->method('dispatchTyped');
- $password = $this->invokePrivate($this->declarativeManager, 'getValue', [$this->adminUser, 'testing', 'test_form_1', 'test_field_2']);
- self::assertEquals('very secret password', $password);
- }
- }
|