From 2b2b9840b8524b3801b4a69cf9c159321899d03e Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Tue, 12 May 2026 22:22:40 +0200 Subject: [PATCH] test(appstore): add test cases for appinfo.xml Signed-off-by: Ferdinand Thiessen --- lib/private/legacy/OC_App.php | 3 +- tests/data/app/appinfo-attributes-once.json | 162 +++++++++ .../app/appinfo-attributes-once.json.license | 2 + tests/data/app/appinfo-attributes-once.xml | 77 +++++ tests/data/app/appinfo-multi-once.json | 192 +++++++++++ .../data/app/appinfo-multi-once.json.license | 2 + tests/data/app/appinfo-multi-once.xml | 149 ++++++++ tests/data/app/appinfo-multi-twice.json | 325 ++++++++++++++++++ .../data/app/appinfo-multi-twice.json.license | 2 + tests/data/app/appinfo-multi-twice.xml | 202 +++++++++++ tests/data/app/description-multi-lang.xml | 12 - tests/data/app/description-single-lang.xml | 11 - tests/data/app/expected-info.json | 1 - tests/data/app/navigation-one-item.json | 1 - tests/data/app/navigation-two-items.json | 1 - tests/data/app/various-single-item.json | 1 - tests/lib/App/InfoParserTest.php | 21 +- 17 files changed, 1126 insertions(+), 38 deletions(-) create mode 100644 tests/data/app/appinfo-attributes-once.json create mode 100644 tests/data/app/appinfo-attributes-once.json.license create mode 100644 tests/data/app/appinfo-attributes-once.xml create mode 100644 tests/data/app/appinfo-multi-once.json create mode 100644 tests/data/app/appinfo-multi-once.json.license create mode 100644 tests/data/app/appinfo-multi-once.xml create mode 100644 tests/data/app/appinfo-multi-twice.json create mode 100644 tests/data/app/appinfo-multi-twice.json.license create mode 100644 tests/data/app/appinfo-multi-twice.xml delete mode 100644 tests/data/app/description-multi-lang.xml delete mode 100644 tests/data/app/description-single-lang.xml diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 87d8213f5c0..e87fee19f1b 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -470,9 +470,8 @@ class OC_App { } } - $info['license'] ??= $info['licence']; + $info['license'] = $info['licence']; $info['version'] = $appManager->getAppVersion($app); - $info['license'] ??= $info['licence']; $appList[] = $info; } } diff --git a/tests/data/app/appinfo-attributes-once.json b/tests/data/app/appinfo-attributes-once.json new file mode 100644 index 00000000000..3ecc803733d --- /dev/null +++ b/tests/data/app/appinfo-attributes-once.json @@ -0,0 +1,162 @@ +{ + "id": "attributes_once", + "name": { + "@attributes": { + "lang": "en" + }, + "@value": "Attributes Once" + }, + "summary": { + "@attributes": { + "lang": "en" + }, + "@value": "Single occurrence with attributes set on allowed elements." + }, + "description": { + "@attributes": { + "lang": "en" + }, + "@value": "Fixture that sets attributes where allowed (e.g., lang, type, min-version, for)." + }, + "version": "1.2.3", + "licence": "agpl", + "author": { + "@attributes": { + "homepage": "http://example.com", + "mail": "jane@example.com" + }, + "@value": "Jane Doe" + }, + "types": [ + "filesystem" + ], + "documentation": { + "user": "https://example.test/attributes-once/user" + }, + "category": [ + "tools" + ], + "website": "https://example.test/attributes-once", + "bugs": "https://example.com/issues", + "repository": { + "@attributes": { + "type": "git" + }, + "@value": "https://example.test/attributes-once.git" + }, + "screenshot": { + "@attributes": { + "small-thumbnail": "https://example.test/attributes-once-small.png" + }, + "@value": "https://example.test/attributes-once.png" + }, + "dependencies": { + "php": { + "@attributes": { + "min-version": "8.2" + } + }, + "database": { + "@attributes": { + "min-version": "2.0" + }, + "@value": "pgsql" + }, + "lib": { + "@attributes": { + "min-version": "1.5" + }, + "@value": "curl" + }, + "owncloud": { + "@attributes": { + "min-version": "1.0", + "max-version": "2.0" + } + }, + "nextcloud": { + "@attributes": { + "min-version": "30.0", + "max-version": "31.0" + } + }, + "backend": [ + "caldav" + ] + }, + "background-jobs": { + "job": "OCA\\AttributesOnce\\BackgroundJob\\Job" + }, + "repair-steps": { + "install": { + "step": "OCA\\AttributesOnce\\RepairStep\\Install" + }, + "pre-migration": [], + "post-migration": [], + "live-migration": [], + "uninstall": [] + }, + "commands": { + "command": "OCA\\AttributesOnce\\Command\\Run" + }, + "settings": { + "admin": [ + "OCA\\AttributesOnce\\Settings\\Admin" + ], + "admin-section": [], + "personal": [], + "personal-section": [] + }, + "activity": { + "providers": { + "provider": "OCA\\AttributesOnce\\Activity\\Provider" + }, + "filters": [], + "settings": [] + }, + "navigations": { + "navigation": [ + { + "@attributes": { + "role": "admin" + }, + "name": "Attributes", + "route": "attributes.once.route", + "icon": "attributes-once.svg", + "order": "5" + } + ] + }, + "collaboration": { + "plugins": { + "@attributes": { + "type": "collaborator-search" + }, + "@value": "OCA\\AttributesOnce\\Collaboration\\Plugin" + } + }, + "sabre": { + "plugins": { + "plugin": "OCA\\AttributesOnce\\Sabre\\Plugin" + } + }, + "trash": { + "backend": { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\AttributesOnce\\Trash\\Backend" + } + }, + "versions": { + "backend": { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\AttributesOnce\\Versions\\Backend" + } + }, + "remote": [], + "public": [], + "two-factor-providers": [] +} diff --git a/tests/data/app/appinfo-attributes-once.json.license b/tests/data/app/appinfo-attributes-once.json.license new file mode 100644 index 00000000000..8c5ffd52494 --- /dev/null +++ b/tests/data/app/appinfo-attributes-once.json.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/tests/data/app/appinfo-attributes-once.xml b/tests/data/app/appinfo-attributes-once.xml new file mode 100644 index 00000000000..d3c246240ae --- /dev/null +++ b/tests/data/app/appinfo-attributes-once.xml @@ -0,0 +1,77 @@ + + + + attributes_once + Attributes Once + Single occurrence with attributes set on allowed elements. + Fixture that sets attributes where allowed (e.g., lang, type, min-version, for). + 1.2.3 + agpl + Jane Doe + + + + + https://example.test/attributes-once/user + + tools + https://example.test/attributes-once + https://example.com/issues + https://example.test/attributes-once.git + https://example.test/attributes-once.png + + + pgsql + curl + + + caldav + + + OCA\AttributesOnce\BackgroundJob\Job + + + + OCA\AttributesOnce\RepairStep\Install + + + + OCA\AttributesOnce\Command\Run + + + OCA\AttributesOnce\Settings\Admin + + + + OCA\AttributesOnce\Activity\Provider + + + + + Attributes + attributes.once.route + attributes-once.svg + 5 + + + + + OCA\AttributesOnce\Collaboration\Plugin + + + + + OCA\AttributesOnce\Sabre\Plugin + + + + OCA\AttributesOnce\Trash\Backend + + + OCA\AttributesOnce\Versions\Backend + + diff --git a/tests/data/app/appinfo-multi-once.json b/tests/data/app/appinfo-multi-once.json new file mode 100644 index 00000000000..0145060ebe7 --- /dev/null +++ b/tests/data/app/appinfo-multi-once.json @@ -0,0 +1,192 @@ +{ + "id": "multi_once", + "name": "Multi Once", + "summary": "Every repeatable element is used exactly once.", + "description": "Fixture that exercises the single-item normalization path.", + "version": "1.0.0", + "licence": "agpl", + "author": [ + "Jane Doe" + ], + "types": [ + "filesystem", + "logging" + ], + "documentation": { + "user": "https://example.test/multi-once/user", + "admin": "https://example.test/multi-once/admin", + "developer": "https://example.test/multi-once/developer" + }, + "category": [ + "monitoring" + ], + "website": "https://example.test/multi-once", + "discussion": "https://example.test/multi-once/discussion", + "bugs": "https://example.test/multi-once/issues", + "repository": "https://example.test/multi-once.git", + "screenshot": [ + "https://example.test/multi-once.png" + ], + "donation": "https://example.test/donate", + "dependencies": { + "database": "sqlite", + "command": "awk", + "lib": { + "@attributes": { + "min-version": "1.0" + }, + "@value": "curl" + }, + "nextcloud": { + "@attributes": { + "min-version": "30.0", + "max-version": "31.0" + } + }, + "architecture": "x86_64", + "backend": [ + "caldav" + ] + }, + "background-jobs": { + "job": "OCA\\MultiOnce\\BackgroundJob\\Cleanup" + }, + "repair-steps": { + "pre-migration": { + "step": "OCA\\MultiOnce\\RepairStep\\PreMigration" + }, + "post-migration": { + "step": "OCA\\MultiOnce\\RepairStep\\PostMigration" + }, + "live-migration": { + "step": "OCA\\MultiOnce\\RepairStep\\LiveMigration" + }, + "install": { + "step": "OCA\\MultiOnce\\RepairStep\\Install" + }, + "uninstall": { + "step": "OCA\\MultiOnce\\RepairStep\\Uninstall" + } + }, + "two-factor-providers": { + "provider": "OCA\\MultiOnce\\TwoFactor\\Provider" + }, + "commands": { + "command": "OCA\\MultiOnce\\Command\\Migrate" + }, + "settings": { + "admin": [ + "OCA\\MultiOnce\\Settings\\Admin" + ], + "admin-section": [ + "OCA\\MultiOnce\\Settings\\AdminSection" + ], + "personal": [ + "OCA\\MultiOnce\\Settings\\Personal" + ], + "personal-section": [ + "OCA\\MultiOnce\\Settings\\PersonalSection" + ], + "admin-delegation": [ + "OCA\\MultiOnce\\Settings\\AdminDelegation" + ], + "admin-delegation-section": [ + "OCA\\MultiOnce\\Settings\\AdminDelegationSection" + ] + }, + "activity": { + "settings": { + "setting": "OCA\\MultiOnce\\Activity\\Setting" + }, + "filters": { + "filter": "OCA\\MultiOnce\\Activity\\Filter" + }, + "providers": { + "provider": "OCA\\MultiOnce\\Activity\\Provider" + } + }, + "dashboard": { + "widget": "OCA\\MultiOnce\\Dashboard\\Widget" + }, + "fulltextsearch": { + "platform": "OCA\\MultiOnce\\Search\\Platform", + "provider": "OCA\\MultiOnce\\Search\\Provider" + }, + "navigations": { + "navigation": [ + { + "name": "Multi Once", + "route": "multi.once.route", + "icon": "multi-once.svg", + "order": "1" + } + ] + }, + "contactsmenu": { + "provider": "OCA\\MultiOnce\\ContactsMenu\\Provider" + }, + "collaboration": { + "plugins": { + "@attributes": { + "type": "collaborator-search" + }, + "@value": "OCA\\MultiOnce\\Collaboration\\Plugin" + } + }, + "openmetrics": { + "exporter": [ + "OCA\\MultiOnce\\OpenMetrics\\Exporter" + ] + }, + "sabre": { + "collections": { + "collection": "OCA\\MultiOnce\\Sabre\\Collection" + }, + "plugins": { + "plugin": "OCA\\MultiOnce\\Sabre\\Plugin" + }, + "address-book-plugins": { + "plugin": "OCA\\MultiOnce\\Sabre\\AddressBookPlugin" + }, + "calendar-plugins": { + "plugin": "OCA\\MultiOnce\\Sabre\\CalendarPlugin" + } + }, + "trash": { + "backend": { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\MultiOnce\\Trash\\Backend" + } + }, + "versions": { + "backend": { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\MultiOnce\\Versions\\Backend" + } + }, + "external-app": { + "docker-install": { + "registry": "registry.example.test", + "image": "multi-once", + "image-tag": "1.0.0" + }, + "scopes": { + "value": "scope-one" + }, + "system": "true", + "environment-variables": { + "variable": { + "name": "MULTI_ONCE_ONE", + "display-name": "Multi Once One", + "description": "First variable", + "default": "one" + } + } + }, + "remote": [], + "public": [] +} diff --git a/tests/data/app/appinfo-multi-once.json.license b/tests/data/app/appinfo-multi-once.json.license new file mode 100644 index 00000000000..5e8eced1a6a --- /dev/null +++ b/tests/data/app/appinfo-multi-once.json.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/tests/data/app/appinfo-multi-once.xml b/tests/data/app/appinfo-multi-once.xml new file mode 100644 index 00000000000..3c14664a073 --- /dev/null +++ b/tests/data/app/appinfo-multi-once.xml @@ -0,0 +1,149 @@ + + + + multi_once + Multi Once + Every repeatable element is used exactly once. + Fixture that exercises the single-item normalization path. + 1.0.0 + agpl + Jane Doe + + + + + + https://example.test/multi-once/user + https://example.test/multi-once/admin + https://example.test/multi-once/developer + + monitoring + https://example.test/multi-once + https://example.test/multi-once/discussion + https://example.test/multi-once/issues + https://example.test/multi-once.git + https://example.test/multi-once.png + https://example.test/donate + + sqlite + awk + curl + + x86_64 + caldav + + + OCA\MultiOnce\BackgroundJob\Cleanup + + + + OCA\MultiOnce\RepairStep\PreMigration + + + OCA\MultiOnce\RepairStep\PostMigration + + + OCA\MultiOnce\RepairStep\LiveMigration + + + OCA\MultiOnce\RepairStep\Install + + + OCA\MultiOnce\RepairStep\Uninstall + + + + OCA\MultiOnce\TwoFactor\Provider + + + OCA\MultiOnce\Command\Migrate + + + OCA\MultiOnce\Settings\Admin + OCA\MultiOnce\Settings\AdminSection + OCA\MultiOnce\Settings\Personal + OCA\MultiOnce\Settings\PersonalSection + OCA\MultiOnce\Settings\AdminDelegation + OCA\MultiOnce\Settings\AdminDelegationSection + + + + OCA\MultiOnce\Activity\Setting + + + OCA\MultiOnce\Activity\Filter + + + OCA\MultiOnce\Activity\Provider + + + + OCA\MultiOnce\Dashboard\Widget + + + OCA\MultiOnce\Search\Platform + OCA\MultiOnce\Search\Provider + + + + Multi Once + multi.once.route + multi-once.svg + 1 + + + + OCA\MultiOnce\ContactsMenu\Provider + + + + OCA\MultiOnce\Collaboration\Plugin + + + + OCA\MultiOnce\OpenMetrics\Exporter + + + + OCA\MultiOnce\Sabre\Collection + + + OCA\MultiOnce\Sabre\Plugin + + + OCA\MultiOnce\Sabre\AddressBookPlugin + + + OCA\MultiOnce\Sabre\CalendarPlugin + + + + OCA\MultiOnce\Trash\Backend + + + OCA\MultiOnce\Versions\Backend + + + + registry.example.test + multi-once + 1.0.0 + + + scope-one + + true + + + MULTI_ONCE_ONE + Multi Once One + First variable + one + + + + \ No newline at end of file diff --git a/tests/data/app/appinfo-multi-twice.json b/tests/data/app/appinfo-multi-twice.json new file mode 100644 index 00000000000..114cab05f6c --- /dev/null +++ b/tests/data/app/appinfo-multi-twice.json @@ -0,0 +1,325 @@ +{ + "id": "multi_twice", + "name": "Multi Twice", + "summary": "Every repeatable element is used exactly twice.", + "description": "Fixture that exercises the list normalization path.", + "version": "1.0.0", + "licence": [ + "agpl", + "mit" + ], + "author": [ + "Jane Doe", + "John Doe" + ], + "types": [ + "filesystem", + "logging" + ], + "documentation": { + "user": "https://example.test/multi-twice/user", + "admin": "https://example.test/multi-twice/admin", + "developer": "https://example.test/multi-twice/developer" + }, + "category": [ + "monitoring", + "social" + ], + "website": "https://example.test/multi-twice", + "discussion": "https://example.test/multi-twice/discussion", + "bugs": "https://example.test/multi-twice/issues", + "repository": { + "@attributes": { + "type": "git" + }, + "@value": "https://example.test/multi-twice.git" + }, + "screenshot": [ + "https://example.test/multi-twice-1.png", + "https://example.test/multi-twice-2.png" + ], + "donation": [ + "https://example.test/donate/1", + "https://example.test/donate/2" + ], + "dependencies": { + "php": { + "@attributes": { + "min-version": "8.2" + } + }, + "database": [ + { + "@attributes": { + "min-version": "1.0" + }, + "@value": "sqlite" + }, + { + "@attributes": { + "min-version": "1.0" + }, + "@value": "mysql" + } + ], + "command": [ + "awk", + "grep" + ], + "lib": [ + { + "@attributes": { + "min-version": "1.0" + }, + "@value": "curl" + }, + { + "@attributes": { + "min-version": "1.0" + }, + "@value": "intl" + } + ], + "owncloud": { + "@attributes": { + "min-version": "1.0", + "max-version": "2.0" + } + }, + "nextcloud": { + "@attributes": { + "min-version": "30.0", + "max-version": "31.0" + } + }, + "architecture": [ + "x86_64", + "aarch64" + ], + "backend": [ + "caldav", + "caldav" + ] + }, + "background-jobs": [ + "OCA\\MultiTwice\\BackgroundJob\\CleanupOne", + "OCA\\MultiTwice\\BackgroundJob\\CleanupTwo" + ], + "repair-steps": { + "pre-migration": [ + "OCA\\MultiTwice\\RepairStep\\PreMigrationOne", + "OCA\\MultiTwice\\RepairStep\\PreMigrationTwo" + ], + "post-migration": [ + "OCA\\MultiTwice\\RepairStep\\PostMigrationOne", + "OCA\\MultiTwice\\RepairStep\\PostMigrationTwo" + ], + "live-migration": [ + "OCA\\MultiTwice\\RepairStep\\LiveMigrationOne", + "OCA\\MultiTwice\\RepairStep\\LiveMigrationTwo" + ], + "install": [ + "OCA\\MultiTwice\\RepairStep\\InstallOne", + "OCA\\MultiTwice\\RepairStep\\InstallTwo" + ], + "uninstall": [ + "OCA\\MultiTwice\\RepairStep\\UninstallOne", + "OCA\\MultiTwice\\RepairStep\\UninstallTwo" + ] + }, + "two-factor-providers": [ + "OCA\\MultiTwice\\TwoFactor\\ProviderOne", + "OCA\\MultiTwice\\TwoFactor\\ProviderTwo" + ], + "commands": [ + "OCA\\MultiTwice\\Command\\MigrateOne", + "OCA\\MultiTwice\\Command\\MigrateTwo" + ], + "settings": { + "admin": [ + "OCA\\MultiTwice\\Settings\\AdminOne", + "OCA\\MultiTwice\\Settings\\AdminTwo" + ], + "admin-section": [ + "OCA\\MultiTwice\\Settings\\AdminSectionOne", + "OCA\\MultiTwice\\Settings\\AdminSectionTwo" + ], + "personal": [ + "OCA\\MultiTwice\\Settings\\PersonalOne", + "OCA\\MultiTwice\\Settings\\PersonalTwo" + ], + "personal-section": [ + "OCA\\MultiTwice\\Settings\\PersonalSectionOne", + "OCA\\MultiTwice\\Settings\\PersonalSectionTwo" + ], + "admin-delegation": [ + "OCA\\MultiTwice\\Settings\\AdminDelegationOne", + "OCA\\MultiTwice\\Settings\\AdminDelegationTwo" + ], + "admin-delegation-section": [ + "OCA\\MultiTwice\\Settings\\AdminDelegationSectionOne", + "OCA\\MultiTwice\\Settings\\AdminDelegationSectionTwo" + ] + }, + "activity": { + "settings": [ + "OCA\\MultiTwice\\Activity\\SettingOne", + "OCA\\MultiTwice\\Activity\\SettingTwo" + ], + "filters": [ + "OCA\\MultiTwice\\Activity\\FilterOne", + "OCA\\MultiTwice\\Activity\\FilterTwo" + ], + "providers": [ + "OCA\\MultiTwice\\Activity\\ProviderOne", + "OCA\\MultiTwice\\Activity\\ProviderTwo" + ] + }, + "dashboard": { + "widget": [ + "OCA\\MultiTwice\\Dashboard\\WidgetOne", + "OCA\\MultiTwice\\Dashboard\\WidgetTwo" + ] + }, + "fulltextsearch": { + "platform": [ + "OCA\\MultiTwice\\Search\\PlatformOne", + "OCA\\MultiTwice\\Search\\PlatformTwo" + ], + "provider": [ + "OCA\\MultiTwice\\Search\\ProviderOne", + "OCA\\MultiTwice\\Search\\ProviderTwo" + ] + }, + "navigations": { + "navigation": [ + { + "name": "Multi Twice One", + "route": "multi.twice.one", + "icon": "multi-twice-1.svg", + "order": "1" + }, + { + "name": "Multi Twice Two", + "route": "multi.twice.two", + "icon": "multi-twice-2.svg", + "order": "2" + } + ] + }, + "contactsmenu": { + "provider": "OCA\\MultiTwice\\ContactsMenu\\Provider" + }, + "collaboration": { + "plugins": [ + { + "@attributes": { + "type": "collaborator-search" + }, + "@value": "OCA\\MultiTwice\\Collaboration\\PluginOne" + }, + { + "@attributes": { + "type": "autocomplete-sort" + }, + "@value": "OCA\\MultiTwice\\Collaboration\\PluginTwo" + } + ] + }, + "openmetrics": { + "exporter": [ + "OCA\\MultiTwice\\OpenMetrics\\ExporterOne", + "OCA\\MultiTwice\\OpenMetrics\\ExporterTwo" + ] + }, + "sabre": { + "collections": { + "collection": [ + "OCA\\MultiTwice\\Sabre\\CollectionOne", + "OCA\\MultiTwice\\Sabre\\CollectionTwo" + ] + }, + "plugins": { + "plugin": [ + "OCA\\MultiTwice\\Sabre\\PluginOne", + "OCA\\MultiTwice\\Sabre\\PluginTwo" + ] + }, + "address-book-plugins": { + "plugin": [ + "OCA\\MultiTwice\\Sabre\\AddressBookPluginOne", + "OCA\\MultiTwice\\Sabre\\AddressBookPluginTwo" + ] + }, + "calendar-plugins": { + "plugin": [ + "OCA\\MultiTwice\\Sabre\\CalendarPluginOne", + "OCA\\MultiTwice\\Sabre\\CalendarPluginTwo" + ] + } + }, + "trash": { + "backend": [ + { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\MultiTwice\\Trash\\BackendOne" + }, + { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\MultiTwice\\Trash\\BackendTwo" + } + ] + }, + "versions": { + "backend": [ + { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\MultiTwice\\Versions\\BackendOne" + }, + { + "@attributes": { + "for": "files" + }, + "@value": "OCA\\MultiTwice\\Versions\\BackendTwo" + } + ] + }, + "external-app": { + "docker-install": { + "registry": "registry.example.test", + "image": "multi-twice", + "image-tag": "2.0.0" + }, + "scopes": { + "value": [ + "scope-one", + "scope-two" + ] + }, + "system": "true", + "environment-variables": { + "variable": [ + { + "name": "MULTI_TWICE_ONE", + "display-name": "Multi Twice One", + "description": "First variable", + "default": "one" + }, + { + "name": "MULTI_TWICE_TWO", + "display-name": "Multi Twice Two", + "description": "Second variable", + "default": "two" + } + ] + } + }, + "remote": [], + "public": [] +} diff --git a/tests/data/app/appinfo-multi-twice.json.license b/tests/data/app/appinfo-multi-twice.json.license new file mode 100644 index 00000000000..5e8eced1a6a --- /dev/null +++ b/tests/data/app/appinfo-multi-twice.json.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/tests/data/app/appinfo-multi-twice.xml b/tests/data/app/appinfo-multi-twice.xml new file mode 100644 index 00000000000..911e8342815 --- /dev/null +++ b/tests/data/app/appinfo-multi-twice.xml @@ -0,0 +1,202 @@ + + + + multi_twice + Multi Twice + Every repeatable element is used exactly twice. + Fixture that exercises the list normalization path. + 1.0.0 + agpl + mit + Jane Doe + John Doe + + + + + + https://example.test/multi-twice/user + https://example.test/multi-twice/admin + https://example.test/multi-twice/developer + + monitoring + social + https://example.test/multi-twice + https://example.test/multi-twice/discussion + https://example.test/multi-twice/issues + https://example.test/multi-twice.git + https://example.test/multi-twice-1.png + https://example.test/multi-twice-2.png + https://example.test/donate/1 + https://example.test/donate/2 + + + sqlite + mysql + awk + grep + curl + intl + + + x86_64 + aarch64 + caldav + caldav + + + OCA\MultiTwice\BackgroundJob\CleanupOne + OCA\MultiTwice\BackgroundJob\CleanupTwo + + + + OCA\MultiTwice\RepairStep\PreMigrationOne + OCA\MultiTwice\RepairStep\PreMigrationTwo + + + OCA\MultiTwice\RepairStep\PostMigrationOne + OCA\MultiTwice\RepairStep\PostMigrationTwo + + + OCA\MultiTwice\RepairStep\LiveMigrationOne + OCA\MultiTwice\RepairStep\LiveMigrationTwo + + + OCA\MultiTwice\RepairStep\InstallOne + OCA\MultiTwice\RepairStep\InstallTwo + + + OCA\MultiTwice\RepairStep\UninstallOne + OCA\MultiTwice\RepairStep\UninstallTwo + + + + OCA\MultiTwice\TwoFactor\ProviderOne + OCA\MultiTwice\TwoFactor\ProviderTwo + + + OCA\MultiTwice\Command\MigrateOne + OCA\MultiTwice\Command\MigrateTwo + + + OCA\MultiTwice\Settings\AdminOne + OCA\MultiTwice\Settings\AdminTwo + OCA\MultiTwice\Settings\AdminSectionOne + OCA\MultiTwice\Settings\AdminSectionTwo + OCA\MultiTwice\Settings\PersonalOne + OCA\MultiTwice\Settings\PersonalTwo + OCA\MultiTwice\Settings\PersonalSectionOne + OCA\MultiTwice\Settings\PersonalSectionTwo + OCA\MultiTwice\Settings\AdminDelegationOne + OCA\MultiTwice\Settings\AdminDelegationTwo + OCA\MultiTwice\Settings\AdminDelegationSectionOne + OCA\MultiTwice\Settings\AdminDelegationSectionTwo + + + + OCA\MultiTwice\Activity\SettingOne + OCA\MultiTwice\Activity\SettingTwo + + + OCA\MultiTwice\Activity\FilterOne + OCA\MultiTwice\Activity\FilterTwo + + + OCA\MultiTwice\Activity\ProviderOne + OCA\MultiTwice\Activity\ProviderTwo + + + + OCA\MultiTwice\Dashboard\WidgetOne + OCA\MultiTwice\Dashboard\WidgetTwo + + + OCA\MultiTwice\Search\PlatformOne + OCA\MultiTwice\Search\PlatformTwo + OCA\MultiTwice\Search\ProviderOne + OCA\MultiTwice\Search\ProviderTwo + + + + Multi Twice One + multi.twice.one + multi-twice-1.svg + 1 + + + Multi Twice Two + multi.twice.two + multi-twice-2.svg + 2 + + + + OCA\MultiTwice\ContactsMenu\Provider + + + + OCA\MultiTwice\Collaboration\PluginOne + OCA\MultiTwice\Collaboration\PluginTwo + + + + OCA\MultiTwice\OpenMetrics\ExporterOne + OCA\MultiTwice\OpenMetrics\ExporterTwo + + + + OCA\MultiTwice\Sabre\CollectionOne + OCA\MultiTwice\Sabre\CollectionTwo + + + OCA\MultiTwice\Sabre\PluginOne + OCA\MultiTwice\Sabre\PluginTwo + + + OCA\MultiTwice\Sabre\AddressBookPluginOne + OCA\MultiTwice\Sabre\AddressBookPluginTwo + + + OCA\MultiTwice\Sabre\CalendarPluginOne + OCA\MultiTwice\Sabre\CalendarPluginTwo + + + + OCA\MultiTwice\Trash\BackendOne + OCA\MultiTwice\Trash\BackendTwo + + + OCA\MultiTwice\Versions\BackendOne + OCA\MultiTwice\Versions\BackendTwo + + + + registry.example.test + multi-twice + 2.0.0 + + + scope-one + scope-two + + true + + + MULTI_TWICE_ONE + Multi Twice One + First variable + one + + + MULTI_TWICE_TWO + Multi Twice Two + Second variable + two + + + + \ No newline at end of file diff --git a/tests/data/app/description-multi-lang.xml b/tests/data/app/description-multi-lang.xml deleted file mode 100644 index be1dd616a99..00000000000 --- a/tests/data/app/description-multi-lang.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - files_encryption - Server-side Encryption - English - German - AGPL - diff --git a/tests/data/app/description-single-lang.xml b/tests/data/app/description-single-lang.xml deleted file mode 100644 index 36fb2aacbe2..00000000000 --- a/tests/data/app/description-single-lang.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - files_encryption - Server-side Encryption - English - AGPL - diff --git a/tests/data/app/expected-info.json b/tests/data/app/expected-info.json index 3154644e472..d34f9e67ed3 100644 --- a/tests/data/app/expected-info.json +++ b/tests/data/app/expected-info.json @@ -1,5 +1,4 @@ { - "info": [], "remote": [], "public": [], "id": "files_encryption", diff --git a/tests/data/app/navigation-one-item.json b/tests/data/app/navigation-one-item.json index 2bd81461586..a9ce44f105d 100644 --- a/tests/data/app/navigation-one-item.json +++ b/tests/data/app/navigation-one-item.json @@ -72,7 +72,6 @@ } ] }, - "info": [], "remote": [], "public": [], "repair-steps": { diff --git a/tests/data/app/navigation-two-items.json b/tests/data/app/navigation-two-items.json index 4b081d3bbd9..118c9242794 100644 --- a/tests/data/app/navigation-two-items.json +++ b/tests/data/app/navigation-two-items.json @@ -78,7 +78,6 @@ } ] }, - "info": [], "remote": [], "public": [], "repair-steps": { diff --git a/tests/data/app/various-single-item.json b/tests/data/app/various-single-item.json index ff395f5199d..ad845adc4b0 100644 --- a/tests/data/app/various-single-item.json +++ b/tests/data/app/various-single-item.json @@ -22,7 +22,6 @@ "category": [ "monitoring" ], - "info": [], "background-jobs": [], "activity": { "filters": [], diff --git a/tests/lib/App/InfoParserTest.php b/tests/lib/App/InfoParserTest.php index b59c02198a0..09169801ff4 100644 --- a/tests/lib/App/InfoParserTest.php +++ b/tests/lib/App/InfoParserTest.php @@ -47,16 +47,19 @@ class InfoParserTest extends TestCase { #[\PHPUnit\Framework\Attributes\DataProvider('appDataProvider')] public function testApplyL10N(array $data, array $expected, string $language): void { $parser = new InfoParser(); - $this->assertSame($expected, $parser->applyL10N($data, $language)); + $this->assertEqualsCanonicalizing($expected, $parser->applyL10N($data, $language)); } public static function providesInfoXml(): array { return [ - ['expected-info.json', 'valid-info.xml'], - [null, 'invalid-info.xml'], - ['navigation-one-item.json', 'navigation-one-item.xml'], - ['navigation-two-items.json', 'navigation-two-items.xml'], - ['various-single-item.json', 'various-single-item.xml'], + 'Only one value in each list' => ['appinfo-multi-once.json', 'appinfo-multi-once.xml'], + 'Only one value in each list with attributes' => ['appinfo-attributes-once.json', 'appinfo-attributes-once.xml'], + 'Multiple values in each list' => ['appinfo-multi-twice.json', 'appinfo-multi-twice.xml'], + 'Valid info' => ['expected-info.json', 'valid-info.xml'], + 'Invalid info' => [null, 'invalid-info.xml'], + 'Navigation one item' => ['navigation-one-item.json', 'navigation-one-item.xml'], + 'Navigation two items' => ['navigation-two-items.json', 'navigation-two-items.xml'], + 'Various single item' => ['various-single-item.json', 'various-single-item.xml'], ]; } @@ -83,17 +86,17 @@ class InfoParserTest extends TestCase { // test trimming [ ['description' => " \t This is a multiline \n test with \n \t \n \n some new lines "], - ['description' => "This is a multiline \n test with \n \t \n \n some new lines"], + ['description' => "This is a multiline \n test with \n \t \n \n some new lines", 'summary' => '', 'name' => ''], 'en' ], [ ['description' => " \t This is a multiline \n test with \n \t some new lines "], - ['description' => "This is a multiline \n test with \n \t some new lines"], + ['description' => "This is a multiline \n test with \n \t some new lines", 'summary' => '', 'name' => ''], 'en' ], [ ['description' => hex2bin('5065726d657420646520732761757468656e7469666965722064616e732070697769676f20646972656374656d656e74206176656320736573206964656e74696669616e7473206f776e636c6f75642073616e73206c65732072657461706572206574206d657420c3a0206a6f757273206365757820636920656e20636173206465206368616e67656d656e74206465206d6f742064652070617373652e0d0a0d')], - ['description' => "Permet de s'authentifier dans piwigo directement avec ses identifiants owncloud sans les retaper et met à jours ceux ci en cas de changement de mot de passe."], + ['description' => "Permet de s'authentifier dans piwigo directement avec ses identifiants owncloud sans les retaper et met à jours ceux ci en cas de changement de mot de passe.", 'summary' => '', 'name' => ''], 'fr' ], // test proper translation handling