From 43f83ed3c964b4f6a5e1cf3b7f36aa51c8a34faa Mon Sep 17 00:00:00 2001 From: Stefan Dietrich <5716289+stediefan@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:05:05 +0200 Subject: [PATCH 1/3] feat: introduce a function to bind a teamfolder to a team Assisted by: GitHub Copilot:Claude Sonnet 5 Assisted by: GitHub Copilot:GPT-5.6 Terra Assisted by: GitHub Copilot:Kimi K2.7 Code Signed-off-by: Stefan Dietrich <5716289+stediefan@users.noreply.github.com> --- lib/private/Teams/TeamManager.php | 12 +++++ lib/public/Teams/ITeamFolderProvider.php | 51 ++++++++++++++++++++ lib/public/Teams/ITeamManager.php | 8 ++++ lib/public/Teams/TeamFolder.php | 52 ++++++++++++++++++++ tests/lib/Teams/TeamFolderTest.php | 23 +++++++++ tests/lib/Teams/TeamManagerTest.php | 61 ++++++++++++++++++++++++ 6 files changed, 207 insertions(+) create mode 100644 lib/public/Teams/ITeamFolderProvider.php create mode 100644 lib/public/Teams/TeamFolder.php create mode 100644 tests/lib/Teams/TeamFolderTest.php create mode 100644 tests/lib/Teams/TeamManagerTest.php diff --git a/lib/private/Teams/TeamManager.php b/lib/private/Teams/TeamManager.php index c466b1d20c088..ab3471cd45953 100644 --- a/lib/private/Teams/TeamManager.php +++ b/lib/private/Teams/TeamManager.php @@ -16,6 +16,7 @@ use OCP\IURLGenerator; use OCP\Server; use OCP\Teams\ITeamManager; +use OCP\Teams\ITeamFolderProvider; use OCP\Teams\ITeamResourceProvider; use OCP\Teams\Team; use Psr\Container\ContainerExceptionInterface; @@ -70,6 +71,17 @@ public function getProvider(string $providerId): ITeamResourceProvider { throw new \RuntimeException('No provider found for id ' . $providerId); } + #[\Override] + public function getTeamFolderProvider(): ?ITeamFolderProvider { + foreach ($this->getProviders() as $provider) { + if ($provider instanceof ITeamFolderProvider) { + return $provider; + } + } + + return null; + } + #[\Override] public function getSharedWith(string $teamId, string $userId): array { if (!$this->hasTeamSupport()) { diff --git a/lib/public/Teams/ITeamFolderProvider.php b/lib/public/Teams/ITeamFolderProvider.php new file mode 100644 index 0000000000000..1f78bc18f014d --- /dev/null +++ b/lib/public/Teams/ITeamFolderProvider.php @@ -0,0 +1,51 @@ +id; + } + + /** + * @since 35.0.0 + */ + public function getMountPoint(): string { + return $this->mountPoint; + } + + /** + * @return array{id: int, mountPoint: string} + * @since 35.0.0 + */ + #[\Override] + public function jsonSerialize(): array { + return [ + 'id' => $this->id, + 'mountPoint' => $this->mountPoint, + ]; + } +} diff --git a/tests/lib/Teams/TeamFolderTest.php b/tests/lib/Teams/TeamFolderTest.php new file mode 100644 index 0000000000000..0b5397dbf224e --- /dev/null +++ b/tests/lib/Teams/TeamFolderTest.php @@ -0,0 +1,23 @@ +assertSame(42, $folder->getId()); + $this->assertSame('Engineering', $folder->getMountPoint()); + $this->assertSame(['id' => 42, 'mountPoint' => 'Engineering'], $folder->jsonSerialize()); + } +} diff --git a/tests/lib/Teams/TeamManagerTest.php b/tests/lib/Teams/TeamManagerTest.php new file mode 100644 index 0000000000000..45d233e50e30e --- /dev/null +++ b/tests/lib/Teams/TeamManagerTest.php @@ -0,0 +1,61 @@ +createTeamManager(null); + + $this->assertNull($teamManager->getTeamFolderProvider()); + } + + public function testGetTeamFolderProviderReturnsNullWithoutFolderProvider(): void { + $teamManager = $this->createTeamManager($this->createMock(CirclesManager::class)); + $this->setProviders($teamManager, [ + 'other' => $this->createMock(ITeamResourceProvider::class), + ]); + + $this->assertNull($teamManager->getTeamFolderProvider()); + } + + public function testGetTeamFolderProviderReturnsRegisteredFolderProvider(): void { + $teamManager = $this->createTeamManager($this->createMock(CirclesManager::class)); + $folderProvider = $this->createMock(ITeamFolderProvider::class); + $this->setProviders($teamManager, [ + 'other' => $this->createMock(ITeamResourceProvider::class), + 'folder' => $folderProvider, + ]); + + $this->assertSame($folderProvider, $teamManager->getTeamFolderProvider()); + } + + private function createTeamManager(?CirclesManager $circlesManager): TeamManager { + return new TeamManager( + $this->createMock(Coordinator::class), + $this->createMock(IURLGenerator::class), + $circlesManager, + ); + } + + /** + * @param array $providers + */ + private function setProviders(TeamManager $teamManager, array $providers): void { + (new \ReflectionProperty(TeamManager::class, 'providers'))->setValue($teamManager, $providers); + } +} \ No newline at end of file From faf66fef7b38d2eb8b1759b93694d45b2cd253bc Mon Sep 17 00:00:00 2001 From: Stefan Dietrich <5716289+stediefan@users.noreply.github.com> Date: Mon, 27 Jul 2026 08:56:17 +0200 Subject: [PATCH 2/3] fix(teams): add consumable and implementable annotations fix: tests for TeamFolderProvider and TeamManager Assisted-by: GitHub Copilot:GPT-5.6 Terra Signed-off-by: Stefan Dietrich <5716289+stediefan@users.noreply.github.com> --- lib/composer/composer/autoload_classmap.php | 2 ++ lib/composer/composer/autoload_static.php | 2 ++ lib/private/Teams/TeamManager.php | 2 +- lib/public/Teams/ITeamFolderProvider.php | 5 ++++ lib/public/Teams/TeamFolder.php | 3 ++ tests/lib/Teams/TeamManagerTest.php | 31 +++++++++++++-------- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 7f6c8757b0d16..541afc34c351e 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1024,9 +1024,11 @@ 'OCP\\TaskProcessing\\TaskTypes\\TextToTextSummary' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextSummary.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTopics' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextTopics.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTranslate' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php', + 'OCP\\Teams\\ITeamFolderProvider' => $baseDir . '/lib/public/Teams/ITeamFolderProvider.php', 'OCP\\Teams\\ITeamManager' => $baseDir . '/lib/public/Teams/ITeamManager.php', 'OCP\\Teams\\ITeamResourceProvider' => $baseDir . '/lib/public/Teams/ITeamResourceProvider.php', 'OCP\\Teams\\Team' => $baseDir . '/lib/public/Teams/Team.php', + 'OCP\\Teams\\TeamFolder' => $baseDir . '/lib/public/Teams/TeamFolder.php', 'OCP\\Teams\\TeamResource' => $baseDir . '/lib/public/Teams/TeamResource.php', 'OCP\\Template' => $baseDir . '/lib/public/Template.php', 'OCP\\Template\\ITemplate' => $baseDir . '/lib/public/Template/ITemplate.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 3d2026f49ff4c..2390266ad685c 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1065,9 +1065,11 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\TaskProcessing\\TaskTypes\\TextToTextSummary' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextSummary.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTopics' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextTopics.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTranslate' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php', + 'OCP\\Teams\\ITeamFolderProvider' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamFolderProvider.php', 'OCP\\Teams\\ITeamManager' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamManager.php', 'OCP\\Teams\\ITeamResourceProvider' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamResourceProvider.php', 'OCP\\Teams\\Team' => __DIR__ . '/../../..' . '/lib/public/Teams/Team.php', + 'OCP\\Teams\\TeamFolder' => __DIR__ . '/../../..' . '/lib/public/Teams/TeamFolder.php', 'OCP\\Teams\\TeamResource' => __DIR__ . '/../../..' . '/lib/public/Teams/TeamResource.php', 'OCP\\Template' => __DIR__ . '/../../..' . '/lib/public/Template.php', 'OCP\\Template\\ITemplate' => __DIR__ . '/../../..' . '/lib/public/Template/ITemplate.php', diff --git a/lib/private/Teams/TeamManager.php b/lib/private/Teams/TeamManager.php index ab3471cd45953..d17aefe36223b 100644 --- a/lib/private/Teams/TeamManager.php +++ b/lib/private/Teams/TeamManager.php @@ -15,8 +15,8 @@ use OCA\Circles\Model\Probes\CircleProbe; use OCP\IURLGenerator; use OCP\Server; -use OCP\Teams\ITeamManager; use OCP\Teams\ITeamFolderProvider; +use OCP\Teams\ITeamManager; use OCP\Teams\ITeamResourceProvider; use OCP\Teams\Team; use Psr\Container\ContainerExceptionInterface; diff --git a/lib/public/Teams/ITeamFolderProvider.php b/lib/public/Teams/ITeamFolderProvider.php index 1f78bc18f014d..58b1b6f19ca41 100644 --- a/lib/public/Teams/ITeamFolderProvider.php +++ b/lib/public/Teams/ITeamFolderProvider.php @@ -9,6 +9,9 @@ namespace OCP\Teams; +use OCP\AppFramework\Attribute\Consumable; +use OCP\AppFramework\Attribute\Implementable; + /** * Provides the exclusive folder belonging to a team. * @@ -17,6 +20,8 @@ * * @since 35.0.0 */ +#[Consumable(since: '35.0.0')] +#[Implementable(since: '35.0.0')] interface ITeamFolderProvider extends ITeamResourceProvider { /** * Return the folder exclusively linked to the team. diff --git a/lib/public/Teams/TeamFolder.php b/lib/public/Teams/TeamFolder.php index 330a8bfd4ca4b..0d87a11b46f36 100644 --- a/lib/public/Teams/TeamFolder.php +++ b/lib/public/Teams/TeamFolder.php @@ -9,11 +9,14 @@ namespace OCP\Teams; +use OCP\AppFramework\Attribute\Consumable; + /** * A folder exclusively linked to a team. * * @since 35.0.0 */ +#[Consumable(since: '35.0.0')] class TeamFolder implements \JsonSerializable { /** * @since 35.0.0 diff --git a/tests/lib/Teams/TeamManagerTest.php b/tests/lib/Teams/TeamManagerTest.php index 45d233e50e30e..b5b1a42aca124 100644 --- a/tests/lib/Teams/TeamManagerTest.php +++ b/tests/lib/Teams/TeamManagerTest.php @@ -11,7 +11,6 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\Teams\TeamManager; -use OCA\Circles\CirclesManager; use OCP\IURLGenerator; use OCP\Teams\ITeamFolderProvider; use OCP\Teams\ITeamResourceProvider; @@ -19,13 +18,13 @@ class TeamManagerTest extends TestCase { public function testGetTeamFolderProviderReturnsNullWithoutTeamSupport(): void { - $teamManager = $this->createTeamManager(null); + $teamManager = $this->createTeamManager(); $this->assertNull($teamManager->getTeamFolderProvider()); } public function testGetTeamFolderProviderReturnsNullWithoutFolderProvider(): void { - $teamManager = $this->createTeamManager($this->createMock(CirclesManager::class)); + $teamManager = $this->createTeamManager(true); $this->setProviders($teamManager, [ 'other' => $this->createMock(ITeamResourceProvider::class), ]); @@ -34,7 +33,7 @@ public function testGetTeamFolderProviderReturnsNullWithoutFolderProvider(): voi } public function testGetTeamFolderProviderReturnsRegisteredFolderProvider(): void { - $teamManager = $this->createTeamManager($this->createMock(CirclesManager::class)); + $teamManager = $this->createTeamManager(true); $folderProvider = $this->createMock(ITeamFolderProvider::class); $this->setProviders($teamManager, [ 'other' => $this->createMock(ITeamResourceProvider::class), @@ -44,12 +43,22 @@ public function testGetTeamFolderProviderReturnsRegisteredFolderProvider(): void $this->assertSame($folderProvider, $teamManager->getTeamFolderProvider()); } - private function createTeamManager(?CirclesManager $circlesManager): TeamManager { - return new TeamManager( - $this->createMock(Coordinator::class), - $this->createMock(IURLGenerator::class), - $circlesManager, - ); + private function createTeamManager(bool $hasTeamSupport = false): TeamManager { + return new class($this->createMock(Coordinator::class), $this->createMock(IURLGenerator::class), null, $hasTeamSupport, ) extends TeamManager { + public function __construct( + Coordinator $bootContext, + IURLGenerator $urlGenerator, + null $circlesManager, + private bool $hasTeamSupport, + ) { + parent::__construct($bootContext, $urlGenerator, $circlesManager); + } + + #[\Override] + public function hasTeamSupport(): bool { + return $this->hasTeamSupport; + } + }; } /** @@ -58,4 +67,4 @@ private function createTeamManager(?CirclesManager $circlesManager): TeamManager private function setProviders(TeamManager $teamManager, array $providers): void { (new \ReflectionProperty(TeamManager::class, 'providers'))->setValue($teamManager, $providers); } -} \ No newline at end of file +} From 7912a85722f0858ae05390709c05cb87b85e97b4 Mon Sep 17 00:00:00 2001 From: Stefan Dietrich <5716289+stediefan@users.noreply.github.com> Date: Wed, 5 Aug 2026 07:26:15 +0200 Subject: [PATCH 3/3] fix: tests wrongly assumed ordered items Signed-off-by: Stefan Dietrich <5716289+stediefan@users.noreply.github.com> --- tests/lib/Sharing/AbstractSharingManagerTests.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lib/Sharing/AbstractSharingManagerTests.php b/tests/lib/Sharing/AbstractSharingManagerTests.php index 8f4522dc86ff2..62260bbf2ae2f 100644 --- a/tests/lib/Sharing/AbstractSharingManagerTests.php +++ b/tests/lib/Sharing/AbstractSharingManagerTests.php @@ -3159,6 +3159,7 @@ public function testGetShareUniqueDisplayNames(): void { $this->dbConnection->commit(); $share = $this->getShare($accessContext, $id); + usort($share['recipients'], fn (array $a, array $b): int => $a['value'] <=> $b['value']); $this->assertEquals([ [ 'class' => TestShareSourceType1::class,