Completed
Push — master ( d03f6d...56f3d1 )
by Joas
20:52 queued 17s
created
apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php 1 patch
Indentation   +152 added lines, -152 removed lines patch added patch discarded remove patch
@@ -17,9 +17,9 @@  discard block
 block discarded – undo
17 17
 use Test\TestCase;
18 18
 
19 19
 enum ERROR_TYPE {
20
-	case MIN_ERROR;
21
-	case MAX_ERROR;
22
-	case NONE;
20
+    case MIN_ERROR;
21
+    case MAX_ERROR;
22
+    case NONE;
23 23
 }
24 24
 
25 25
 /**
@@ -29,153 +29,153 @@  discard block
 block discarded – undo
29 29
  */
30 30
 class BlockLegacyClientPluginTest extends TestCase {
31 31
 
32
-	private IConfig&MockObject $config;
33
-	private ThemingDefaults&MockObject $themingDefaults;
34
-	private BlockLegacyClientPlugin $blockLegacyClientVersionPlugin;
35
-
36
-	protected function setUp(): void {
37
-		parent::setUp();
38
-
39
-		$this->config = $this->createMock(IConfig::class);
40
-		$this->themingDefaults = $this->createMock(ThemingDefaults::class);
41
-		$this->blockLegacyClientVersionPlugin = new BlockLegacyClientPlugin(
42
-			$this->config,
43
-			$this->themingDefaults,
44
-		);
45
-	}
46
-
47
-	public static function oldDesktopClientProvider(): array {
48
-		return [
49
-			['Mozilla/5.0 (Windows) mirall/1.5.0', ERROR_TYPE::MIN_ERROR],
50
-			['Mozilla/5.0 (Bogus Text) mirall/1.6.9', ERROR_TYPE::MIN_ERROR],
51
-			['Mozilla/5.0 (Windows) mirall/2.5.0', ERROR_TYPE::MAX_ERROR],
52
-			['Mozilla/5.0 (Bogus Text) mirall/2.0.1', ERROR_TYPE::MAX_ERROR],
53
-			['Mozilla/5.0 (Windows) mirall/2.0.0', ERROR_TYPE::NONE],
54
-			['Mozilla/5.0 (Bogus Text) mirall/2.0.0', ERROR_TYPE::NONE],
55
-		];
56
-	}
57
-
58
-	/**
59
-	 * @dataProvider oldDesktopClientProvider
60
-	 */
61
-	public function testBeforeHandlerException(string $userAgent, ERROR_TYPE $errorType): void {
62
-		$this->themingDefaults
63
-			->expects($this->atMost(1))
64
-			->method('getSyncClientUrl')
65
-			->willReturn('https://nextcloud.com/install/#install-clients');
66
-
67
-		$this->config
68
-			->expects($this->exactly(2))
69
-			->method('getSystemValueString')
70
-			->willReturnCallback(function (string $key) {
71
-				if ($key === 'minimum.supported.desktop.version') {
72
-					return '1.7.0';
73
-				}
74
-				return '2.0.0';
75
-			});
76
-
77
-		if ($errorType !== ERROR_TYPE::NONE) {
78
-			$errorString = $errorType === ERROR_TYPE::MIN_ERROR
79
-			? 'This version of the client is unsupported. Upgrade to <a href="https://nextcloud.com/install/#install-clients">version 1.7.0 or later</a>.'
80
-			: 'This version of the client is unsupported. Downgrade to <a href="https://nextcloud.com/install/#install-clients">version 2.0.0 or earlier</a>.';
81
-			$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
82
-			$this->expectExceptionMessage($errorString);
83
-		}
84
-
85
-		/** @var RequestInterface|MockObject $request */
86
-		$request = $this->createMock(RequestInterface::class);
87
-		$request
88
-			->expects($this->once())
89
-			->method('getHeader')
90
-			->with('User-Agent')
91
-			->willReturn($userAgent);
92
-
93
-		$this->blockLegacyClientVersionPlugin->beforeHandler($request);
94
-	}
95
-
96
-	/**
97
-	 * Ensure that there is no room for XSS attack through configured URL / version
98
-	 * @dataProvider oldDesktopClientProvider
99
-	 */
100
-	public function testBeforeHandlerExceptionPreventXSSAttack(string $userAgent, ERROR_TYPE $errorType): void {
101
-		$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
102
-
103
-		$this->themingDefaults
104
-			->expects($this->atMost(1))
105
-			->method('getSyncClientUrl')
106
-			->willReturn('https://example.com"><script>alter("hacked");</script>');
107
-
108
-		$this->config
109
-			->expects($this->exactly(2))
110
-			->method('getSystemValueString')
111
-			->willReturnCallback(function (string $key) {
112
-				if ($key === 'minimum.supported.desktop.version') {
113
-					return '1.7.0 <script>alert("unsafe")</script>';
114
-				}
115
-				return '2.0.0 <script>alert("unsafe")</script>';
116
-			});
117
-
118
-		$errorString = $errorType === ERROR_TYPE::MIN_ERROR
119
-			? 'This version of the client is unsupported. Upgrade to <a href="https://example.com&quot;&gt;&lt;script&gt;alter(&quot;hacked&quot;);&lt;/script&gt;">version 1.7.0 &lt;script&gt;alert(&quot;unsafe&quot;)&lt;/script&gt; or later</a>.'
120
-			: 'This version of the client is unsupported. Downgrade to <a href="https://example.com&quot;&gt;&lt;script&gt;alter(&quot;hacked&quot;);&lt;/script&gt;">version 2.0.0 &lt;script&gt;alert(&quot;unsafe&quot;)&lt;/script&gt; or earlier</a>.';
121
-		$this->expectExceptionMessage($errorString);
122
-
123
-		/** @var RequestInterface|MockObject $request */
124
-		$request = $this->createMock('\Sabre\HTTP\RequestInterface');
125
-		$request
126
-			->expects($this->once())
127
-			->method('getHeader')
128
-			->with('User-Agent')
129
-			->willReturn($userAgent);
130
-
131
-		$this->blockLegacyClientVersionPlugin->beforeHandler($request);
132
-	}
133
-
134
-	public static function newAndAlternateDesktopClientProvider(): array {
135
-		return [
136
-			['Mozilla/5.0 (Windows) mirall/1.7.0'],
137
-			['Mozilla/5.0 (Bogus Text) mirall/1.9.3'],
138
-			['Mozilla/5.0 (Not Our Client But Old Version) LegacySync/1.1.0'],
139
-			['Mozilla/5.0 (Windows) mirall/4.7.0'],
140
-			['Mozilla/5.0 (Bogus Text) mirall/3.9.3'],
141
-			['Mozilla/5.0 (Not Our Client But Old Version) LegacySync/45.0.0'],
142
-		];
143
-	}
144
-
145
-	/**
146
-	 * @dataProvider newAndAlternateDesktopClientProvider
147
-	 */
148
-	public function testBeforeHandlerSuccess(string $userAgent): void {
149
-		/** @var RequestInterface|MockObject $request */
150
-		$request = $this->createMock(RequestInterface::class);
151
-		$request
152
-			->expects($this->once())
153
-			->method('getHeader')
154
-			->with('User-Agent')
155
-			->willReturn($userAgent);
156
-
157
-		$this->config
158
-			->expects($this->exactly(2))
159
-			->method('getSystemValueString')
160
-			->willReturnCallback(function (string $key) {
161
-				if ($key === 'minimum.supported.desktop.version') {
162
-					return '1.7.0';
163
-				}
164
-				return '10.0.0';
165
-			});
166
-
167
-		$this->blockLegacyClientVersionPlugin->beforeHandler($request);
168
-	}
169
-
170
-	public function testBeforeHandlerNoUserAgent(): void {
171
-		/** @var RequestInterface|MockObject $request */
172
-		$request = $this->createMock(RequestInterface::class);
173
-		$request
174
-			->expects($this->once())
175
-			->method('getHeader')
176
-			->with('User-Agent')
177
-			->willReturn(null);
178
-
179
-		$this->blockLegacyClientVersionPlugin->beforeHandler($request);
180
-	}
32
+    private IConfig&MockObject $config;
33
+    private ThemingDefaults&MockObject $themingDefaults;
34
+    private BlockLegacyClientPlugin $blockLegacyClientVersionPlugin;
35
+
36
+    protected function setUp(): void {
37
+        parent::setUp();
38
+
39
+        $this->config = $this->createMock(IConfig::class);
40
+        $this->themingDefaults = $this->createMock(ThemingDefaults::class);
41
+        $this->blockLegacyClientVersionPlugin = new BlockLegacyClientPlugin(
42
+            $this->config,
43
+            $this->themingDefaults,
44
+        );
45
+    }
46
+
47
+    public static function oldDesktopClientProvider(): array {
48
+        return [
49
+            ['Mozilla/5.0 (Windows) mirall/1.5.0', ERROR_TYPE::MIN_ERROR],
50
+            ['Mozilla/5.0 (Bogus Text) mirall/1.6.9', ERROR_TYPE::MIN_ERROR],
51
+            ['Mozilla/5.0 (Windows) mirall/2.5.0', ERROR_TYPE::MAX_ERROR],
52
+            ['Mozilla/5.0 (Bogus Text) mirall/2.0.1', ERROR_TYPE::MAX_ERROR],
53
+            ['Mozilla/5.0 (Windows) mirall/2.0.0', ERROR_TYPE::NONE],
54
+            ['Mozilla/5.0 (Bogus Text) mirall/2.0.0', ERROR_TYPE::NONE],
55
+        ];
56
+    }
57
+
58
+    /**
59
+     * @dataProvider oldDesktopClientProvider
60
+     */
61
+    public function testBeforeHandlerException(string $userAgent, ERROR_TYPE $errorType): void {
62
+        $this->themingDefaults
63
+            ->expects($this->atMost(1))
64
+            ->method('getSyncClientUrl')
65
+            ->willReturn('https://nextcloud.com/install/#install-clients');
66
+
67
+        $this->config
68
+            ->expects($this->exactly(2))
69
+            ->method('getSystemValueString')
70
+            ->willReturnCallback(function (string $key) {
71
+                if ($key === 'minimum.supported.desktop.version') {
72
+                    return '1.7.0';
73
+                }
74
+                return '2.0.0';
75
+            });
76
+
77
+        if ($errorType !== ERROR_TYPE::NONE) {
78
+            $errorString = $errorType === ERROR_TYPE::MIN_ERROR
79
+            ? 'This version of the client is unsupported. Upgrade to <a href="https://nextcloud.com/install/#install-clients">version 1.7.0 or later</a>.'
80
+            : 'This version of the client is unsupported. Downgrade to <a href="https://nextcloud.com/install/#install-clients">version 2.0.0 or earlier</a>.';
81
+            $this->expectException(\Sabre\DAV\Exception\Forbidden::class);
82
+            $this->expectExceptionMessage($errorString);
83
+        }
84
+
85
+        /** @var RequestInterface|MockObject $request */
86
+        $request = $this->createMock(RequestInterface::class);
87
+        $request
88
+            ->expects($this->once())
89
+            ->method('getHeader')
90
+            ->with('User-Agent')
91
+            ->willReturn($userAgent);
92
+
93
+        $this->blockLegacyClientVersionPlugin->beforeHandler($request);
94
+    }
95
+
96
+    /**
97
+     * Ensure that there is no room for XSS attack through configured URL / version
98
+     * @dataProvider oldDesktopClientProvider
99
+     */
100
+    public function testBeforeHandlerExceptionPreventXSSAttack(string $userAgent, ERROR_TYPE $errorType): void {
101
+        $this->expectException(\Sabre\DAV\Exception\Forbidden::class);
102
+
103
+        $this->themingDefaults
104
+            ->expects($this->atMost(1))
105
+            ->method('getSyncClientUrl')
106
+            ->willReturn('https://example.com"><script>alter("hacked");</script>');
107
+
108
+        $this->config
109
+            ->expects($this->exactly(2))
110
+            ->method('getSystemValueString')
111
+            ->willReturnCallback(function (string $key) {
112
+                if ($key === 'minimum.supported.desktop.version') {
113
+                    return '1.7.0 <script>alert("unsafe")</script>';
114
+                }
115
+                return '2.0.0 <script>alert("unsafe")</script>';
116
+            });
117
+
118
+        $errorString = $errorType === ERROR_TYPE::MIN_ERROR
119
+            ? 'This version of the client is unsupported. Upgrade to <a href="https://example.com&quot;&gt;&lt;script&gt;alter(&quot;hacked&quot;);&lt;/script&gt;">version 1.7.0 &lt;script&gt;alert(&quot;unsafe&quot;)&lt;/script&gt; or later</a>.'
120
+            : 'This version of the client is unsupported. Downgrade to <a href="https://example.com&quot;&gt;&lt;script&gt;alter(&quot;hacked&quot;);&lt;/script&gt;">version 2.0.0 &lt;script&gt;alert(&quot;unsafe&quot;)&lt;/script&gt; or earlier</a>.';
121
+        $this->expectExceptionMessage($errorString);
122
+
123
+        /** @var RequestInterface|MockObject $request */
124
+        $request = $this->createMock('\Sabre\HTTP\RequestInterface');
125
+        $request
126
+            ->expects($this->once())
127
+            ->method('getHeader')
128
+            ->with('User-Agent')
129
+            ->willReturn($userAgent);
130
+
131
+        $this->blockLegacyClientVersionPlugin->beforeHandler($request);
132
+    }
133
+
134
+    public static function newAndAlternateDesktopClientProvider(): array {
135
+        return [
136
+            ['Mozilla/5.0 (Windows) mirall/1.7.0'],
137
+            ['Mozilla/5.0 (Bogus Text) mirall/1.9.3'],
138
+            ['Mozilla/5.0 (Not Our Client But Old Version) LegacySync/1.1.0'],
139
+            ['Mozilla/5.0 (Windows) mirall/4.7.0'],
140
+            ['Mozilla/5.0 (Bogus Text) mirall/3.9.3'],
141
+            ['Mozilla/5.0 (Not Our Client But Old Version) LegacySync/45.0.0'],
142
+        ];
143
+    }
144
+
145
+    /**
146
+     * @dataProvider newAndAlternateDesktopClientProvider
147
+     */
148
+    public function testBeforeHandlerSuccess(string $userAgent): void {
149
+        /** @var RequestInterface|MockObject $request */
150
+        $request = $this->createMock(RequestInterface::class);
151
+        $request
152
+            ->expects($this->once())
153
+            ->method('getHeader')
154
+            ->with('User-Agent')
155
+            ->willReturn($userAgent);
156
+
157
+        $this->config
158
+            ->expects($this->exactly(2))
159
+            ->method('getSystemValueString')
160
+            ->willReturnCallback(function (string $key) {
161
+                if ($key === 'minimum.supported.desktop.version') {
162
+                    return '1.7.0';
163
+                }
164
+                return '10.0.0';
165
+            });
166
+
167
+        $this->blockLegacyClientVersionPlugin->beforeHandler($request);
168
+    }
169
+
170
+    public function testBeforeHandlerNoUserAgent(): void {
171
+        /** @var RequestInterface|MockObject $request */
172
+        $request = $this->createMock(RequestInterface::class);
173
+        $request
174
+            ->expects($this->once())
175
+            ->method('getHeader')
176
+            ->with('User-Agent')
177
+            ->willReturn(null);
178
+
179
+        $this->blockLegacyClientVersionPlugin->beforeHandler($request);
180
+    }
181 181
 }
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php 1 patch
Indentation   +908 added lines, -908 removed lines patch added patch discarded remove patch
@@ -32,912 +32,912 @@
 block discarded – undo
32 32
 use Test\TestCase;
33 33
 
34 34
 class PrincipalTest extends TestCase {
35
-	private IUserManager&MockObject $userManager;
36
-	private IGroupManager&MockObject $groupManager;
37
-	private IAccountManager&MockObject $accountManager;
38
-	private IManager&MockObject $shareManager;
39
-	private IUserSession&MockObject $userSession;
40
-	private IAppManager&MockObject $appManager;
41
-	private ProxyMapper&MockObject $proxyMapper;
42
-	private KnownUserService&MockObject $knownUserService;
43
-	private IConfig&MockObject $config;
44
-	private IFactory&MockObject $languageFactory;
45
-	private Principal $connector;
46
-
47
-	protected function setUp(): void {
48
-		parent::setUp();
49
-
50
-		$this->userManager = $this->createMock(IUserManager::class);
51
-		$this->groupManager = $this->createMock(IGroupManager::class);
52
-		$this->accountManager = $this->createMock(IAccountManager::class);
53
-		$this->shareManager = $this->createMock(IManager::class);
54
-		$this->userSession = $this->createMock(IUserSession::class);
55
-		$this->appManager = $this->createMock(IAppManager::class);
56
-		$this->proxyMapper = $this->createMock(ProxyMapper::class);
57
-		$this->knownUserService = $this->createMock(KnownUserService::class);
58
-		$this->config = $this->createMock(IConfig::class);
59
-		$this->languageFactory = $this->createMock(IFactory::class);
60
-
61
-		$this->connector = new Principal(
62
-			$this->userManager,
63
-			$this->groupManager,
64
-			$this->accountManager,
65
-			$this->shareManager,
66
-			$this->userSession,
67
-			$this->appManager,
68
-			$this->proxyMapper,
69
-			$this->knownUserService,
70
-			$this->config,
71
-			$this->languageFactory
72
-		);
73
-	}
74
-
75
-	public function testGetPrincipalsByPrefixWithoutPrefix(): void {
76
-		$response = $this->connector->getPrincipalsByPrefix('');
77
-		$this->assertSame([], $response);
78
-	}
79
-
80
-	public function testGetPrincipalsByPrefixWithUsers(): void {
81
-		$fooUser = $this->createMock(User::class);
82
-		$fooUser
83
-			->expects($this->once())
84
-			->method('getUID')
85
-			->willReturn('foo');
86
-		$fooUser
87
-			->expects($this->once())
88
-			->method('getDisplayName')
89
-			->willReturn('Dr. Foo-Bar');
90
-		$fooUser
91
-			->expects($this->once())
92
-			->method('getSystemEMailAddress')
93
-			->willReturn('');
94
-		$barUser = $this->createMock(User::class);
95
-		$barUser
96
-			->expects($this->once())
97
-			->method('getUID')
98
-			->willReturn('bar');
99
-		$barUser
100
-			->expects($this->once())
101
-			->method('getSystemEMailAddress')
102
-			->willReturn('[email protected]');
103
-		$this->userManager
104
-			->expects($this->once())
105
-			->method('search')
106
-			->with('')
107
-			->willReturn([$fooUser, $barUser]);
108
-
109
-		$this->languageFactory
110
-			->expects($this->exactly(2))
111
-			->method('getUserLanguage')
112
-			->willReturnMap([
113
-				[$fooUser, 'de'],
114
-				[$barUser, 'en'],
115
-			]);
116
-
117
-		$fooAccountPropertyCollection = $this->createMock(IAccountPropertyCollection::class);
118
-		$fooAccountPropertyCollection->expects($this->once())
119
-			->method('getProperties')
120
-			->willReturn([]);
121
-		$fooAccount = $this->createMock(IAccount::class);
122
-		$fooAccount->expects($this->once())
123
-			->method('getPropertyCollection')
124
-			->with(IAccountManager::COLLECTION_EMAIL)
125
-			->willReturn($fooAccountPropertyCollection);
126
-
127
-		$emailPropertyOne = $this->createMock(IAccountProperty::class);
128
-		$emailPropertyOne->expects($this->once())
129
-			->method('getValue')
130
-			->willReturn('[email protected]');
131
-		$emailPropertyTwo = $this->createMock(IAccountProperty::class);
132
-		$emailPropertyTwo->expects($this->once())
133
-			->method('getValue')
134
-			->willReturn('[email protected]');
135
-
136
-		$barAccountPropertyCollection = $this->createMock(IAccountPropertyCollection::class);
137
-		$barAccountPropertyCollection->expects($this->once())
138
-			->method('getProperties')
139
-			->willReturn([$emailPropertyOne, $emailPropertyTwo]);
140
-		$barAccount = $this->createMock(IAccount::class);
141
-		$barAccount->expects($this->once())
142
-			->method('getPropertyCollection')
143
-			->with(IAccountManager::COLLECTION_EMAIL)
144
-			->willReturn($barAccountPropertyCollection);
145
-
146
-		$this->accountManager
147
-			->expects($this->exactly(2))
148
-			->method('getAccount')
149
-			->willReturnMap([
150
-				[$fooUser, $fooAccount],
151
-				[$barUser, $barAccount],
152
-			]);
153
-
154
-		$expectedResponse = [
155
-			0 => [
156
-				'uri' => 'principals/users/foo',
157
-				'{DAV:}displayname' => 'Dr. Foo-Bar',
158
-				'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
159
-				'{http://nextcloud.com/ns}language' => 'de',
160
-			],
161
-			1 => [
162
-				'uri' => 'principals/users/bar',
163
-				'{DAV:}displayname' => 'bar',
164
-				'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
165
-				'{http://nextcloud.com/ns}language' => 'en',
166
-				'{http://sabredav.org/ns}email-address' => '[email protected]',
167
-				'{DAV:}alternate-URI-set' => ['mailto:[email protected]', 'mailto:[email protected]']
168
-			]
169
-		];
170
-		$response = $this->connector->getPrincipalsByPrefix('principals/users');
171
-		$this->assertSame($expectedResponse, $response);
172
-	}
173
-
174
-	public function testGetPrincipalsByPrefixEmpty(): void {
175
-		$this->userManager
176
-			->expects($this->once())
177
-			->method('search')
178
-			->with('')
179
-			->willReturn([]);
180
-
181
-		$response = $this->connector->getPrincipalsByPrefix('principals/users');
182
-		$this->assertSame([], $response);
183
-	}
184
-
185
-	public function testGetPrincipalsByPathWithoutMail(): void {
186
-		$fooUser = $this->createMock(User::class);
187
-		$fooUser
188
-			->expects($this->once())
189
-			->method('getUID')
190
-			->willReturn('foo');
191
-		$this->userManager
192
-			->expects($this->once())
193
-			->method('get')
194
-			->with('foo')
195
-			->willReturn($fooUser);
196
-
197
-		$this->languageFactory
198
-			->expects($this->once())
199
-			->method('getUserLanguage')
200
-			->with($fooUser)
201
-			->willReturn('de');
202
-
203
-		$expectedResponse = [
204
-			'uri' => 'principals/users/foo',
205
-			'{DAV:}displayname' => 'foo',
206
-			'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
207
-			'{http://nextcloud.com/ns}language' => 'de'
208
-		];
209
-		$response = $this->connector->getPrincipalByPath('principals/users/foo');
210
-		$this->assertSame($expectedResponse, $response);
211
-	}
212
-
213
-	public function testGetPrincipalsByPathWithMail(): void {
214
-		$fooUser = $this->createMock(User::class);
215
-		$fooUser
216
-			->expects($this->once())
217
-			->method('getSystemEMailAddress')
218
-			->willReturn('[email protected]');
219
-		$fooUser
220
-			->expects($this->once())
221
-			->method('getUID')
222
-			->willReturn('foo');
223
-		$this->userManager
224
-			->expects($this->once())
225
-			->method('get')
226
-			->with('foo')
227
-			->willReturn($fooUser);
228
-
229
-		$this->languageFactory
230
-			->expects($this->once())
231
-			->method('getUserLanguage')
232
-			->with($fooUser)
233
-			->willReturn('de');
234
-
235
-		$expectedResponse = [
236
-			'uri' => 'principals/users/foo',
237
-			'{DAV:}displayname' => 'foo',
238
-			'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
239
-			'{http://nextcloud.com/ns}language' => 'de',
240
-			'{http://sabredav.org/ns}email-address' => '[email protected]',
241
-		];
242
-		$response = $this->connector->getPrincipalByPath('principals/users/foo');
243
-		$this->assertSame($expectedResponse, $response);
244
-	}
245
-
246
-	public function testGetPrincipalsByPathEmpty(): void {
247
-		$this->userManager
248
-			->expects($this->once())
249
-			->method('get')
250
-			->with('foo')
251
-			->willReturn(null);
252
-
253
-		$response = $this->connector->getPrincipalByPath('principals/users/foo');
254
-		$this->assertNull($response);
255
-	}
256
-
257
-	public function testGetGroupMemberSet(): void {
258
-		$response = $this->connector->getGroupMemberSet('principals/users/foo');
259
-		$this->assertSame([], $response);
260
-	}
261
-
262
-
263
-	public function testGetGroupMemberSetEmpty(): void {
264
-		$this->expectException(Exception::class);
265
-		$this->expectExceptionMessage('Principal not found');
266
-
267
-		$this->userManager
268
-			->expects($this->once())
269
-			->method('get')
270
-			->with('foo')
271
-			->willReturn(null);
272
-
273
-		$this->connector->getGroupMemberSet('principals/users/foo/calendar-proxy-read');
274
-	}
275
-
276
-	public function testGetGroupMemberSetProxyRead(): void {
277
-		$fooUser = $this->createMock(User::class);
278
-		$fooUser
279
-			->expects($this->once())
280
-			->method('getUID')
281
-			->willReturn('foo');
282
-		$this->userManager
283
-			->expects($this->once())
284
-			->method('get')
285
-			->with('foo')
286
-			->willReturn($fooUser);
287
-
288
-		$proxy1 = new Proxy();
289
-		$proxy1->setProxyId('proxyId1');
290
-		$proxy1->setPermissions(1);
291
-
292
-		$proxy2 = new Proxy();
293
-		$proxy2->setProxyId('proxyId2');
294
-		$proxy2->setPermissions(3);
295
-
296
-		$proxy3 = new Proxy();
297
-		$proxy3->setProxyId('proxyId3');
298
-		$proxy3->setPermissions(3);
299
-
300
-		$this->proxyMapper->expects($this->once())
301
-			->method('getProxiesOf')
302
-			->with('principals/users/foo')
303
-			->willReturn([$proxy1, $proxy2, $proxy3]);
304
-
305
-		$this->assertEquals(['proxyId1'], $this->connector->getGroupMemberSet('principals/users/foo/calendar-proxy-read'));
306
-	}
307
-
308
-	public function testGetGroupMemberSetProxyWrite(): void {
309
-		$fooUser = $this->createMock(User::class);
310
-		$fooUser
311
-			->expects($this->once())
312
-			->method('getUID')
313
-			->willReturn('foo');
314
-		$this->userManager
315
-			->expects($this->once())
316
-			->method('get')
317
-			->with('foo')
318
-			->willReturn($fooUser);
319
-
320
-		$proxy1 = new Proxy();
321
-		$proxy1->setProxyId('proxyId1');
322
-		$proxy1->setPermissions(1);
323
-
324
-		$proxy2 = new Proxy();
325
-		$proxy2->setProxyId('proxyId2');
326
-		$proxy2->setPermissions(3);
327
-
328
-		$proxy3 = new Proxy();
329
-		$proxy3->setProxyId('proxyId3');
330
-		$proxy3->setPermissions(3);
331
-
332
-		$this->proxyMapper->expects($this->once())
333
-			->method('getProxiesOf')
334
-			->with('principals/users/foo')
335
-			->willReturn([$proxy1, $proxy2, $proxy3]);
336
-
337
-		$this->assertEquals(['proxyId2', 'proxyId3'], $this->connector->getGroupMemberSet('principals/users/foo/calendar-proxy-write'));
338
-	}
339
-
340
-	public function testGetGroupMembership(): void {
341
-		$fooUser = $this->createMock(User::class);
342
-		$group1 = $this->createMock(IGroup::class);
343
-		$group1->expects($this->once())
344
-			->method('getGID')
345
-			->willReturn('group1');
346
-		$group2 = $this->createMock(IGroup::class);
347
-		$group2->expects($this->once())
348
-			->method('getGID')
349
-			->willReturn('foo/bar');
350
-		$this->userManager
351
-			->expects($this->exactly(2))
352
-			->method('get')
353
-			->with('foo')
354
-			->willReturn($fooUser);
355
-		$this->groupManager
356
-			->expects($this->once())
357
-			->method('getUserGroups')
358
-			->with($fooUser)
359
-			->willReturn([
360
-				$group1,
361
-				$group2,
362
-			]);
363
-
364
-		$proxy1 = new Proxy();
365
-		$proxy1->setOwnerId('proxyId1');
366
-		$proxy1->setPermissions(1);
367
-
368
-		$proxy2 = new Proxy();
369
-		$proxy2->setOwnerId('proxyId2');
370
-		$proxy2->setPermissions(3);
371
-
372
-		$this->proxyMapper->expects($this->once())
373
-			->method('getProxiesFor')
374
-			->with('principals/users/foo')
375
-			->willReturn([$proxy1, $proxy2]);
376
-
377
-		$expectedResponse = [
378
-			'principals/groups/group1',
379
-			'principals/groups/foo%2Fbar',
380
-			'proxyId1/calendar-proxy-read',
381
-			'proxyId2/calendar-proxy-write',
382
-		];
383
-		$response = $this->connector->getGroupMembership('principals/users/foo');
384
-		$this->assertSame($expectedResponse, $response);
385
-	}
386
-
387
-
388
-	public function testGetGroupMembershipEmpty(): void {
389
-		$this->expectException(Exception::class);
390
-		$this->expectExceptionMessage('Principal not found');
391
-
392
-		$this->userManager
393
-			->expects($this->once())
394
-			->method('get')
395
-			->with('foo')
396
-			->willReturn(null);
397
-
398
-		$this->connector->getGroupMembership('principals/users/foo');
399
-	}
400
-
401
-
402
-	public function testSetGroupMembership(): void {
403
-		$this->expectException(Exception::class);
404
-		$this->expectExceptionMessage('Setting members of the group is not supported yet');
405
-
406
-		$this->connector->setGroupMemberSet('principals/users/foo', ['foo']);
407
-	}
408
-
409
-	public function testSetGroupMembershipProxy(): void {
410
-		$fooUser = $this->createMock(User::class);
411
-		$fooUser
412
-			->expects($this->once())
413
-			->method('getUID')
414
-			->willReturn('foo');
415
-		$barUser = $this->createMock(User::class);
416
-		$barUser
417
-			->expects($this->once())
418
-			->method('getUID')
419
-			->willReturn('bar');
420
-		$this->userManager
421
-			->expects($this->exactly(2))
422
-			->method('get')
423
-			->willReturnMap([
424
-				['foo', $fooUser],
425
-				['bar', $barUser],
426
-			]);
427
-
428
-		$this->proxyMapper->expects($this->once())
429
-			->method('getProxiesOf')
430
-			->with('principals/users/foo')
431
-			->willReturn([]);
432
-
433
-		$this->proxyMapper->expects($this->once())
434
-			->method('insert')
435
-			->with($this->callback(function ($proxy) {
436
-				/** @var Proxy $proxy */
437
-				if ($proxy->getOwnerId() !== 'principals/users/foo') {
438
-					return false;
439
-				}
440
-				if ($proxy->getProxyId() !== 'principals/users/bar') {
441
-					return false;
442
-				}
443
-				if ($proxy->getPermissions() !== 3) {
444
-					return false;
445
-				}
446
-
447
-				return true;
448
-			}));
449
-
450
-		$this->connector->setGroupMemberSet('principals/users/foo/calendar-proxy-write', ['principals/users/bar']);
451
-	}
452
-
453
-	public function testUpdatePrincipal(): void {
454
-		$this->assertSame(0, $this->connector->updatePrincipal('foo', new PropPatch([])));
455
-	}
456
-
457
-	public function testSearchPrincipalsWithEmptySearchProperties(): void {
458
-		$this->assertSame([], $this->connector->searchPrincipals('principals/users', []));
459
-	}
460
-
461
-	public function testSearchPrincipalsWithWrongPrefixPath(): void {
462
-		$this->assertSame([], $this->connector->searchPrincipals('principals/groups',
463
-			['{http://sabredav.org/ns}email-address' => 'foo']));
464
-	}
465
-
466
-	/**
467
-	 * @dataProvider searchPrincipalsDataProvider
468
-	 */
469
-	public function testSearchPrincipals(bool $sharingEnabled, bool $groupsOnly, string $test, array $result): void {
470
-		$this->shareManager->expects($this->once())
471
-			->method('shareAPIEnabled')
472
-			->willReturn($sharingEnabled);
473
-
474
-		$getUserGroupIdsReturnMap = [];
475
-
476
-		if ($sharingEnabled) {
477
-			$this->shareManager->expects($this->once())
478
-				->method('allowEnumeration')
479
-				->willReturn(true);
480
-
481
-			$this->shareManager->expects($this->once())
482
-				->method('shareWithGroupMembersOnly')
483
-				->willReturn($groupsOnly);
484
-
485
-			if ($groupsOnly) {
486
-				$user = $this->createMock(IUser::class);
487
-				$this->userSession->expects($this->atLeastOnce())
488
-					->method('getUser')
489
-					->willReturn($user);
490
-
491
-				$getUserGroupIdsReturnMap[] = [$user, ['group1', 'group2', 'group5']];
492
-			}
493
-		} else {
494
-			$this->config->expects($this->never())
495
-				->method('getAppValue');
496
-			$this->shareManager->expects($this->never())
497
-				->method('shareWithGroupMembersOnly');
498
-			$this->groupManager->expects($this->never())
499
-				->method($this->anything());
500
-		}
501
-
502
-		$user2 = $this->createMock(IUser::class);
503
-		$user2->method('getUID')->willReturn('user2');
504
-		$user3 = $this->createMock(IUser::class);
505
-		$user3->method('getUID')->willReturn('user3');
506
-		$user4 = $this->createMock(IUser::class);
507
-		$user4->method('getUID')->willReturn('user4');
508
-
509
-		if ($sharingEnabled) {
510
-			$this->userManager->expects($this->once())
511
-				->method('getByEmail')
512
-				->with('[email protected]')
513
-				->willReturn([$user2, $user3]);
514
-
515
-			$this->userManager->expects($this->once())
516
-				->method('searchDisplayName')
517
-				->with('User 12')
518
-				->willReturn([$user3, $user4]);
519
-		} else {
520
-			$this->userManager->expects($this->never())
521
-				->method('getByEmail');
522
-
523
-			$this->userManager->expects($this->never())
524
-				->method('searchDisplayName');
525
-		}
526
-
527
-		if ($sharingEnabled && $groupsOnly) {
528
-			$getUserGroupIdsReturnMap[] = [$user2, ['group1', 'group3']];
529
-			$getUserGroupIdsReturnMap[] = [$user3, ['group3', 'group4']];
530
-			$getUserGroupIdsReturnMap[] = [$user4, ['group4', 'group5']];
531
-		}
532
-
533
-		$this->groupManager->expects($this->any())
534
-			->method('getUserGroupIds')
535
-			->willReturnMap($getUserGroupIdsReturnMap);
536
-
537
-
538
-		$this->assertEquals($result, $this->connector->searchPrincipals('principals/users',
539
-			['{http://sabredav.org/ns}email-address' => '[email protected]',
540
-				'{DAV:}displayname' => 'User 12'], $test));
541
-	}
542
-
543
-	public static function searchPrincipalsDataProvider(): array {
544
-		return [
545
-			[true, false, 'allof', ['principals/users/user3']],
546
-			[true, false, 'anyof', ['principals/users/user2', 'principals/users/user3', 'principals/users/user4']],
547
-			[true, true, 'allof', []],
548
-			[true, true, 'anyof', ['principals/users/user2', 'principals/users/user4']],
549
-			[false, false, 'allof', []],
550
-			[false, false, 'anyof', []],
551
-		];
552
-	}
553
-
554
-	public function testSearchPrincipalByCalendarUserAddressSet(): void {
555
-		$this->shareManager->expects($this->exactly(2))
556
-			->method('shareAPIEnabled')
557
-			->willReturn(true);
558
-
559
-		$this->shareManager->expects($this->exactly(2))
560
-			->method('allowEnumeration')
561
-			->willReturn(true);
562
-
563
-		$this->shareManager->expects($this->exactly(2))
564
-			->method('shareWithGroupMembersOnly')
565
-			->willReturn(false);
566
-
567
-		$user2 = $this->createMock(IUser::class);
568
-		$user2->method('getUID')->willReturn('user2');
569
-		$user3 = $this->createMock(IUser::class);
570
-		$user3->method('getUID')->willReturn('user3');
571
-
572
-		$this->userManager->expects($this->once())
573
-			->method('getByEmail')
574
-			->with('[email protected]')
575
-			->willReturn([$user2, $user3]);
576
-
577
-		$this->assertEquals([
578
-			'principals/users/user2',
579
-			'principals/users/user3',
580
-		], $this->connector->searchPrincipals('principals/users',
581
-			['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => '[email protected]']));
582
-	}
583
-
584
-	public function testSearchPrincipalWithEnumerationDisabledDisplayname(): void {
585
-		$this->shareManager->expects($this->once())
586
-			->method('shareAPIEnabled')
587
-			->willReturn(true);
588
-
589
-		$this->shareManager->expects($this->once())
590
-			->method('allowEnumeration')
591
-			->willReturn(false);
592
-
593
-		$this->shareManager->expects($this->once())
594
-			->method('shareWithGroupMembersOnly')
595
-			->willReturn(false);
596
-
597
-		$this->shareManager->expects($this->once())
598
-			->method('allowEnumerationFullMatch')
599
-			->willReturn(true);
600
-
601
-		$user2 = $this->createMock(IUser::class);
602
-		$user2->method('getUID')->willReturn('user2');
603
-		$user2->method('getDisplayName')->willReturn('User 2');
604
-		$user2->method('getSystemEMailAddress')->willReturn('[email protected]');
605
-		$user3 = $this->createMock(IUser::class);
606
-		$user3->method('getUID')->willReturn('user3');
607
-		$user3->method('getDisplayName')->willReturn('User 22');
608
-		$user3->method('getSystemEMailAddress')->willReturn('[email protected]');
609
-		$user4 = $this->createMock(IUser::class);
610
-		$user4->method('getUID')->willReturn('user4');
611
-		$user4->method('getDisplayName')->willReturn('User 222');
612
-		$user4->method('getSystemEMailAddress')->willReturn('[email protected]');
613
-
614
-		$this->userManager->expects($this->once())
615
-			->method('searchDisplayName')
616
-			->with('User 2')
617
-			->willReturn([$user2, $user3, $user4]);
618
-
619
-		$this->assertEquals(['principals/users/user2'], $this->connector->searchPrincipals('principals/users',
620
-			['{DAV:}displayname' => 'User 2']));
621
-	}
622
-
623
-	public function testSearchPrincipalWithEnumerationDisabledDisplaynameOnFullMatch(): void {
624
-		$this->shareManager->expects($this->once())
625
-			->method('shareAPIEnabled')
626
-			->willReturn(true);
627
-
628
-		$this->shareManager->expects($this->once())
629
-			->method('allowEnumeration')
630
-			->willReturn(false);
631
-
632
-		$this->shareManager->expects($this->once())
633
-			->method('shareWithGroupMembersOnly')
634
-			->willReturn(false);
635
-
636
-		$this->shareManager->expects($this->once())
637
-			->method('allowEnumerationFullMatch')
638
-			->willReturn(false);
639
-
640
-		$this->assertEquals([], $this->connector->searchPrincipals('principals/users',
641
-			['{DAV:}displayname' => 'User 2']));
642
-	}
643
-
644
-	public function testSearchPrincipalWithEnumerationDisabledEmail(): void {
645
-		$this->shareManager->expects($this->once())
646
-			->method('shareAPIEnabled')
647
-			->willReturn(true);
648
-
649
-		$this->shareManager->expects($this->once())
650
-			->method('allowEnumeration')
651
-			->willReturn(false);
652
-
653
-		$this->shareManager->expects($this->once())
654
-			->method('shareWithGroupMembersOnly')
655
-			->willReturn(false);
656
-
657
-		$this->shareManager->expects($this->once())
658
-			->method('allowEnumerationFullMatch')
659
-			->willReturn(true);
660
-
661
-		$this->shareManager->expects($this->once())
662
-			->method('matchEmail')
663
-			->willReturn(true);
664
-
665
-		$user2 = $this->createMock(IUser::class);
666
-		$user2->method('getUID')->willReturn('user2');
667
-		$user2->method('getDisplayName')->willReturn('User 2');
668
-		$user2->method('getSystemEMailAddress')->willReturn('[email protected]');
669
-		$user3 = $this->createMock(IUser::class);
670
-		$user3->method('getUID')->willReturn('user3');
671
-		$user2->method('getDisplayName')->willReturn('User 22');
672
-		$user2->method('getSystemEMailAddress')->willReturn('[email protected]');
673
-		$user4 = $this->createMock(IUser::class);
674
-		$user4->method('getUID')->willReturn('user4');
675
-		$user2->method('getDisplayName')->willReturn('User 222');
676
-		$user2->method('getSystemEMailAddress')->willReturn('[email protected]');
677
-
678
-		$this->userManager->expects($this->once())
679
-			->method('getByEmail')
680
-			->with('[email protected]')
681
-			->willReturn([$user2]);
682
-
683
-		$this->assertEquals(['principals/users/user2'], $this->connector->searchPrincipals('principals/users',
684
-			['{http://sabredav.org/ns}email-address' => '[email protected]']));
685
-	}
686
-
687
-	public function testSearchPrincipalWithEnumerationDisabledEmailOnFullMatch(): void {
688
-		$this->shareManager->expects($this->once())
689
-			->method('shareAPIEnabled')
690
-			->willReturn(true);
691
-
692
-		$this->shareManager->expects($this->once())
693
-			->method('allowEnumeration')
694
-			->willReturn(false);
695
-
696
-		$this->shareManager->expects($this->once())
697
-			->method('shareWithGroupMembersOnly')
698
-			->willReturn(false);
699
-
700
-		$this->shareManager->expects($this->once())
701
-			->method('allowEnumerationFullMatch')
702
-			->willReturn(false);
703
-
704
-
705
-		$this->assertEquals([], $this->connector->searchPrincipals('principals/users',
706
-			['{http://sabredav.org/ns}email-address' => '[email protected]']));
707
-	}
708
-
709
-	public function testSearchPrincipalWithEnumerationLimitedDisplayname(): void {
710
-		$this->shareManager->expects($this->once())
711
-			->method('shareAPIEnabled')
712
-			->willReturn(true);
713
-
714
-		$this->shareManager->expects($this->once())
715
-			->method('allowEnumeration')
716
-			->willReturn(true);
717
-
718
-		$this->shareManager->expects($this->once())
719
-			->method('limitEnumerationToGroups')
720
-			->willReturn(true);
721
-
722
-		$this->shareManager->expects($this->once())
723
-			->method('shareWithGroupMembersOnly')
724
-			->willReturn(false);
725
-
726
-		$user2 = $this->createMock(IUser::class);
727
-		$user2->method('getUID')->willReturn('user2');
728
-		$user2->method('getDisplayName')->willReturn('User 2');
729
-		$user2->method('getSystemEMailAddress')->willReturn('[email protected]');
730
-		$user3 = $this->createMock(IUser::class);
731
-		$user3->method('getUID')->willReturn('user3');
732
-		$user3->method('getDisplayName')->willReturn('User 22');
733
-		$user3->method('getSystemEMailAddress')->willReturn('[email protected]');
734
-		$user4 = $this->createMock(IUser::class);
735
-		$user4->method('getUID')->willReturn('user4');
736
-		$user4->method('getDisplayName')->willReturn('User 222');
737
-		$user4->method('getSystemEMailAddress')->willReturn('[email protected]');
738
-
739
-
740
-		$this->userSession->expects($this->once())
741
-			->method('getUser')
742
-			->willReturn($user2);
743
-
744
-		$this->groupManager->expects($this->exactly(4))
745
-			->method('getUserGroupIds')
746
-			->willReturnMap([
747
-				[$user2, ['group1']],
748
-				[$user3, ['group1']],
749
-				[$user4, ['group2']],
750
-			]);
751
-
752
-		$this->userManager->expects($this->once())
753
-			->method('searchDisplayName')
754
-			->with('User')
755
-			->willReturn([$user2, $user3, $user4]);
756
-
757
-
758
-		$this->assertEquals([
759
-			'principals/users/user2',
760
-			'principals/users/user3',
761
-		], $this->connector->searchPrincipals('principals/users',
762
-			['{DAV:}displayname' => 'User']));
763
-	}
764
-
765
-	public function testSearchPrincipalWithEnumerationLimitedMail(): void {
766
-		$this->shareManager->expects($this->once())
767
-			->method('shareAPIEnabled')
768
-			->willReturn(true);
769
-
770
-		$this->shareManager->expects($this->once())
771
-			->method('allowEnumeration')
772
-			->willReturn(true);
773
-
774
-		$this->shareManager->expects($this->once())
775
-			->method('limitEnumerationToGroups')
776
-			->willReturn(true);
777
-
778
-		$this->shareManager->expects($this->once())
779
-			->method('shareWithGroupMembersOnly')
780
-			->willReturn(false);
781
-
782
-		$user2 = $this->createMock(IUser::class);
783
-		$user2->method('getUID')->willReturn('user2');
784
-		$user2->method('getDisplayName')->willReturn('User 2');
785
-		$user2->method('getSystemEMailAddress')->willReturn('[email protected]');
786
-		$user3 = $this->createMock(IUser::class);
787
-		$user3->method('getUID')->willReturn('user3');
788
-		$user3->method('getDisplayName')->willReturn('User 22');
789
-		$user3->method('getSystemEMailAddress')->willReturn('[email protected]');
790
-		$user4 = $this->createMock(IUser::class);
791
-		$user4->method('getUID')->willReturn('user4');
792
-		$user4->method('getDisplayName')->willReturn('User 222');
793
-		$user4->method('getSystemEMailAddress')->willReturn('[email protected]');
794
-
795
-
796
-		$this->userSession->expects($this->once())
797
-			->method('getUser')
798
-			->willReturn($user2);
799
-
800
-		$this->groupManager->expects($this->exactly(4))
801
-			->method('getUserGroupIds')
802
-			->willReturnMap([
803
-				[$user2, ['group1']],
804
-				[$user3, ['group1']],
805
-				[$user4, ['group2']],
806
-			]);
807
-
808
-		$this->userManager->expects($this->once())
809
-			->method('getByEmail')
810
-			->with('user')
811
-			->willReturn([$user2, $user3, $user4]);
812
-
813
-
814
-		$this->assertEquals([
815
-			'principals/users/user2',
816
-			'principals/users/user3'
817
-		], $this->connector->searchPrincipals('principals/users',
818
-			['{http://sabredav.org/ns}email-address' => 'user']));
819
-	}
820
-
821
-	public function testFindByUriSharingApiDisabled(): void {
822
-		$this->shareManager->expects($this->once())
823
-			->method('shareApiEnabled')
824
-			->willReturn(false);
825
-
826
-		$this->assertEquals(null, $this->connector->findByUri('mailto:[email protected]', 'principals/users'));
827
-	}
828
-
829
-	/**
830
-	 * @dataProvider findByUriWithGroupRestrictionDataProvider
831
-	 */
832
-	public function testFindByUriWithGroupRestriction(string $uri, string $email, ?string $expects): void {
833
-		$this->shareManager->expects($this->once())
834
-			->method('shareApiEnabled')
835
-			->willReturn(true);
836
-
837
-		$this->shareManager->expects($this->once())
838
-			->method('shareWithGroupMembersOnly')
839
-			->willReturn(true);
840
-
841
-		$user = $this->createMock(IUser::class);
842
-		$this->userSession->expects($this->once())
843
-			->method('getUser')
844
-			->willReturn($user);
845
-
846
-		$user2 = $this->createMock(IUser::class);
847
-		$user2->method('getUID')->willReturn('user2');
848
-		$user3 = $this->createMock(IUser::class);
849
-		$user3->method('getUID')->willReturn('user3');
850
-
851
-		$this->userManager->expects($this->once())
852
-			->method('getByEmail')
853
-			->with($email)
854
-			->willReturn([$email === '[email protected]' ? $user2 : $user3]);
855
-
856
-		if ($email === '[email protected]') {
857
-			$this->groupManager->expects($this->exactly(2))
858
-				->method('getUserGroupIds')
859
-				->willReturnMap([
860
-					[$user, ['group1', 'group2']],
861
-					[$user2, ['group1', 'group3']],
862
-				]);
863
-		} else {
864
-			$this->groupManager->expects($this->exactly(2))
865
-				->method('getUserGroupIds')
866
-				->willReturnMap([
867
-					[$user, ['group1', 'group2']],
868
-					[$user3, ['group3', 'group3']],
869
-				]);
870
-		}
871
-
872
-		$this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users'));
873
-	}
874
-
875
-	public static function findByUriWithGroupRestrictionDataProvider(): array {
876
-		return [
877
-			['mailto:[email protected]', '[email protected]', 'principals/users/user2'],
878
-			['mailto:[email protected]', '[email protected]', null],
879
-		];
880
-	}
881
-
882
-	/**
883
-	 * @dataProvider findByUriWithoutGroupRestrictionDataProvider
884
-	 */
885
-	public function testFindByUriWithoutGroupRestriction(string $uri, string $email, string $expects): void {
886
-		$this->shareManager->expects($this->once())
887
-			->method('shareApiEnabled')
888
-			->willReturn(true);
889
-
890
-		$this->shareManager->expects($this->once())
891
-			->method('shareWithGroupMembersOnly')
892
-			->willReturn(false);
893
-
894
-		$user2 = $this->createMock(IUser::class);
895
-		$user2->method('getUID')->willReturn('user2');
896
-		$user3 = $this->createMock(IUser::class);
897
-		$user3->method('getUID')->willReturn('user3');
898
-
899
-		$this->userManager->expects($this->once())
900
-			->method('getByEmail')
901
-			->with($email)
902
-			->willReturn([$email === '[email protected]' ? $user2 : $user3]);
903
-
904
-		$this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users'));
905
-	}
906
-
907
-	public static function findByUriWithoutGroupRestrictionDataProvider(): array {
908
-		return [
909
-			['mailto:[email protected]', '[email protected]', 'principals/users/user2'],
910
-			['mailto:[email protected]', '[email protected]', 'principals/users/user3'],
911
-		];
912
-	}
913
-
914
-	public function testGetEmailAddressesOfPrincipal(): void {
915
-		$principal = [
916
-			'{http://sabredav.org/ns}email-address' => '[email protected]',
917
-			'{DAV:}alternate-URI-set' => [
918
-				'/some/url',
919
-				'mailto:[email protected]',
920
-				'mailto:[email protected]',
921
-			],
922
-			'{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => [
923
-				'mailto:[email protected]',
924
-				'mailto:[email protected]',
925
-			],
926
-			'{http://calendarserver.org/ns/}email-address-set' => [
927
-				'mailto:[email protected]',
928
-				'mailto:[email protected]',
929
-			],
930
-		];
931
-
932
-		$expected = [
933
-			'[email protected]',
934
-			'[email protected]',
935
-			'[email protected]',
936
-			'[email protected]',
937
-			'[email protected]',
938
-			'[email protected]',
939
-		];
940
-		$actual = $this->connector->getEmailAddressesOfPrincipal($principal);
941
-		$this->assertEquals($expected, $actual);
942
-	}
35
+    private IUserManager&MockObject $userManager;
36
+    private IGroupManager&MockObject $groupManager;
37
+    private IAccountManager&MockObject $accountManager;
38
+    private IManager&MockObject $shareManager;
39
+    private IUserSession&MockObject $userSession;
40
+    private IAppManager&MockObject $appManager;
41
+    private ProxyMapper&MockObject $proxyMapper;
42
+    private KnownUserService&MockObject $knownUserService;
43
+    private IConfig&MockObject $config;
44
+    private IFactory&MockObject $languageFactory;
45
+    private Principal $connector;
46
+
47
+    protected function setUp(): void {
48
+        parent::setUp();
49
+
50
+        $this->userManager = $this->createMock(IUserManager::class);
51
+        $this->groupManager = $this->createMock(IGroupManager::class);
52
+        $this->accountManager = $this->createMock(IAccountManager::class);
53
+        $this->shareManager = $this->createMock(IManager::class);
54
+        $this->userSession = $this->createMock(IUserSession::class);
55
+        $this->appManager = $this->createMock(IAppManager::class);
56
+        $this->proxyMapper = $this->createMock(ProxyMapper::class);
57
+        $this->knownUserService = $this->createMock(KnownUserService::class);
58
+        $this->config = $this->createMock(IConfig::class);
59
+        $this->languageFactory = $this->createMock(IFactory::class);
60
+
61
+        $this->connector = new Principal(
62
+            $this->userManager,
63
+            $this->groupManager,
64
+            $this->accountManager,
65
+            $this->shareManager,
66
+            $this->userSession,
67
+            $this->appManager,
68
+            $this->proxyMapper,
69
+            $this->knownUserService,
70
+            $this->config,
71
+            $this->languageFactory
72
+        );
73
+    }
74
+
75
+    public function testGetPrincipalsByPrefixWithoutPrefix(): void {
76
+        $response = $this->connector->getPrincipalsByPrefix('');
77
+        $this->assertSame([], $response);
78
+    }
79
+
80
+    public function testGetPrincipalsByPrefixWithUsers(): void {
81
+        $fooUser = $this->createMock(User::class);
82
+        $fooUser
83
+            ->expects($this->once())
84
+            ->method('getUID')
85
+            ->willReturn('foo');
86
+        $fooUser
87
+            ->expects($this->once())
88
+            ->method('getDisplayName')
89
+            ->willReturn('Dr. Foo-Bar');
90
+        $fooUser
91
+            ->expects($this->once())
92
+            ->method('getSystemEMailAddress')
93
+            ->willReturn('');
94
+        $barUser = $this->createMock(User::class);
95
+        $barUser
96
+            ->expects($this->once())
97
+            ->method('getUID')
98
+            ->willReturn('bar');
99
+        $barUser
100
+            ->expects($this->once())
101
+            ->method('getSystemEMailAddress')
102
+            ->willReturn('[email protected]');
103
+        $this->userManager
104
+            ->expects($this->once())
105
+            ->method('search')
106
+            ->with('')
107
+            ->willReturn([$fooUser, $barUser]);
108
+
109
+        $this->languageFactory
110
+            ->expects($this->exactly(2))
111
+            ->method('getUserLanguage')
112
+            ->willReturnMap([
113
+                [$fooUser, 'de'],
114
+                [$barUser, 'en'],
115
+            ]);
116
+
117
+        $fooAccountPropertyCollection = $this->createMock(IAccountPropertyCollection::class);
118
+        $fooAccountPropertyCollection->expects($this->once())
119
+            ->method('getProperties')
120
+            ->willReturn([]);
121
+        $fooAccount = $this->createMock(IAccount::class);
122
+        $fooAccount->expects($this->once())
123
+            ->method('getPropertyCollection')
124
+            ->with(IAccountManager::COLLECTION_EMAIL)
125
+            ->willReturn($fooAccountPropertyCollection);
126
+
127
+        $emailPropertyOne = $this->createMock(IAccountProperty::class);
128
+        $emailPropertyOne->expects($this->once())
129
+            ->method('getValue')
130
+            ->willReturn('[email protected]');
131
+        $emailPropertyTwo = $this->createMock(IAccountProperty::class);
132
+        $emailPropertyTwo->expects($this->once())
133
+            ->method('getValue')
134
+            ->willReturn('[email protected]');
135
+
136
+        $barAccountPropertyCollection = $this->createMock(IAccountPropertyCollection::class);
137
+        $barAccountPropertyCollection->expects($this->once())
138
+            ->method('getProperties')
139
+            ->willReturn([$emailPropertyOne, $emailPropertyTwo]);
140
+        $barAccount = $this->createMock(IAccount::class);
141
+        $barAccount->expects($this->once())
142
+            ->method('getPropertyCollection')
143
+            ->with(IAccountManager::COLLECTION_EMAIL)
144
+            ->willReturn($barAccountPropertyCollection);
145
+
146
+        $this->accountManager
147
+            ->expects($this->exactly(2))
148
+            ->method('getAccount')
149
+            ->willReturnMap([
150
+                [$fooUser, $fooAccount],
151
+                [$barUser, $barAccount],
152
+            ]);
153
+
154
+        $expectedResponse = [
155
+            0 => [
156
+                'uri' => 'principals/users/foo',
157
+                '{DAV:}displayname' => 'Dr. Foo-Bar',
158
+                '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
159
+                '{http://nextcloud.com/ns}language' => 'de',
160
+            ],
161
+            1 => [
162
+                'uri' => 'principals/users/bar',
163
+                '{DAV:}displayname' => 'bar',
164
+                '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
165
+                '{http://nextcloud.com/ns}language' => 'en',
166
+                '{http://sabredav.org/ns}email-address' => '[email protected]',
167
+                '{DAV:}alternate-URI-set' => ['mailto:[email protected]', 'mailto:[email protected]']
168
+            ]
169
+        ];
170
+        $response = $this->connector->getPrincipalsByPrefix('principals/users');
171
+        $this->assertSame($expectedResponse, $response);
172
+    }
173
+
174
+    public function testGetPrincipalsByPrefixEmpty(): void {
175
+        $this->userManager
176
+            ->expects($this->once())
177
+            ->method('search')
178
+            ->with('')
179
+            ->willReturn([]);
180
+
181
+        $response = $this->connector->getPrincipalsByPrefix('principals/users');
182
+        $this->assertSame([], $response);
183
+    }
184
+
185
+    public function testGetPrincipalsByPathWithoutMail(): void {
186
+        $fooUser = $this->createMock(User::class);
187
+        $fooUser
188
+            ->expects($this->once())
189
+            ->method('getUID')
190
+            ->willReturn('foo');
191
+        $this->userManager
192
+            ->expects($this->once())
193
+            ->method('get')
194
+            ->with('foo')
195
+            ->willReturn($fooUser);
196
+
197
+        $this->languageFactory
198
+            ->expects($this->once())
199
+            ->method('getUserLanguage')
200
+            ->with($fooUser)
201
+            ->willReturn('de');
202
+
203
+        $expectedResponse = [
204
+            'uri' => 'principals/users/foo',
205
+            '{DAV:}displayname' => 'foo',
206
+            '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
207
+            '{http://nextcloud.com/ns}language' => 'de'
208
+        ];
209
+        $response = $this->connector->getPrincipalByPath('principals/users/foo');
210
+        $this->assertSame($expectedResponse, $response);
211
+    }
212
+
213
+    public function testGetPrincipalsByPathWithMail(): void {
214
+        $fooUser = $this->createMock(User::class);
215
+        $fooUser
216
+            ->expects($this->once())
217
+            ->method('getSystemEMailAddress')
218
+            ->willReturn('[email protected]');
219
+        $fooUser
220
+            ->expects($this->once())
221
+            ->method('getUID')
222
+            ->willReturn('foo');
223
+        $this->userManager
224
+            ->expects($this->once())
225
+            ->method('get')
226
+            ->with('foo')
227
+            ->willReturn($fooUser);
228
+
229
+        $this->languageFactory
230
+            ->expects($this->once())
231
+            ->method('getUserLanguage')
232
+            ->with($fooUser)
233
+            ->willReturn('de');
234
+
235
+        $expectedResponse = [
236
+            'uri' => 'principals/users/foo',
237
+            '{DAV:}displayname' => 'foo',
238
+            '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
239
+            '{http://nextcloud.com/ns}language' => 'de',
240
+            '{http://sabredav.org/ns}email-address' => '[email protected]',
241
+        ];
242
+        $response = $this->connector->getPrincipalByPath('principals/users/foo');
243
+        $this->assertSame($expectedResponse, $response);
244
+    }
245
+
246
+    public function testGetPrincipalsByPathEmpty(): void {
247
+        $this->userManager
248
+            ->expects($this->once())
249
+            ->method('get')
250
+            ->with('foo')
251
+            ->willReturn(null);
252
+
253
+        $response = $this->connector->getPrincipalByPath('principals/users/foo');
254
+        $this->assertNull($response);
255
+    }
256
+
257
+    public function testGetGroupMemberSet(): void {
258
+        $response = $this->connector->getGroupMemberSet('principals/users/foo');
259
+        $this->assertSame([], $response);
260
+    }
261
+
262
+
263
+    public function testGetGroupMemberSetEmpty(): void {
264
+        $this->expectException(Exception::class);
265
+        $this->expectExceptionMessage('Principal not found');
266
+
267
+        $this->userManager
268
+            ->expects($this->once())
269
+            ->method('get')
270
+            ->with('foo')
271
+            ->willReturn(null);
272
+
273
+        $this->connector->getGroupMemberSet('principals/users/foo/calendar-proxy-read');
274
+    }
275
+
276
+    public function testGetGroupMemberSetProxyRead(): void {
277
+        $fooUser = $this->createMock(User::class);
278
+        $fooUser
279
+            ->expects($this->once())
280
+            ->method('getUID')
281
+            ->willReturn('foo');
282
+        $this->userManager
283
+            ->expects($this->once())
284
+            ->method('get')
285
+            ->with('foo')
286
+            ->willReturn($fooUser);
287
+
288
+        $proxy1 = new Proxy();
289
+        $proxy1->setProxyId('proxyId1');
290
+        $proxy1->setPermissions(1);
291
+
292
+        $proxy2 = new Proxy();
293
+        $proxy2->setProxyId('proxyId2');
294
+        $proxy2->setPermissions(3);
295
+
296
+        $proxy3 = new Proxy();
297
+        $proxy3->setProxyId('proxyId3');
298
+        $proxy3->setPermissions(3);
299
+
300
+        $this->proxyMapper->expects($this->once())
301
+            ->method('getProxiesOf')
302
+            ->with('principals/users/foo')
303
+            ->willReturn([$proxy1, $proxy2, $proxy3]);
304
+
305
+        $this->assertEquals(['proxyId1'], $this->connector->getGroupMemberSet('principals/users/foo/calendar-proxy-read'));
306
+    }
307
+
308
+    public function testGetGroupMemberSetProxyWrite(): void {
309
+        $fooUser = $this->createMock(User::class);
310
+        $fooUser
311
+            ->expects($this->once())
312
+            ->method('getUID')
313
+            ->willReturn('foo');
314
+        $this->userManager
315
+            ->expects($this->once())
316
+            ->method('get')
317
+            ->with('foo')
318
+            ->willReturn($fooUser);
319
+
320
+        $proxy1 = new Proxy();
321
+        $proxy1->setProxyId('proxyId1');
322
+        $proxy1->setPermissions(1);
323
+
324
+        $proxy2 = new Proxy();
325
+        $proxy2->setProxyId('proxyId2');
326
+        $proxy2->setPermissions(3);
327
+
328
+        $proxy3 = new Proxy();
329
+        $proxy3->setProxyId('proxyId3');
330
+        $proxy3->setPermissions(3);
331
+
332
+        $this->proxyMapper->expects($this->once())
333
+            ->method('getProxiesOf')
334
+            ->with('principals/users/foo')
335
+            ->willReturn([$proxy1, $proxy2, $proxy3]);
336
+
337
+        $this->assertEquals(['proxyId2', 'proxyId3'], $this->connector->getGroupMemberSet('principals/users/foo/calendar-proxy-write'));
338
+    }
339
+
340
+    public function testGetGroupMembership(): void {
341
+        $fooUser = $this->createMock(User::class);
342
+        $group1 = $this->createMock(IGroup::class);
343
+        $group1->expects($this->once())
344
+            ->method('getGID')
345
+            ->willReturn('group1');
346
+        $group2 = $this->createMock(IGroup::class);
347
+        $group2->expects($this->once())
348
+            ->method('getGID')
349
+            ->willReturn('foo/bar');
350
+        $this->userManager
351
+            ->expects($this->exactly(2))
352
+            ->method('get')
353
+            ->with('foo')
354
+            ->willReturn($fooUser);
355
+        $this->groupManager
356
+            ->expects($this->once())
357
+            ->method('getUserGroups')
358
+            ->with($fooUser)
359
+            ->willReturn([
360
+                $group1,
361
+                $group2,
362
+            ]);
363
+
364
+        $proxy1 = new Proxy();
365
+        $proxy1->setOwnerId('proxyId1');
366
+        $proxy1->setPermissions(1);
367
+
368
+        $proxy2 = new Proxy();
369
+        $proxy2->setOwnerId('proxyId2');
370
+        $proxy2->setPermissions(3);
371
+
372
+        $this->proxyMapper->expects($this->once())
373
+            ->method('getProxiesFor')
374
+            ->with('principals/users/foo')
375
+            ->willReturn([$proxy1, $proxy2]);
376
+
377
+        $expectedResponse = [
378
+            'principals/groups/group1',
379
+            'principals/groups/foo%2Fbar',
380
+            'proxyId1/calendar-proxy-read',
381
+            'proxyId2/calendar-proxy-write',
382
+        ];
383
+        $response = $this->connector->getGroupMembership('principals/users/foo');
384
+        $this->assertSame($expectedResponse, $response);
385
+    }
386
+
387
+
388
+    public function testGetGroupMembershipEmpty(): void {
389
+        $this->expectException(Exception::class);
390
+        $this->expectExceptionMessage('Principal not found');
391
+
392
+        $this->userManager
393
+            ->expects($this->once())
394
+            ->method('get')
395
+            ->with('foo')
396
+            ->willReturn(null);
397
+
398
+        $this->connector->getGroupMembership('principals/users/foo');
399
+    }
400
+
401
+
402
+    public function testSetGroupMembership(): void {
403
+        $this->expectException(Exception::class);
404
+        $this->expectExceptionMessage('Setting members of the group is not supported yet');
405
+
406
+        $this->connector->setGroupMemberSet('principals/users/foo', ['foo']);
407
+    }
408
+
409
+    public function testSetGroupMembershipProxy(): void {
410
+        $fooUser = $this->createMock(User::class);
411
+        $fooUser
412
+            ->expects($this->once())
413
+            ->method('getUID')
414
+            ->willReturn('foo');
415
+        $barUser = $this->createMock(User::class);
416
+        $barUser
417
+            ->expects($this->once())
418
+            ->method('getUID')
419
+            ->willReturn('bar');
420
+        $this->userManager
421
+            ->expects($this->exactly(2))
422
+            ->method('get')
423
+            ->willReturnMap([
424
+                ['foo', $fooUser],
425
+                ['bar', $barUser],
426
+            ]);
427
+
428
+        $this->proxyMapper->expects($this->once())
429
+            ->method('getProxiesOf')
430
+            ->with('principals/users/foo')
431
+            ->willReturn([]);
432
+
433
+        $this->proxyMapper->expects($this->once())
434
+            ->method('insert')
435
+            ->with($this->callback(function ($proxy) {
436
+                /** @var Proxy $proxy */
437
+                if ($proxy->getOwnerId() !== 'principals/users/foo') {
438
+                    return false;
439
+                }
440
+                if ($proxy->getProxyId() !== 'principals/users/bar') {
441
+                    return false;
442
+                }
443
+                if ($proxy->getPermissions() !== 3) {
444
+                    return false;
445
+                }
446
+
447
+                return true;
448
+            }));
449
+
450
+        $this->connector->setGroupMemberSet('principals/users/foo/calendar-proxy-write', ['principals/users/bar']);
451
+    }
452
+
453
+    public function testUpdatePrincipal(): void {
454
+        $this->assertSame(0, $this->connector->updatePrincipal('foo', new PropPatch([])));
455
+    }
456
+
457
+    public function testSearchPrincipalsWithEmptySearchProperties(): void {
458
+        $this->assertSame([], $this->connector->searchPrincipals('principals/users', []));
459
+    }
460
+
461
+    public function testSearchPrincipalsWithWrongPrefixPath(): void {
462
+        $this->assertSame([], $this->connector->searchPrincipals('principals/groups',
463
+            ['{http://sabredav.org/ns}email-address' => 'foo']));
464
+    }
465
+
466
+    /**
467
+     * @dataProvider searchPrincipalsDataProvider
468
+     */
469
+    public function testSearchPrincipals(bool $sharingEnabled, bool $groupsOnly, string $test, array $result): void {
470
+        $this->shareManager->expects($this->once())
471
+            ->method('shareAPIEnabled')
472
+            ->willReturn($sharingEnabled);
473
+
474
+        $getUserGroupIdsReturnMap = [];
475
+
476
+        if ($sharingEnabled) {
477
+            $this->shareManager->expects($this->once())
478
+                ->method('allowEnumeration')
479
+                ->willReturn(true);
480
+
481
+            $this->shareManager->expects($this->once())
482
+                ->method('shareWithGroupMembersOnly')
483
+                ->willReturn($groupsOnly);
484
+
485
+            if ($groupsOnly) {
486
+                $user = $this->createMock(IUser::class);
487
+                $this->userSession->expects($this->atLeastOnce())
488
+                    ->method('getUser')
489
+                    ->willReturn($user);
490
+
491
+                $getUserGroupIdsReturnMap[] = [$user, ['group1', 'group2', 'group5']];
492
+            }
493
+        } else {
494
+            $this->config->expects($this->never())
495
+                ->method('getAppValue');
496
+            $this->shareManager->expects($this->never())
497
+                ->method('shareWithGroupMembersOnly');
498
+            $this->groupManager->expects($this->never())
499
+                ->method($this->anything());
500
+        }
501
+
502
+        $user2 = $this->createMock(IUser::class);
503
+        $user2->method('getUID')->willReturn('user2');
504
+        $user3 = $this->createMock(IUser::class);
505
+        $user3->method('getUID')->willReturn('user3');
506
+        $user4 = $this->createMock(IUser::class);
507
+        $user4->method('getUID')->willReturn('user4');
508
+
509
+        if ($sharingEnabled) {
510
+            $this->userManager->expects($this->once())
511
+                ->method('getByEmail')
512
+                ->with('[email protected]')
513
+                ->willReturn([$user2, $user3]);
514
+
515
+            $this->userManager->expects($this->once())
516
+                ->method('searchDisplayName')
517
+                ->with('User 12')
518
+                ->willReturn([$user3, $user4]);
519
+        } else {
520
+            $this->userManager->expects($this->never())
521
+                ->method('getByEmail');
522
+
523
+            $this->userManager->expects($this->never())
524
+                ->method('searchDisplayName');
525
+        }
526
+
527
+        if ($sharingEnabled && $groupsOnly) {
528
+            $getUserGroupIdsReturnMap[] = [$user2, ['group1', 'group3']];
529
+            $getUserGroupIdsReturnMap[] = [$user3, ['group3', 'group4']];
530
+            $getUserGroupIdsReturnMap[] = [$user4, ['group4', 'group5']];
531
+        }
532
+
533
+        $this->groupManager->expects($this->any())
534
+            ->method('getUserGroupIds')
535
+            ->willReturnMap($getUserGroupIdsReturnMap);
536
+
537
+
538
+        $this->assertEquals($result, $this->connector->searchPrincipals('principals/users',
539
+            ['{http://sabredav.org/ns}email-address' => '[email protected]',
540
+                '{DAV:}displayname' => 'User 12'], $test));
541
+    }
542
+
543
+    public static function searchPrincipalsDataProvider(): array {
544
+        return [
545
+            [true, false, 'allof', ['principals/users/user3']],
546
+            [true, false, 'anyof', ['principals/users/user2', 'principals/users/user3', 'principals/users/user4']],
547
+            [true, true, 'allof', []],
548
+            [true, true, 'anyof', ['principals/users/user2', 'principals/users/user4']],
549
+            [false, false, 'allof', []],
550
+            [false, false, 'anyof', []],
551
+        ];
552
+    }
553
+
554
+    public function testSearchPrincipalByCalendarUserAddressSet(): void {
555
+        $this->shareManager->expects($this->exactly(2))
556
+            ->method('shareAPIEnabled')
557
+            ->willReturn(true);
558
+
559
+        $this->shareManager->expects($this->exactly(2))
560
+            ->method('allowEnumeration')
561
+            ->willReturn(true);
562
+
563
+        $this->shareManager->expects($this->exactly(2))
564
+            ->method('shareWithGroupMembersOnly')
565
+            ->willReturn(false);
566
+
567
+        $user2 = $this->createMock(IUser::class);
568
+        $user2->method('getUID')->willReturn('user2');
569
+        $user3 = $this->createMock(IUser::class);
570
+        $user3->method('getUID')->willReturn('user3');
571
+
572
+        $this->userManager->expects($this->once())
573
+            ->method('getByEmail')
574
+            ->with('[email protected]')
575
+            ->willReturn([$user2, $user3]);
576
+
577
+        $this->assertEquals([
578
+            'principals/users/user2',
579
+            'principals/users/user3',
580
+        ], $this->connector->searchPrincipals('principals/users',
581
+            ['{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => '[email protected]']));
582
+    }
583
+
584
+    public function testSearchPrincipalWithEnumerationDisabledDisplayname(): void {
585
+        $this->shareManager->expects($this->once())
586
+            ->method('shareAPIEnabled')
587
+            ->willReturn(true);
588
+
589
+        $this->shareManager->expects($this->once())
590
+            ->method('allowEnumeration')
591
+            ->willReturn(false);
592
+
593
+        $this->shareManager->expects($this->once())
594
+            ->method('shareWithGroupMembersOnly')
595
+            ->willReturn(false);
596
+
597
+        $this->shareManager->expects($this->once())
598
+            ->method('allowEnumerationFullMatch')
599
+            ->willReturn(true);
600
+
601
+        $user2 = $this->createMock(IUser::class);
602
+        $user2->method('getUID')->willReturn('user2');
603
+        $user2->method('getDisplayName')->willReturn('User 2');
604
+        $user2->method('getSystemEMailAddress')->willReturn('[email protected]');
605
+        $user3 = $this->createMock(IUser::class);
606
+        $user3->method('getUID')->willReturn('user3');
607
+        $user3->method('getDisplayName')->willReturn('User 22');
608
+        $user3->method('getSystemEMailAddress')->willReturn('[email protected]');
609
+        $user4 = $this->createMock(IUser::class);
610
+        $user4->method('getUID')->willReturn('user4');
611
+        $user4->method('getDisplayName')->willReturn('User 222');
612
+        $user4->method('getSystemEMailAddress')->willReturn('[email protected]');
613
+
614
+        $this->userManager->expects($this->once())
615
+            ->method('searchDisplayName')
616
+            ->with('User 2')
617
+            ->willReturn([$user2, $user3, $user4]);
618
+
619
+        $this->assertEquals(['principals/users/user2'], $this->connector->searchPrincipals('principals/users',
620
+            ['{DAV:}displayname' => 'User 2']));
621
+    }
622
+
623
+    public function testSearchPrincipalWithEnumerationDisabledDisplaynameOnFullMatch(): void {
624
+        $this->shareManager->expects($this->once())
625
+            ->method('shareAPIEnabled')
626
+            ->willReturn(true);
627
+
628
+        $this->shareManager->expects($this->once())
629
+            ->method('allowEnumeration')
630
+            ->willReturn(false);
631
+
632
+        $this->shareManager->expects($this->once())
633
+            ->method('shareWithGroupMembersOnly')
634
+            ->willReturn(false);
635
+
636
+        $this->shareManager->expects($this->once())
637
+            ->method('allowEnumerationFullMatch')
638
+            ->willReturn(false);
639
+
640
+        $this->assertEquals([], $this->connector->searchPrincipals('principals/users',
641
+            ['{DAV:}displayname' => 'User 2']));
642
+    }
643
+
644
+    public function testSearchPrincipalWithEnumerationDisabledEmail(): void {
645
+        $this->shareManager->expects($this->once())
646
+            ->method('shareAPIEnabled')
647
+            ->willReturn(true);
648
+
649
+        $this->shareManager->expects($this->once())
650
+            ->method('allowEnumeration')
651
+            ->willReturn(false);
652
+
653
+        $this->shareManager->expects($this->once())
654
+            ->method('shareWithGroupMembersOnly')
655
+            ->willReturn(false);
656
+
657
+        $this->shareManager->expects($this->once())
658
+            ->method('allowEnumerationFullMatch')
659
+            ->willReturn(true);
660
+
661
+        $this->shareManager->expects($this->once())
662
+            ->method('matchEmail')
663
+            ->willReturn(true);
664
+
665
+        $user2 = $this->createMock(IUser::class);
666
+        $user2->method('getUID')->willReturn('user2');
667
+        $user2->method('getDisplayName')->willReturn('User 2');
668
+        $user2->method('getSystemEMailAddress')->willReturn('[email protected]');
669
+        $user3 = $this->createMock(IUser::class);
670
+        $user3->method('getUID')->willReturn('user3');
671
+        $user2->method('getDisplayName')->willReturn('User 22');
672
+        $user2->method('getSystemEMailAddress')->willReturn('[email protected]');
673
+        $user4 = $this->createMock(IUser::class);
674
+        $user4->method('getUID')->willReturn('user4');
675
+        $user2->method('getDisplayName')->willReturn('User 222');
676
+        $user2->method('getSystemEMailAddress')->willReturn('[email protected]');
677
+
678
+        $this->userManager->expects($this->once())
679
+            ->method('getByEmail')
680
+            ->with('[email protected]')
681
+            ->willReturn([$user2]);
682
+
683
+        $this->assertEquals(['principals/users/user2'], $this->connector->searchPrincipals('principals/users',
684
+            ['{http://sabredav.org/ns}email-address' => '[email protected]']));
685
+    }
686
+
687
+    public function testSearchPrincipalWithEnumerationDisabledEmailOnFullMatch(): void {
688
+        $this->shareManager->expects($this->once())
689
+            ->method('shareAPIEnabled')
690
+            ->willReturn(true);
691
+
692
+        $this->shareManager->expects($this->once())
693
+            ->method('allowEnumeration')
694
+            ->willReturn(false);
695
+
696
+        $this->shareManager->expects($this->once())
697
+            ->method('shareWithGroupMembersOnly')
698
+            ->willReturn(false);
699
+
700
+        $this->shareManager->expects($this->once())
701
+            ->method('allowEnumerationFullMatch')
702
+            ->willReturn(false);
703
+
704
+
705
+        $this->assertEquals([], $this->connector->searchPrincipals('principals/users',
706
+            ['{http://sabredav.org/ns}email-address' => '[email protected]']));
707
+    }
708
+
709
+    public function testSearchPrincipalWithEnumerationLimitedDisplayname(): void {
710
+        $this->shareManager->expects($this->once())
711
+            ->method('shareAPIEnabled')
712
+            ->willReturn(true);
713
+
714
+        $this->shareManager->expects($this->once())
715
+            ->method('allowEnumeration')
716
+            ->willReturn(true);
717
+
718
+        $this->shareManager->expects($this->once())
719
+            ->method('limitEnumerationToGroups')
720
+            ->willReturn(true);
721
+
722
+        $this->shareManager->expects($this->once())
723
+            ->method('shareWithGroupMembersOnly')
724
+            ->willReturn(false);
725
+
726
+        $user2 = $this->createMock(IUser::class);
727
+        $user2->method('getUID')->willReturn('user2');
728
+        $user2->method('getDisplayName')->willReturn('User 2');
729
+        $user2->method('getSystemEMailAddress')->willReturn('[email protected]');
730
+        $user3 = $this->createMock(IUser::class);
731
+        $user3->method('getUID')->willReturn('user3');
732
+        $user3->method('getDisplayName')->willReturn('User 22');
733
+        $user3->method('getSystemEMailAddress')->willReturn('[email protected]');
734
+        $user4 = $this->createMock(IUser::class);
735
+        $user4->method('getUID')->willReturn('user4');
736
+        $user4->method('getDisplayName')->willReturn('User 222');
737
+        $user4->method('getSystemEMailAddress')->willReturn('[email protected]');
738
+
739
+
740
+        $this->userSession->expects($this->once())
741
+            ->method('getUser')
742
+            ->willReturn($user2);
743
+
744
+        $this->groupManager->expects($this->exactly(4))
745
+            ->method('getUserGroupIds')
746
+            ->willReturnMap([
747
+                [$user2, ['group1']],
748
+                [$user3, ['group1']],
749
+                [$user4, ['group2']],
750
+            ]);
751
+
752
+        $this->userManager->expects($this->once())
753
+            ->method('searchDisplayName')
754
+            ->with('User')
755
+            ->willReturn([$user2, $user3, $user4]);
756
+
757
+
758
+        $this->assertEquals([
759
+            'principals/users/user2',
760
+            'principals/users/user3',
761
+        ], $this->connector->searchPrincipals('principals/users',
762
+            ['{DAV:}displayname' => 'User']));
763
+    }
764
+
765
+    public function testSearchPrincipalWithEnumerationLimitedMail(): void {
766
+        $this->shareManager->expects($this->once())
767
+            ->method('shareAPIEnabled')
768
+            ->willReturn(true);
769
+
770
+        $this->shareManager->expects($this->once())
771
+            ->method('allowEnumeration')
772
+            ->willReturn(true);
773
+
774
+        $this->shareManager->expects($this->once())
775
+            ->method('limitEnumerationToGroups')
776
+            ->willReturn(true);
777
+
778
+        $this->shareManager->expects($this->once())
779
+            ->method('shareWithGroupMembersOnly')
780
+            ->willReturn(false);
781
+
782
+        $user2 = $this->createMock(IUser::class);
783
+        $user2->method('getUID')->willReturn('user2');
784
+        $user2->method('getDisplayName')->willReturn('User 2');
785
+        $user2->method('getSystemEMailAddress')->willReturn('[email protected]');
786
+        $user3 = $this->createMock(IUser::class);
787
+        $user3->method('getUID')->willReturn('user3');
788
+        $user3->method('getDisplayName')->willReturn('User 22');
789
+        $user3->method('getSystemEMailAddress')->willReturn('[email protected]');
790
+        $user4 = $this->createMock(IUser::class);
791
+        $user4->method('getUID')->willReturn('user4');
792
+        $user4->method('getDisplayName')->willReturn('User 222');
793
+        $user4->method('getSystemEMailAddress')->willReturn('[email protected]');
794
+
795
+
796
+        $this->userSession->expects($this->once())
797
+            ->method('getUser')
798
+            ->willReturn($user2);
799
+
800
+        $this->groupManager->expects($this->exactly(4))
801
+            ->method('getUserGroupIds')
802
+            ->willReturnMap([
803
+                [$user2, ['group1']],
804
+                [$user3, ['group1']],
805
+                [$user4, ['group2']],
806
+            ]);
807
+
808
+        $this->userManager->expects($this->once())
809
+            ->method('getByEmail')
810
+            ->with('user')
811
+            ->willReturn([$user2, $user3, $user4]);
812
+
813
+
814
+        $this->assertEquals([
815
+            'principals/users/user2',
816
+            'principals/users/user3'
817
+        ], $this->connector->searchPrincipals('principals/users',
818
+            ['{http://sabredav.org/ns}email-address' => 'user']));
819
+    }
820
+
821
+    public function testFindByUriSharingApiDisabled(): void {
822
+        $this->shareManager->expects($this->once())
823
+            ->method('shareApiEnabled')
824
+            ->willReturn(false);
825
+
826
+        $this->assertEquals(null, $this->connector->findByUri('mailto:[email protected]', 'principals/users'));
827
+    }
828
+
829
+    /**
830
+     * @dataProvider findByUriWithGroupRestrictionDataProvider
831
+     */
832
+    public function testFindByUriWithGroupRestriction(string $uri, string $email, ?string $expects): void {
833
+        $this->shareManager->expects($this->once())
834
+            ->method('shareApiEnabled')
835
+            ->willReturn(true);
836
+
837
+        $this->shareManager->expects($this->once())
838
+            ->method('shareWithGroupMembersOnly')
839
+            ->willReturn(true);
840
+
841
+        $user = $this->createMock(IUser::class);
842
+        $this->userSession->expects($this->once())
843
+            ->method('getUser')
844
+            ->willReturn($user);
845
+
846
+        $user2 = $this->createMock(IUser::class);
847
+        $user2->method('getUID')->willReturn('user2');
848
+        $user3 = $this->createMock(IUser::class);
849
+        $user3->method('getUID')->willReturn('user3');
850
+
851
+        $this->userManager->expects($this->once())
852
+            ->method('getByEmail')
853
+            ->with($email)
854
+            ->willReturn([$email === '[email protected]' ? $user2 : $user3]);
855
+
856
+        if ($email === '[email protected]') {
857
+            $this->groupManager->expects($this->exactly(2))
858
+                ->method('getUserGroupIds')
859
+                ->willReturnMap([
860
+                    [$user, ['group1', 'group2']],
861
+                    [$user2, ['group1', 'group3']],
862
+                ]);
863
+        } else {
864
+            $this->groupManager->expects($this->exactly(2))
865
+                ->method('getUserGroupIds')
866
+                ->willReturnMap([
867
+                    [$user, ['group1', 'group2']],
868
+                    [$user3, ['group3', 'group3']],
869
+                ]);
870
+        }
871
+
872
+        $this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users'));
873
+    }
874
+
875
+    public static function findByUriWithGroupRestrictionDataProvider(): array {
876
+        return [
877
+            ['mailto:[email protected]', '[email protected]', 'principals/users/user2'],
878
+            ['mailto:[email protected]', '[email protected]', null],
879
+        ];
880
+    }
881
+
882
+    /**
883
+     * @dataProvider findByUriWithoutGroupRestrictionDataProvider
884
+     */
885
+    public function testFindByUriWithoutGroupRestriction(string $uri, string $email, string $expects): void {
886
+        $this->shareManager->expects($this->once())
887
+            ->method('shareApiEnabled')
888
+            ->willReturn(true);
889
+
890
+        $this->shareManager->expects($this->once())
891
+            ->method('shareWithGroupMembersOnly')
892
+            ->willReturn(false);
893
+
894
+        $user2 = $this->createMock(IUser::class);
895
+        $user2->method('getUID')->willReturn('user2');
896
+        $user3 = $this->createMock(IUser::class);
897
+        $user3->method('getUID')->willReturn('user3');
898
+
899
+        $this->userManager->expects($this->once())
900
+            ->method('getByEmail')
901
+            ->with($email)
902
+            ->willReturn([$email === '[email protected]' ? $user2 : $user3]);
903
+
904
+        $this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users'));
905
+    }
906
+
907
+    public static function findByUriWithoutGroupRestrictionDataProvider(): array {
908
+        return [
909
+            ['mailto:[email protected]', '[email protected]', 'principals/users/user2'],
910
+            ['mailto:[email protected]', '[email protected]', 'principals/users/user3'],
911
+        ];
912
+    }
913
+
914
+    public function testGetEmailAddressesOfPrincipal(): void {
915
+        $principal = [
916
+            '{http://sabredav.org/ns}email-address' => '[email protected]',
917
+            '{DAV:}alternate-URI-set' => [
918
+                '/some/url',
919
+                'mailto:[email protected]',
920
+                'mailto:[email protected]',
921
+            ],
922
+            '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => [
923
+                'mailto:[email protected]',
924
+                'mailto:[email protected]',
925
+            ],
926
+            '{http://calendarserver.org/ns/}email-address-set' => [
927
+                'mailto:[email protected]',
928
+                'mailto:[email protected]',
929
+            ],
930
+        ];
931
+
932
+        $expected = [
933
+            '[email protected]',
934
+            '[email protected]',
935
+            '[email protected]',
936
+            '[email protected]',
937
+            '[email protected]',
938
+            '[email protected]',
939
+        ];
940
+        $actual = $this->connector->getEmailAddressesOfPrincipal($principal);
941
+        $this->assertEquals($expected, $actual);
942
+    }
943 943
 }
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -12,19 +12,19 @@  discard block
 block discarded – undo
12 12
 use Sabre\DAV\Server;
13 13
 
14 14
 class InvalidPathTest extends \Test\TestCase {
15
-	public function testSerialization(): void {
16
-
17
-		// create xml doc
18
-		$DOM = new \DOMDocument('1.0', 'utf-8');
19
-		$DOM->formatOutput = true;
20
-		$error = $DOM->createElementNS('DAV:', 'd:error');
21
-		$error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV);
22
-		$DOM->appendChild($error);
23
-
24
-		// serialize the exception
25
-		$message = '1234567890';
26
-		$retry = false;
27
-		$expectedXml = <<<EOD
15
+    public function testSerialization(): void {
16
+
17
+        // create xml doc
18
+        $DOM = new \DOMDocument('1.0', 'utf-8');
19
+        $DOM->formatOutput = true;
20
+        $error = $DOM->createElementNS('DAV:', 'd:error');
21
+        $error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV);
22
+        $DOM->appendChild($error);
23
+
24
+        // serialize the exception
25
+        $message = '1234567890';
26
+        $retry = false;
27
+        $expectedXml = <<<EOD
28 28
 <?xml version="1.0" encoding="utf-8"?>
29 29
 <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:o="http://owncloud.org/ns">
30 30
   <o:retry xmlns:o="o:">false</o:retry>
@@ -33,12 +33,12 @@  discard block
 block discarded – undo
33 33
 
34 34
 EOD;
35 35
 
36
-		$ex = new InvalidPath($message, $retry);
37
-		$server = $this->createMock(Server::class);
38
-		$ex->serialize($server, $error);
36
+        $ex = new InvalidPath($message, $retry);
37
+        $server = $this->createMock(Server::class);
38
+        $ex->serialize($server, $error);
39 39
 
40
-		// assert
41
-		$xml = $DOM->saveXML();
42
-		$this->assertEquals($expectedXml, $xml);
43
-	}
40
+        // assert
41
+        $xml = $DOM->saveXML();
42
+        $this->assertEquals($expectedXml, $xml);
43
+    }
44 44
 }
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php 1 patch
Indentation   +20 added lines, -20 removed lines patch added patch discarded remove patch
@@ -12,19 +12,19 @@  discard block
 block discarded – undo
12 12
 use Sabre\DAV\Server;
13 13
 
14 14
 class ForbiddenTest extends \Test\TestCase {
15
-	public function testSerialization(): void {
16
-
17
-		// create xml doc
18
-		$DOM = new \DOMDocument('1.0', 'utf-8');
19
-		$DOM->formatOutput = true;
20
-		$error = $DOM->createElementNS('DAV:', 'd:error');
21
-		$error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV);
22
-		$DOM->appendChild($error);
23
-
24
-		// serialize the exception
25
-		$message = '1234567890';
26
-		$retry = false;
27
-		$expectedXml = <<<EOD
15
+    public function testSerialization(): void {
16
+
17
+        // create xml doc
18
+        $DOM = new \DOMDocument('1.0', 'utf-8');
19
+        $DOM->formatOutput = true;
20
+        $error = $DOM->createElementNS('DAV:', 'd:error');
21
+        $error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV);
22
+        $DOM->appendChild($error);
23
+
24
+        // serialize the exception
25
+        $message = '1234567890';
26
+        $retry = false;
27
+        $expectedXml = <<<EOD
28 28
 <?xml version="1.0" encoding="utf-8"?>
29 29
 <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:o="http://owncloud.org/ns">
30 30
   <o:retry xmlns:o="o:">false</o:retry>
@@ -33,12 +33,12 @@  discard block
 block discarded – undo
33 33
 
34 34
 EOD;
35 35
 
36
-		$ex = new Forbidden($message, $retry);
37
-		$server = $this->createMock(Server::class);
38
-		$ex->serialize($server, $error);
36
+        $ex = new Forbidden($message, $retry);
37
+        $server = $this->createMock(Server::class);
38
+        $ex->serialize($server, $error);
39 39
 
40
-		// assert
41
-		$xml = $DOM->saveXML();
42
-		$this->assertEquals($expectedXml, $xml);
43
-	}
40
+        // assert
41
+        $xml = $DOM->saveXML();
42
+        $this->assertEquals($expectedXml, $xml);
43
+    }
44 44
 }
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php 1 patch
Indentation   +59 added lines, -59 removed lines patch added patch discarded remove patch
@@ -16,63 +16,63 @@
 block discarded – undo
16 16
 use Test\TestCase;
17 17
 
18 18
 class CopyEtagHeaderPluginTest extends TestCase {
19
-	private CopyEtagHeaderPlugin $plugin;
20
-	private Server $server;
21
-
22
-	protected function setUp(): void {
23
-		parent::setUp();
24
-		$this->server = new \Sabre\DAV\Server();
25
-		$this->plugin = new CopyEtagHeaderPlugin();
26
-		$this->plugin->initialize($this->server);
27
-	}
28
-
29
-	public function testCopyEtag(): void {
30
-		$request = new \Sabre\Http\Request('GET', 'dummy.file');
31
-		$response = new \Sabre\Http\Response();
32
-		$response->setHeader('Etag', 'abcd');
33
-
34
-		$this->plugin->afterMethod($request, $response);
35
-
36
-		$this->assertEquals('abcd', $response->getHeader('OC-Etag'));
37
-	}
38
-
39
-	public function testNoopWhenEmpty(): void {
40
-		$request = new \Sabre\Http\Request('GET', 'dummy.file');
41
-		$response = new \Sabre\Http\Response();
42
-
43
-		$this->plugin->afterMethod($request, $response);
44
-
45
-		$this->assertNull($response->getHeader('OC-Etag'));
46
-	}
47
-
48
-	public function testAfterMoveNodeNotFound(): void {
49
-		$tree = $this->createMock(Tree::class);
50
-		$tree->expects(self::once())
51
-			->method('getNodeForPath')
52
-			->with('test.txt')
53
-			->willThrowException(new NotFound());
54
-
55
-		$this->server->tree = $tree;
56
-		$this->plugin->afterMove('', 'test.txt');
57
-
58
-		// Nothing to assert, we are just testing if the exception is handled
59
-	}
60
-
61
-	public function testAfterMove(): void {
62
-		$node = $this->createMock(File::class);
63
-		$node->expects($this->once())
64
-			->method('getETag')
65
-			->willReturn('123456');
66
-		$tree = $this->createMock(Tree::class);
67
-		$tree->expects($this->once())
68
-			->method('getNodeForPath')
69
-			->with('test.txt')
70
-			->willReturn($node);
71
-
72
-		$this->server->tree = $tree;
73
-		$this->plugin->afterMove('', 'test.txt');
74
-
75
-		$this->assertEquals('123456', $this->server->httpResponse->getHeader('OC-Etag'));
76
-		$this->assertEquals('123456', $this->server->httpResponse->getHeader('Etag'));
77
-	}
19
+    private CopyEtagHeaderPlugin $plugin;
20
+    private Server $server;
21
+
22
+    protected function setUp(): void {
23
+        parent::setUp();
24
+        $this->server = new \Sabre\DAV\Server();
25
+        $this->plugin = new CopyEtagHeaderPlugin();
26
+        $this->plugin->initialize($this->server);
27
+    }
28
+
29
+    public function testCopyEtag(): void {
30
+        $request = new \Sabre\Http\Request('GET', 'dummy.file');
31
+        $response = new \Sabre\Http\Response();
32
+        $response->setHeader('Etag', 'abcd');
33
+
34
+        $this->plugin->afterMethod($request, $response);
35
+
36
+        $this->assertEquals('abcd', $response->getHeader('OC-Etag'));
37
+    }
38
+
39
+    public function testNoopWhenEmpty(): void {
40
+        $request = new \Sabre\Http\Request('GET', 'dummy.file');
41
+        $response = new \Sabre\Http\Response();
42
+
43
+        $this->plugin->afterMethod($request, $response);
44
+
45
+        $this->assertNull($response->getHeader('OC-Etag'));
46
+    }
47
+
48
+    public function testAfterMoveNodeNotFound(): void {
49
+        $tree = $this->createMock(Tree::class);
50
+        $tree->expects(self::once())
51
+            ->method('getNodeForPath')
52
+            ->with('test.txt')
53
+            ->willThrowException(new NotFound());
54
+
55
+        $this->server->tree = $tree;
56
+        $this->plugin->afterMove('', 'test.txt');
57
+
58
+        // Nothing to assert, we are just testing if the exception is handled
59
+    }
60
+
61
+    public function testAfterMove(): void {
62
+        $node = $this->createMock(File::class);
63
+        $node->expects($this->once())
64
+            ->method('getETag')
65
+            ->willReturn('123456');
66
+        $tree = $this->createMock(Tree::class);
67
+        $tree->expects($this->once())
68
+            ->method('getNodeForPath')
69
+            ->with('test.txt')
70
+            ->willReturn($node);
71
+
72
+        $this->server->tree = $tree;
73
+        $this->plugin->afterMove('', 'test.txt');
74
+
75
+        $this->assertEquals('123456', $this->server->httpResponse->getHeader('OC-Etag'));
76
+        $this->assertEquals('123456', $this->server->httpResponse->getHeader('Etag'));
77
+    }
78 78
 }
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/BearerAuthTest.php 1 patch
Indentation   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -23,60 +23,60 @@
 block discarded – undo
23 23
  * @group DB
24 24
  */
25 25
 class BearerAuthTest extends TestCase {
26
-	private IUserSession&MockObject $userSession;
27
-	private ISession&MockObject $session;
28
-	private IRequest&MockObject $request;
29
-	private BearerAuth $bearerAuth;
26
+    private IUserSession&MockObject $userSession;
27
+    private ISession&MockObject $session;
28
+    private IRequest&MockObject $request;
29
+    private BearerAuth $bearerAuth;
30 30
 
31
-	private IConfig&MockObject $config;
31
+    private IConfig&MockObject $config;
32 32
 
33
-	protected function setUp(): void {
34
-		parent::setUp();
33
+    protected function setUp(): void {
34
+        parent::setUp();
35 35
 
36
-		$this->userSession = $this->createMock(Session::class);
37
-		$this->session = $this->createMock(ISession::class);
38
-		$this->request = $this->createMock(IRequest::class);
39
-		$this->config = $this->createMock(IConfig::class);
36
+        $this->userSession = $this->createMock(Session::class);
37
+        $this->session = $this->createMock(ISession::class);
38
+        $this->request = $this->createMock(IRequest::class);
39
+        $this->config = $this->createMock(IConfig::class);
40 40
 
41
-		$this->bearerAuth = new BearerAuth(
42
-			$this->userSession,
43
-			$this->session,
44
-			$this->request,
45
-			$this->config,
46
-		);
47
-	}
41
+        $this->bearerAuth = new BearerAuth(
42
+            $this->userSession,
43
+            $this->session,
44
+            $this->request,
45
+            $this->config,
46
+        );
47
+    }
48 48
 
49
-	public function testValidateBearerTokenNotLoggedIn(): void {
50
-		$this->assertFalse($this->bearerAuth->validateBearerToken('Token'));
51
-	}
49
+    public function testValidateBearerTokenNotLoggedIn(): void {
50
+        $this->assertFalse($this->bearerAuth->validateBearerToken('Token'));
51
+    }
52 52
 
53
-	public function testValidateBearerToken(): void {
54
-		$this->userSession
55
-			->expects($this->exactly(2))
56
-			->method('isLoggedIn')
57
-			->willReturnOnConsecutiveCalls(
58
-				false,
59
-				true,
60
-			);
61
-		$user = $this->createMock(IUser::class);
62
-		$user
63
-			->expects($this->once())
64
-			->method('getUID')
65
-			->willReturn('admin');
66
-		$this->userSession
67
-			->expects($this->once())
68
-			->method('getUser')
69
-			->willReturn($user);
53
+    public function testValidateBearerToken(): void {
54
+        $this->userSession
55
+            ->expects($this->exactly(2))
56
+            ->method('isLoggedIn')
57
+            ->willReturnOnConsecutiveCalls(
58
+                false,
59
+                true,
60
+            );
61
+        $user = $this->createMock(IUser::class);
62
+        $user
63
+            ->expects($this->once())
64
+            ->method('getUID')
65
+            ->willReturn('admin');
66
+        $this->userSession
67
+            ->expects($this->once())
68
+            ->method('getUser')
69
+            ->willReturn($user);
70 70
 
71
-		$this->assertSame('principals/users/admin', $this->bearerAuth->validateBearerToken('Token'));
72
-	}
71
+        $this->assertSame('principals/users/admin', $this->bearerAuth->validateBearerToken('Token'));
72
+    }
73 73
 
74
-	public function testChallenge(): void {
75
-		/** @var RequestInterface&MockObject $request */
76
-		$request = $this->createMock(RequestInterface::class);
77
-		/** @var ResponseInterface&MockObject $response */
78
-		$response = $this->createMock(ResponseInterface::class);
79
-		$result = $this->bearerAuth->challenge($request, $response);
80
-		$this->assertEmpty($result);
81
-	}
74
+    public function testChallenge(): void {
75
+        /** @var RequestInterface&MockObject $request */
76
+        $request = $this->createMock(RequestInterface::class);
77
+        /** @var ResponseInterface&MockObject $response */
78
+        $response = $this->createMock(ResponseInterface::class);
79
+        $result = $this->bearerAuth->challenge($request, $response);
80
+        $this->assertEmpty($result);
81
+    }
82 82
 }
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php 2 patches
Indentation   +143 added lines, -143 removed lines patch added patch discarded remove patch
@@ -14,147 +14,147 @@
 block discarded – undo
14 14
 use Test\TestCase;
15 15
 
16 16
 class QuotaPluginTest extends TestCase {
17
-	private \Sabre\DAV\Server $server;
18
-
19
-	private QuotaPlugin $plugin;
20
-
21
-	private function init(int $quota, string $checkedPath = ''): void {
22
-		$view = $this->buildFileViewMock((string)$quota, $checkedPath);
23
-		$this->server = new \Sabre\DAV\Server();
24
-		$this->plugin = new QuotaPlugin($view);
25
-		$this->plugin->initialize($this->server);
26
-	}
27
-
28
-	/**
29
-	 * @dataProvider lengthProvider
30
-	 */
31
-	public function testLength(?int $expected, array $headers): void {
32
-		$this->init(0);
33
-
34
-		$this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
35
-		$length = $this->plugin->getLength();
36
-		$this->assertEquals($expected, $length);
37
-	}
38
-
39
-	/**
40
-	 * @dataProvider quotaOkayProvider
41
-	 */
42
-	public function testCheckQuota(int $quota, array $headers): void {
43
-		$this->init($quota);
44
-
45
-		$this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
46
-		$result = $this->plugin->checkQuota('');
47
-		$this->assertTrue($result);
48
-	}
49
-
50
-	/**
51
-	 * @dataProvider quotaExceededProvider
52
-	 */
53
-	public function testCheckExceededQuota(int $quota, array $headers): void {
54
-		$this->expectException(\Sabre\DAV\Exception\InsufficientStorage::class);
55
-
56
-		$this->init($quota);
57
-
58
-		$this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
59
-		$this->plugin->checkQuota('');
60
-	}
61
-
62
-	/**
63
-	 * @dataProvider quotaOkayProvider
64
-	 */
65
-	public function testCheckQuotaOnPath(int $quota, array $headers): void {
66
-		$this->init($quota, 'sub/test.txt');
67
-
68
-		$this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
69
-		$result = $this->plugin->checkQuota('/sub/test.txt');
70
-		$this->assertTrue($result);
71
-	}
72
-
73
-	public static function quotaOkayProvider(): array {
74
-		return [
75
-			[1024, []],
76
-			[1024, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
77
-			[1024, ['CONTENT-LENGTH' => '512']],
78
-			[1024, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
79
-
80
-			[FileInfo::SPACE_UNKNOWN, []],
81
-			[FileInfo::SPACE_UNKNOWN, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
82
-			[FileInfo::SPACE_UNKNOWN, ['CONTENT-LENGTH' => '512']],
83
-			[FileInfo::SPACE_UNKNOWN, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
84
-
85
-			[FileInfo::SPACE_UNLIMITED, []],
86
-			[FileInfo::SPACE_UNLIMITED, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
87
-			[FileInfo::SPACE_UNLIMITED, ['CONTENT-LENGTH' => '512']],
88
-			[FileInfo::SPACE_UNLIMITED, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
89
-		];
90
-	}
91
-
92
-	public static function quotaExceededProvider(): array {
93
-		return [
94
-			[1023, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
95
-			[511, ['CONTENT-LENGTH' => '512']],
96
-			[2047, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => '1024']],
97
-		];
98
-	}
99
-
100
-	public static function lengthProvider(): array {
101
-		return [
102
-			[null, []],
103
-			[1024, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
104
-			[512, ['CONTENT-LENGTH' => '512']],
105
-			[2048, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => '1024']],
106
-			[4096, ['OC-TOTAL-LENGTH' => '2048', 'X-EXPECTED-ENTITY-LENGTH' => '4096']],
107
-			[null, ['X-EXPECTED-ENTITY-LENGTH' => 'A']],
108
-			[null, ['CONTENT-LENGTH' => 'A']],
109
-			[1024, ['OC-TOTAL-LENGTH' => 'A', 'CONTENT-LENGTH' => '1024']],
110
-			[1024, ['OC-TOTAL-LENGTH' => 'A', 'X-EXPECTED-ENTITY-LENGTH' => '1024']],
111
-			[2048, ['OC-TOTAL-LENGTH' => '2048', 'X-EXPECTED-ENTITY-LENGTH' => 'A']],
112
-			[2048, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => 'A']],
113
-		];
114
-	}
115
-
116
-	public static function quotaChunkedOkProvider(): array {
117
-		return [
118
-			[1024, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
119
-			[1024, 0, ['CONTENT-LENGTH' => '512']],
120
-			[1024, 0, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
121
-			// with existing chunks (allowed size = total length - chunk total size)
122
-			[400, 128, ['X-EXPECTED-ENTITY-LENGTH' => '512']],
123
-			[400, 128, ['CONTENT-LENGTH' => '512']],
124
-			[400, 128, ['OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500']],
125
-			// \OCP\Files\FileInfo::SPACE-UNKNOWN = -2
126
-			[-2, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
127
-			[-2, 0, ['CONTENT-LENGTH' => '512']],
128
-			[-2, 0, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
129
-			[-2, 128, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
130
-			[-2, 128, ['CONTENT-LENGTH' => '512']],
131
-			[-2, 128, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
132
-		];
133
-	}
134
-
135
-	public static function quotaChunkedFailProvider(): array {
136
-		return [
137
-			[400, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
138
-			[400, 0, ['CONTENT-LENGTH' => '512']],
139
-			[400, 0, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
140
-			// with existing chunks (allowed size = total length - chunk total size)
141
-			[380, 128, ['X-EXPECTED-ENTITY-LENGTH' => '512']],
142
-			[380, 128, ['CONTENT-LENGTH' => '512']],
143
-			[380, 128, ['OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500']],
144
-		];
145
-	}
146
-
147
-	private function buildFileViewMock(string $quota, string $checkedPath): View {
148
-		// mock filesystem
149
-		$view = $this->getMockBuilder(View::class)
150
-			->onlyMethods(['free_space'])
151
-			->disableOriginalConstructor()
152
-			->getMock();
153
-		$view->expects($this->any())
154
-			->method('free_space')
155
-			->with($checkedPath)
156
-			->willReturn($quota);
157
-
158
-		return $view;
159
-	}
17
+    private \Sabre\DAV\Server $server;
18
+
19
+    private QuotaPlugin $plugin;
20
+
21
+    private function init(int $quota, string $checkedPath = ''): void {
22
+        $view = $this->buildFileViewMock((string)$quota, $checkedPath);
23
+        $this->server = new \Sabre\DAV\Server();
24
+        $this->plugin = new QuotaPlugin($view);
25
+        $this->plugin->initialize($this->server);
26
+    }
27
+
28
+    /**
29
+     * @dataProvider lengthProvider
30
+     */
31
+    public function testLength(?int $expected, array $headers): void {
32
+        $this->init(0);
33
+
34
+        $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
35
+        $length = $this->plugin->getLength();
36
+        $this->assertEquals($expected, $length);
37
+    }
38
+
39
+    /**
40
+     * @dataProvider quotaOkayProvider
41
+     */
42
+    public function testCheckQuota(int $quota, array $headers): void {
43
+        $this->init($quota);
44
+
45
+        $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
46
+        $result = $this->plugin->checkQuota('');
47
+        $this->assertTrue($result);
48
+    }
49
+
50
+    /**
51
+     * @dataProvider quotaExceededProvider
52
+     */
53
+    public function testCheckExceededQuota(int $quota, array $headers): void {
54
+        $this->expectException(\Sabre\DAV\Exception\InsufficientStorage::class);
55
+
56
+        $this->init($quota);
57
+
58
+        $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
59
+        $this->plugin->checkQuota('');
60
+    }
61
+
62
+    /**
63
+     * @dataProvider quotaOkayProvider
64
+     */
65
+    public function testCheckQuotaOnPath(int $quota, array $headers): void {
66
+        $this->init($quota, 'sub/test.txt');
67
+
68
+        $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers);
69
+        $result = $this->plugin->checkQuota('/sub/test.txt');
70
+        $this->assertTrue($result);
71
+    }
72
+
73
+    public static function quotaOkayProvider(): array {
74
+        return [
75
+            [1024, []],
76
+            [1024, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
77
+            [1024, ['CONTENT-LENGTH' => '512']],
78
+            [1024, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
79
+
80
+            [FileInfo::SPACE_UNKNOWN, []],
81
+            [FileInfo::SPACE_UNKNOWN, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
82
+            [FileInfo::SPACE_UNKNOWN, ['CONTENT-LENGTH' => '512']],
83
+            [FileInfo::SPACE_UNKNOWN, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
84
+
85
+            [FileInfo::SPACE_UNLIMITED, []],
86
+            [FileInfo::SPACE_UNLIMITED, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
87
+            [FileInfo::SPACE_UNLIMITED, ['CONTENT-LENGTH' => '512']],
88
+            [FileInfo::SPACE_UNLIMITED, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
89
+        ];
90
+    }
91
+
92
+    public static function quotaExceededProvider(): array {
93
+        return [
94
+            [1023, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
95
+            [511, ['CONTENT-LENGTH' => '512']],
96
+            [2047, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => '1024']],
97
+        ];
98
+    }
99
+
100
+    public static function lengthProvider(): array {
101
+        return [
102
+            [null, []],
103
+            [1024, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
104
+            [512, ['CONTENT-LENGTH' => '512']],
105
+            [2048, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => '1024']],
106
+            [4096, ['OC-TOTAL-LENGTH' => '2048', 'X-EXPECTED-ENTITY-LENGTH' => '4096']],
107
+            [null, ['X-EXPECTED-ENTITY-LENGTH' => 'A']],
108
+            [null, ['CONTENT-LENGTH' => 'A']],
109
+            [1024, ['OC-TOTAL-LENGTH' => 'A', 'CONTENT-LENGTH' => '1024']],
110
+            [1024, ['OC-TOTAL-LENGTH' => 'A', 'X-EXPECTED-ENTITY-LENGTH' => '1024']],
111
+            [2048, ['OC-TOTAL-LENGTH' => '2048', 'X-EXPECTED-ENTITY-LENGTH' => 'A']],
112
+            [2048, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => 'A']],
113
+        ];
114
+    }
115
+
116
+    public static function quotaChunkedOkProvider(): array {
117
+        return [
118
+            [1024, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
119
+            [1024, 0, ['CONTENT-LENGTH' => '512']],
120
+            [1024, 0, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
121
+            // with existing chunks (allowed size = total length - chunk total size)
122
+            [400, 128, ['X-EXPECTED-ENTITY-LENGTH' => '512']],
123
+            [400, 128, ['CONTENT-LENGTH' => '512']],
124
+            [400, 128, ['OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500']],
125
+            // \OCP\Files\FileInfo::SPACE-UNKNOWN = -2
126
+            [-2, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
127
+            [-2, 0, ['CONTENT-LENGTH' => '512']],
128
+            [-2, 0, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
129
+            [-2, 128, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
130
+            [-2, 128, ['CONTENT-LENGTH' => '512']],
131
+            [-2, 128, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
132
+        ];
133
+    }
134
+
135
+    public static function quotaChunkedFailProvider(): array {
136
+        return [
137
+            [400, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']],
138
+            [400, 0, ['CONTENT-LENGTH' => '512']],
139
+            [400, 0, ['OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512']],
140
+            // with existing chunks (allowed size = total length - chunk total size)
141
+            [380, 128, ['X-EXPECTED-ENTITY-LENGTH' => '512']],
142
+            [380, 128, ['CONTENT-LENGTH' => '512']],
143
+            [380, 128, ['OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500']],
144
+        ];
145
+    }
146
+
147
+    private function buildFileViewMock(string $quota, string $checkedPath): View {
148
+        // mock filesystem
149
+        $view = $this->getMockBuilder(View::class)
150
+            ->onlyMethods(['free_space'])
151
+            ->disableOriginalConstructor()
152
+            ->getMock();
153
+        $view->expects($this->any())
154
+            ->method('free_space')
155
+            ->with($checkedPath)
156
+            ->willReturn($quota);
157
+
158
+        return $view;
159
+    }
160 160
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@
 block discarded – undo
19 19
 	private QuotaPlugin $plugin;
20 20
 
21 21
 	private function init(int $quota, string $checkedPath = ''): void {
22
-		$view = $this->buildFileViewMock((string)$quota, $checkedPath);
22
+		$view = $this->buildFileViewMock((string) $quota, $checkedPath);
23 23
 		$this->server = new \Sabre\DAV\Server();
24 24
 		$this->plugin = new QuotaPlugin($view);
25 25
 		$this->plugin->initialize($this->server);
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/FileTest.php 2 patches
Indentation   +966 added lines, -966 removed lines patch added patch discarded remove patch
@@ -51,983 +51,983 @@
 block discarded – undo
51 51
  * @package OCA\DAV\Tests\unit\Connector\Sabre
52 52
  */
53 53
 class FileTest extends TestCase {
54
-	use MountProviderTrait;
55
-	use UserTrait;
56
-
57
-	private string $user;
58
-	protected IConfig&MockObject $config;
59
-	protected IRequestId&MockObject $requestId;
60
-
61
-	protected function setUp(): void {
62
-		parent::setUp();
63
-
64
-		\OC_Hook::clear();
65
-
66
-		$this->user = 'test_user';
67
-		$this->createUser($this->user, 'pass');
68
-
69
-		self::loginAsUser($this->user);
70
-
71
-		$this->config = $this->createMock(IConfig::class);
72
-		$this->requestId = $this->createMock(IRequestId::class);
73
-	}
74
-
75
-	protected function tearDown(): void {
76
-		$userManager = Server::get(IUserManager::class);
77
-		$userManager->get($this->user)->delete();
78
-
79
-		parent::tearDown();
80
-	}
81
-
82
-	private function getMockStorage(): MockObject&IStorage {
83
-		$storage = $this->createMock(IStorage::class);
84
-		$storage->method('getId')
85
-			->willReturn('home::someuser');
86
-		return $storage;
87
-	}
88
-
89
-	private function getStream(string $string) {
90
-		$stream = fopen('php://temp', 'r+');
91
-		fwrite($stream, $string);
92
-		fseek($stream, 0);
93
-		return $stream;
94
-	}
95
-
96
-
97
-	public static function fopenFailuresProvider(): array {
98
-		return [
99
-			[
100
-				// return false
101
-				null,
102
-				'\Sabre\Dav\Exception',
103
-				false
104
-			],
105
-			[
106
-				new NotPermittedException(),
107
-				'Sabre\DAV\Exception\Forbidden'
108
-			],
109
-			[
110
-				new EntityTooLargeException(),
111
-				'OCA\DAV\Connector\Sabre\Exception\EntityTooLarge'
112
-			],
113
-			[
114
-				new InvalidContentException(),
115
-				'OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType'
116
-			],
117
-			[
118
-				new InvalidPathException(),
119
-				'Sabre\DAV\Exception\Forbidden'
120
-			],
121
-			[
122
-				new ForbiddenException('', true),
123
-				'OCA\DAV\Connector\Sabre\Exception\Forbidden'
124
-			],
125
-			[
126
-				new LockNotAcquiredException('/test.txt', 1),
127
-				'OCA\DAV\Connector\Sabre\Exception\FileLocked'
128
-			],
129
-			[
130
-				new LockedException('/test.txt'),
131
-				'OCA\DAV\Connector\Sabre\Exception\FileLocked'
132
-			],
133
-			[
134
-				new GenericEncryptionException(),
135
-				'Sabre\DAV\Exception\ServiceUnavailable'
136
-			],
137
-			[
138
-				new StorageNotAvailableException(),
139
-				'Sabre\DAV\Exception\ServiceUnavailable'
140
-			],
141
-			[
142
-				new \Sabre\DAV\Exception('Generic sabre exception'),
143
-				'Sabre\DAV\Exception',
144
-				false
145
-			],
146
-			[
147
-				new \Exception('Generic exception'),
148
-				'Sabre\DAV\Exception'
149
-			],
150
-		];
151
-	}
152
-
153
-	/**
154
-	 * @dataProvider fopenFailuresProvider
155
-	 */
156
-	public function testSimplePutFails(?\Throwable $thrownException, string $expectedException, bool $checkPreviousClass = true): void {
157
-		// setup
158
-		$storage = $this->getMockBuilder(Local::class)
159
-			->onlyMethods(['writeStream'])
160
-			->setConstructorArgs([['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]])
161
-			->getMock();
162
-		Filesystem::mount($storage, [], $this->user . '/');
163
-		/** @var View&MockObject $view */
164
-		$view = $this->getMockBuilder(View::class)
165
-			->onlyMethods(['getRelativePath', 'resolvePath'])
166
-			->getMock();
167
-		$view->expects($this->atLeastOnce())
168
-			->method('resolvePath')
169
-			->willReturnCallback(
170
-				function ($path) use ($storage) {
171
-					return [$storage, $path];
172
-				}
173
-			);
174
-
175
-		if ($thrownException !== null) {
176
-			$storage->expects($this->once())
177
-				->method('writeStream')
178
-				->will($this->throwException($thrownException));
179
-		} else {
180
-			$storage->expects($this->once())
181
-				->method('writeStream')
182
-				->willReturn(0);
183
-		}
184
-
185
-		$view->expects($this->any())
186
-			->method('getRelativePath')
187
-			->willReturnArgument(0);
188
-
189
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
190
-			'permissions' => Constants::PERMISSION_ALL,
191
-			'type' => FileInfo::TYPE_FOLDER,
192
-		], null);
193
-
194
-		$file = new File($view, $info);
195
-
196
-		// action
197
-		$caughtException = null;
198
-		try {
199
-			$file->put('test data');
200
-		} catch (\Exception $e) {
201
-			$caughtException = $e;
202
-		}
203
-
204
-		$this->assertInstanceOf($expectedException, $caughtException);
205
-		if ($checkPreviousClass) {
206
-			$this->assertInstanceOf(get_class($thrownException), $caughtException->getPrevious());
207
-		}
208
-
209
-		$this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
210
-	}
211
-
212
-	/**
213
-	 * Simulate putting a file to the given path.
214
-	 *
215
-	 * @param string $path path to put the file into
216
-	 * @param ?string $viewRoot root to use for the view
217
-	 * @param null|Request $request the HTTP request
218
-	 *
219
-	 * @return null|string of the PUT operation which is usually the etag
220
-	 */
221
-	private function doPut(string $path, ?string $viewRoot = null, ?Request $request = null) {
222
-		$view = Filesystem::getView();
223
-		if (!is_null($viewRoot)) {
224
-			$view = new View($viewRoot);
225
-		} else {
226
-			$viewRoot = '/' . $this->user . '/files';
227
-		}
228
-
229
-		$info = new \OC\Files\FileInfo(
230
-			$viewRoot . '/' . ltrim($path, '/'),
231
-			$this->getMockStorage(),
232
-			null,
233
-			[
234
-				'permissions' => Constants::PERMISSION_ALL,
235
-				'type' => FileInfo::TYPE_FOLDER,
236
-			],
237
-			null
238
-		);
239
-
240
-		/** @var File&MockObject $file */
241
-		$file = $this->getMockBuilder(File::class)
242
-			->setConstructorArgs([$view, $info, null, $request])
243
-			->onlyMethods(['header'])
244
-			->getMock();
245
-
246
-		// beforeMethod locks
247
-		$view->lockFile($path, ILockingProvider::LOCK_SHARED);
248
-
249
-		$result = $file->put($this->getStream('test data'));
250
-
251
-		// afterMethod unlocks
252
-		$view->unlockFile($path, ILockingProvider::LOCK_SHARED);
253
-
254
-		return $result;
255
-	}
256
-
257
-	/**
258
-	 * Test putting a single file
259
-	 */
260
-	public function testPutSingleFile(): void {
261
-		$this->assertNotEmpty($this->doPut('/foo.txt'));
262
-	}
263
-
264
-	public static function legalMtimeProvider(): array {
265
-		return [
266
-			'string' => [
267
-				'requestMtime' => 'string',
268
-				'resultMtime' => null
269
-			],
270
-			'castable string (int)' => [
271
-				'requestMtime' => '987654321',
272
-				'resultMtime' => 987654321
273
-			],
274
-			'castable string (float)' => [
275
-				'requestMtime' => '123456789.56',
276
-				'resultMtime' => 123456789
277
-			],
278
-			'float' => [
279
-				'requestMtime' => 123456789.56,
280
-				'resultMtime' => 123456789
281
-			],
282
-			'zero' => [
283
-				'requestMtime' => 0,
284
-				'resultMtime' => null
285
-			],
286
-			'zero string' => [
287
-				'requestMtime' => '0',
288
-				'resultMtime' => null
289
-			],
290
-			'negative zero string' => [
291
-				'requestMtime' => '-0',
292
-				'resultMtime' => null
293
-			],
294
-			'string starting with number following by char' => [
295
-				'requestMtime' => '2345asdf',
296
-				'resultMtime' => null
297
-			],
298
-			'string castable hex int' => [
299
-				'requestMtime' => '0x45adf',
300
-				'resultMtime' => null
301
-			],
302
-			'string that looks like invalid hex int' => [
303
-				'requestMtime' => '0x123g',
304
-				'resultMtime' => null
305
-			],
306
-			'negative int' => [
307
-				'requestMtime' => -34,
308
-				'resultMtime' => null
309
-			],
310
-			'negative float' => [
311
-				'requestMtime' => -34.43,
312
-				'resultMtime' => null
313
-			],
314
-		];
315
-	}
316
-
317
-	/**
318
-	 * Test putting a file with string Mtime
319
-	 * @dataProvider legalMtimeProvider
320
-	 */
321
-	public function testPutSingleFileLegalMtime(mixed $requestMtime, ?int $resultMtime): void {
322
-		$request = new Request([
323
-			'server' => [
324
-				'HTTP_X_OC_MTIME' => (string)$requestMtime,
325
-			]
326
-		], $this->requestId, $this->config, null);
327
-		$file = 'foo.txt';
328
-
329
-		if ($resultMtime === null) {
330
-			$this->expectException(\InvalidArgumentException::class);
331
-		}
332
-
333
-		$this->doPut($file, null, $request);
334
-
335
-		if ($resultMtime !== null) {
336
-			$this->assertEquals($resultMtime, $this->getFileInfos($file)['mtime']);
337
-		}
338
-	}
339
-
340
-	/**
341
-	 * Test that putting a file triggers create hooks
342
-	 */
343
-	public function testPutSingleFileTriggersHooks(): void {
344
-		HookHelper::setUpHooks();
345
-
346
-		$this->assertNotEmpty($this->doPut('/foo.txt'));
347
-
348
-		$this->assertCount(4, HookHelper::$hookCalls);
349
-		$this->assertHookCall(
350
-			HookHelper::$hookCalls[0],
351
-			Filesystem::signal_create,
352
-			'/foo.txt'
353
-		);
354
-		$this->assertHookCall(
355
-			HookHelper::$hookCalls[1],
356
-			Filesystem::signal_write,
357
-			'/foo.txt'
358
-		);
359
-		$this->assertHookCall(
360
-			HookHelper::$hookCalls[2],
361
-			Filesystem::signal_post_create,
362
-			'/foo.txt'
363
-		);
364
-		$this->assertHookCall(
365
-			HookHelper::$hookCalls[3],
366
-			Filesystem::signal_post_write,
367
-			'/foo.txt'
368
-		);
369
-	}
370
-
371
-	/**
372
-	 * Test that putting a file triggers update hooks
373
-	 */
374
-	public function testPutOverwriteFileTriggersHooks(): void {
375
-		$view = Filesystem::getView();
376
-		$view->file_put_contents('/foo.txt', 'some content that will be replaced');
377
-
378
-		HookHelper::setUpHooks();
379
-
380
-		$this->assertNotEmpty($this->doPut('/foo.txt'));
381
-
382
-		$this->assertCount(4, HookHelper::$hookCalls);
383
-		$this->assertHookCall(
384
-			HookHelper::$hookCalls[0],
385
-			Filesystem::signal_update,
386
-			'/foo.txt'
387
-		);
388
-		$this->assertHookCall(
389
-			HookHelper::$hookCalls[1],
390
-			Filesystem::signal_write,
391
-			'/foo.txt'
392
-		);
393
-		$this->assertHookCall(
394
-			HookHelper::$hookCalls[2],
395
-			Filesystem::signal_post_update,
396
-			'/foo.txt'
397
-		);
398
-		$this->assertHookCall(
399
-			HookHelper::$hookCalls[3],
400
-			Filesystem::signal_post_write,
401
-			'/foo.txt'
402
-		);
403
-	}
404
-
405
-	/**
406
-	 * Test that putting a file triggers hooks with the correct path
407
-	 * if the passed view was chrooted (can happen with public webdav
408
-	 * where the root is the share root)
409
-	 */
410
-	public function testPutSingleFileTriggersHooksDifferentRoot(): void {
411
-		$view = Filesystem::getView();
412
-		$view->mkdir('noderoot');
413
-
414
-		HookHelper::setUpHooks();
415
-
416
-		// happens with public webdav where the view root is the share root
417
-		$this->assertNotEmpty($this->doPut('/foo.txt', '/' . $this->user . '/files/noderoot'));
418
-
419
-		$this->assertCount(4, HookHelper::$hookCalls);
420
-		$this->assertHookCall(
421
-			HookHelper::$hookCalls[0],
422
-			Filesystem::signal_create,
423
-			'/noderoot/foo.txt'
424
-		);
425
-		$this->assertHookCall(
426
-			HookHelper::$hookCalls[1],
427
-			Filesystem::signal_write,
428
-			'/noderoot/foo.txt'
429
-		);
430
-		$this->assertHookCall(
431
-			HookHelper::$hookCalls[2],
432
-			Filesystem::signal_post_create,
433
-			'/noderoot/foo.txt'
434
-		);
435
-		$this->assertHookCall(
436
-			HookHelper::$hookCalls[3],
437
-			Filesystem::signal_post_write,
438
-			'/noderoot/foo.txt'
439
-		);
440
-	}
441
-
442
-	public static function cancellingHook($params): void {
443
-		self::$hookCalls[] = [
444
-			'signal' => Filesystem::signal_post_create,
445
-			'params' => $params
446
-		];
447
-	}
448
-
449
-	/**
450
-	 * Test put file with cancelled hook
451
-	 */
452
-	public function testPutSingleFileCancelPreHook(): void {
453
-		Util::connectHook(
454
-			Filesystem::CLASSNAME,
455
-			Filesystem::signal_create,
456
-			'\Test\HookHelper',
457
-			'cancellingCallback'
458
-		);
459
-
460
-		// action
461
-		$thrown = false;
462
-		try {
463
-			$this->doPut('/foo.txt');
464
-		} catch (\Sabre\DAV\Exception $e) {
465
-			$thrown = true;
466
-		}
467
-
468
-		$this->assertTrue($thrown);
469
-		$this->assertEmpty($this->listPartFiles(), 'No stray part files');
470
-	}
471
-
472
-	/**
473
-	 * Test exception when the uploaded size did not match
474
-	 */
475
-	public function testSimplePutFailsSizeCheck(): void {
476
-		// setup
477
-		/** @var View&MockObject */
478
-		$view = $this->getMockBuilder(View::class)
479
-			->onlyMethods(['rename', 'getRelativePath', 'filesize'])
480
-			->getMock();
481
-		$view->expects($this->any())
482
-			->method('rename')
483
-			->withAnyParameters()
484
-			->willReturn(false);
485
-		$view->expects($this->any())
486
-			->method('getRelativePath')
487
-			->willReturnArgument(0);
488
-
489
-		$view->expects($this->any())
490
-			->method('filesize')
491
-			->willReturn(123456);
492
-
493
-		$request = new Request([
494
-			'server' => [
495
-				'CONTENT_LENGTH' => '123456',
496
-			],
497
-			'method' => 'PUT',
498
-		], $this->requestId, $this->config, null);
499
-
500
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
501
-			'permissions' => Constants::PERMISSION_ALL,
502
-			'type' => FileInfo::TYPE_FOLDER,
503
-		], null);
504
-
505
-		$file = new File($view, $info, null, $request);
506
-
507
-		// action
508
-		$thrown = false;
509
-		try {
510
-			// beforeMethod locks
511
-			$file->acquireLock(ILockingProvider::LOCK_SHARED);
512
-
513
-			$file->put($this->getStream('test data'));
514
-
515
-			// afterMethod unlocks
516
-			$file->releaseLock(ILockingProvider::LOCK_SHARED);
517
-		} catch (\Sabre\DAV\Exception\BadRequest $e) {
518
-			$thrown = true;
519
-		}
520
-
521
-		$this->assertTrue($thrown);
522
-		$this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
523
-	}
524
-
525
-	/**
526
-	 * Test exception during final rename in simple upload mode
527
-	 */
528
-	public function testSimplePutFailsMoveFromStorage(): void {
529
-		$view = new View('/' . $this->user . '/files');
530
-
531
-		// simulate situation where the target file is locked
532
-		$view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE);
533
-
534
-		$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, [
535
-			'permissions' => Constants::PERMISSION_ALL,
536
-			'type' => FileInfo::TYPE_FOLDER,
537
-		], null);
538
-
539
-		$file = new File($view, $info);
540
-
541
-		// action
542
-		$thrown = false;
543
-		try {
544
-			// beforeMethod locks
545
-			$view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
546
-
547
-			$file->put($this->getStream('test data'));
548
-
549
-			// afterMethod unlocks
550
-			$view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
551
-		} catch (FileLocked $e) {
552
-			$thrown = true;
553
-		}
554
-
555
-		$this->assertTrue($thrown);
556
-		$this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
557
-	}
558
-
559
-	/**
560
-	 * Test put file with invalid chars
561
-	 */
562
-	public function testSimplePutInvalidChars(): void {
563
-		// setup
564
-		/** @var View&MockObject */
565
-		$view = $this->getMockBuilder(View::class)
566
-			->onlyMethods(['getRelativePath'])
567
-			->getMock();
568
-		$view->expects($this->any())
569
-			->method('getRelativePath')
570
-			->willReturnArgument(0);
571
-
572
-		$info = new \OC\Files\FileInfo("/i\nvalid", $this->getMockStorage(), null, [
573
-			'permissions' => Constants::PERMISSION_ALL,
574
-			'type' => FileInfo::TYPE_FOLDER,
575
-		], null);
576
-		$file = new File($view, $info);
577
-
578
-		// action
579
-		$thrown = false;
580
-		try {
581
-			// beforeMethod locks
582
-			$view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
583
-
584
-			$file->put($this->getStream('test data'));
585
-
586
-			// afterMethod unlocks
587
-			$view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
588
-		} catch (InvalidPath $e) {
589
-			$thrown = true;
590
-		}
591
-
592
-		$this->assertTrue($thrown);
593
-		$this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
594
-	}
595
-
596
-	/**
597
-	 * Test setting name with setName() with invalid chars
598
-	 *
599
-	 */
600
-	public function testSetNameInvalidChars(): void {
601
-		$this->expectException(InvalidPath::class);
602
-
603
-		// setup
604
-		/** @var View&MockObject */
605
-		$view = $this->getMockBuilder(View::class)
606
-			->onlyMethods(['getRelativePath'])
607
-			->getMock();
608
-
609
-		$view->expects($this->any())
610
-			->method('getRelativePath')
611
-			->willReturnArgument(0);
612
-
613
-		$info = new \OC\Files\FileInfo('/valid', $this->getMockStorage(), null, [
614
-			'permissions' => Constants::PERMISSION_ALL,
615
-			'type' => FileInfo::TYPE_FOLDER,
616
-		], null);
617
-		$file = new File($view, $info);
618
-
619
-		$file->setName("/i\nvalid");
620
-	}
621
-
622
-
623
-	public function testUploadAbort(): void {
624
-		// setup
625
-		/** @var View&MockObject */
626
-		$view = $this->getMockBuilder(View::class)
627
-			->onlyMethods(['rename', 'getRelativePath', 'filesize'])
628
-			->getMock();
629
-		$view->expects($this->any())
630
-			->method('rename')
631
-			->withAnyParameters()
632
-			->willReturn(false);
633
-		$view->expects($this->any())
634
-			->method('getRelativePath')
635
-			->willReturnArgument(0);
636
-		$view->expects($this->any())
637
-			->method('filesize')
638
-			->willReturn(123456);
639
-
640
-		$request = new Request([
641
-			'server' => [
642
-				'CONTENT_LENGTH' => '123456',
643
-			],
644
-			'method' => 'PUT',
645
-		], $this->requestId, $this->config, null);
646
-
647
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
648
-			'permissions' => Constants::PERMISSION_ALL,
649
-			'type' => FileInfo::TYPE_FOLDER,
650
-		], null);
651
-
652
-		$file = new File($view, $info, null, $request);
653
-
654
-		// action
655
-		$thrown = false;
656
-		try {
657
-			// beforeMethod locks
658
-			$view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
659
-
660
-			$file->put($this->getStream('test data'));
661
-
662
-			// afterMethod unlocks
663
-			$view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
664
-		} catch (\Sabre\DAV\Exception\BadRequest $e) {
665
-			$thrown = true;
666
-		}
667
-
668
-		$this->assertTrue($thrown);
669
-		$this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
670
-	}
671
-
672
-
673
-	public function testDeleteWhenAllowed(): void {
674
-		// setup
675
-		/** @var View&MockObject */
676
-		$view = $this->getMockBuilder(View::class)
677
-			->getMock();
678
-
679
-		$view->expects($this->once())
680
-			->method('unlink')
681
-			->willReturn(true);
682
-
683
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
684
-			'permissions' => Constants::PERMISSION_ALL,
685
-			'type' => FileInfo::TYPE_FOLDER,
686
-		], null);
687
-
688
-		$file = new File($view, $info);
689
-
690
-		// action
691
-		$file->delete();
692
-	}
693
-
694
-
695
-	public function testDeleteThrowsWhenDeletionNotAllowed(): void {
696
-		$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
697
-
698
-		// setup
699
-		/** @var View&MockObject */
700
-		$view = $this->getMockBuilder(View::class)
701
-			->getMock();
702
-
703
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
704
-			'permissions' => 0,
705
-			'type' => FileInfo::TYPE_FOLDER,
706
-		], null);
707
-
708
-		$file = new File($view, $info);
709
-
710
-		// action
711
-		$file->delete();
712
-	}
713
-
714
-
715
-	public function testDeleteThrowsWhenDeletionFailed(): void {
716
-		$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
717
-
718
-		// setup
719
-		/** @var View&MockObject */
720
-		$view = $this->getMockBuilder(View::class)
721
-			->getMock();
722
-
723
-		// but fails
724
-		$view->expects($this->once())
725
-			->method('unlink')
726
-			->willReturn(false);
727
-
728
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
729
-			'permissions' => Constants::PERMISSION_ALL,
730
-			'type' => FileInfo::TYPE_FOLDER,
731
-		], null);
732
-
733
-		$file = new File($view, $info);
734
-
735
-		// action
736
-		$file->delete();
737
-	}
738
-
739
-
740
-	public function testDeleteThrowsWhenDeletionThrows(): void {
741
-		$this->expectException(Forbidden::class);
742
-
743
-		// setup
744
-		/** @var View&MockObject */
745
-		$view = $this->getMockBuilder(View::class)
746
-			->getMock();
747
-
748
-		// but fails
749
-		$view->expects($this->once())
750
-			->method('unlink')
751
-			->willThrowException(new ForbiddenException('', true));
752
-
753
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
754
-			'permissions' => Constants::PERMISSION_ALL,
755
-			'type' => FileInfo::TYPE_FOLDER,
756
-		], null);
757
-
758
-		$file = new File($view, $info);
759
-
760
-		// action
761
-		$file->delete();
762
-	}
763
-
764
-	/**
765
-	 * Asserts hook call
766
-	 *
767
-	 * @param array $callData hook call data to check
768
-	 * @param string $signal signal name
769
-	 * @param string $hookPath hook path
770
-	 */
771
-	protected function assertHookCall($callData, $signal, $hookPath) {
772
-		$this->assertEquals($signal, $callData['signal']);
773
-		$params = $callData['params'];
774
-		$this->assertEquals(
775
-			$hookPath,
776
-			$params[Filesystem::signal_param_path]
777
-		);
778
-	}
779
-
780
-	/**
781
-	 * Test whether locks are set before and after the operation
782
-	 */
783
-	public function testPutLocking(): void {
784
-		$view = new View('/' . $this->user . '/files/');
785
-
786
-		$path = 'test-locking.txt';
787
-		$info = new \OC\Files\FileInfo(
788
-			'/' . $this->user . '/files/' . $path,
789
-			$this->getMockStorage(),
790
-			null,
791
-			[
792
-				'permissions' => Constants::PERMISSION_ALL,
793
-				'type' => FileInfo::TYPE_FOLDER,
794
-			],
795
-			null
796
-		);
797
-
798
-		$file = new File($view, $info);
799
-
800
-		$this->assertFalse(
801
-			$this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED),
802
-			'File unlocked before put'
803
-		);
804
-		$this->assertFalse(
805
-			$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE),
806
-			'File unlocked before put'
807
-		);
808
-
809
-		$wasLockedPre = false;
810
-		$wasLockedPost = false;
811
-		$eventHandler = $this->getMockBuilder(\stdclass::class)
812
-			->addMethods(['writeCallback', 'postWriteCallback'])
813
-			->getMock();
814
-
815
-		// both pre and post hooks might need access to the file,
816
-		// so only shared lock is acceptable
817
-		$eventHandler->expects($this->once())
818
-			->method('writeCallback')
819
-			->willReturnCallback(
820
-				function () use ($view, $path, &$wasLockedPre): void {
821
-					$wasLockedPre = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED);
822
-					$wasLockedPre = $wasLockedPre && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE);
823
-				}
824
-			);
825
-		$eventHandler->expects($this->once())
826
-			->method('postWriteCallback')
827
-			->willReturnCallback(
828
-				function () use ($view, $path, &$wasLockedPost): void {
829
-					$wasLockedPost = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED);
830
-					$wasLockedPost = $wasLockedPost && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE);
831
-				}
832
-			);
833
-
834
-		Util::connectHook(
835
-			Filesystem::CLASSNAME,
836
-			Filesystem::signal_write,
837
-			$eventHandler,
838
-			'writeCallback'
839
-		);
840
-		Util::connectHook(
841
-			Filesystem::CLASSNAME,
842
-			Filesystem::signal_post_write,
843
-			$eventHandler,
844
-			'postWriteCallback'
845
-		);
846
-
847
-		// beforeMethod locks
848
-		$view->lockFile($path, ILockingProvider::LOCK_SHARED);
849
-
850
-		$this->assertNotEmpty($file->put($this->getStream('test data')));
851
-
852
-		// afterMethod unlocks
853
-		$view->unlockFile($path, ILockingProvider::LOCK_SHARED);
854
-
855
-		$this->assertTrue($wasLockedPre, 'File was locked during pre-hooks');
856
-		$this->assertTrue($wasLockedPost, 'File was locked during post-hooks');
857
-
858
-		$this->assertFalse(
859
-			$this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED),
860
-			'File unlocked after put'
861
-		);
862
-		$this->assertFalse(
863
-			$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE),
864
-			'File unlocked after put'
865
-		);
866
-	}
867
-
868
-	/**
869
-	 * Returns part files in the given path
870
-	 *
871
-	 * @param \OC\Files\View view which root is the current user's "files" folder
872
-	 * @param string $path path for which to list part files
873
-	 *
874
-	 * @return array list of part files
875
-	 */
876
-	private function listPartFiles(?View $userView = null, $path = '') {
877
-		if ($userView === null) {
878
-			$userView = Filesystem::getView();
879
-		}
880
-		$files = [];
881
-		[$storage, $internalPath] = $userView->resolvePath($path);
882
-		if ($storage instanceof Local) {
883
-			$realPath = $storage->getSourcePath($internalPath);
884
-			$dh = opendir($realPath);
885
-			while (($file = readdir($dh)) !== false) {
886
-				if (str_ends_with($file, '.part')) {
887
-					$files[] = $file;
888
-				}
889
-			}
890
-			closedir($dh);
891
-		}
892
-		return $files;
893
-	}
894
-
895
-	/**
896
-	 * returns an array of file information filesize, mtime, filetype,  mimetype
897
-	 *
898
-	 * @param string $path
899
-	 * @param View $userView
900
-	 * @return array
901
-	 */
902
-	private function getFileInfos($path = '', ?View $userView = null) {
903
-		if ($userView === null) {
904
-			$userView = Filesystem::getView();
905
-		}
906
-		return [
907
-			'filesize' => $userView->filesize($path),
908
-			'mtime' => $userView->filemtime($path),
909
-			'filetype' => $userView->filetype($path),
910
-			'mimetype' => $userView->getMimeType($path)
911
-		];
912
-	}
913
-
914
-
915
-	public function testGetFopenFails(): void {
916
-		$this->expectException(\Sabre\DAV\Exception\ServiceUnavailable::class);
917
-
918
-		/** @var View&MockObject */
919
-		$view = $this->getMockBuilder(View::class)
920
-			->onlyMethods(['fopen'])
921
-			->getMock();
922
-		$view->expects($this->atLeastOnce())
923
-			->method('fopen')
924
-			->willReturn(false);
925
-
926
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
927
-			'permissions' => Constants::PERMISSION_ALL,
928
-			'type' => FileInfo::TYPE_FILE,
929
-		], null);
930
-
931
-		$file = new File($view, $info);
932
-
933
-		$file->get();
934
-	}
935
-
936
-
937
-	public function testGetFopenThrows(): void {
938
-		$this->expectException(Forbidden::class);
939
-
940
-		/** @var View&MockObject */
941
-		$view = $this->getMockBuilder(View::class)
942
-			->onlyMethods(['fopen'])
943
-			->getMock();
944
-		$view->expects($this->atLeastOnce())
945
-			->method('fopen')
946
-			->willThrowException(new ForbiddenException('', true));
947
-
948
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
949
-			'permissions' => Constants::PERMISSION_ALL,
950
-			'type' => FileInfo::TYPE_FILE,
951
-		], null);
952
-
953
-		$file = new File($view, $info);
954
-
955
-		$file->get();
956
-	}
957
-
958
-
959
-	public function testGetThrowsIfNoPermission(): void {
960
-		$this->expectException(\Sabre\DAV\Exception\NotFound::class);
961
-
962
-		/** @var View&MockObject */
963
-		$view = $this->getMockBuilder(View::class)
964
-			->onlyMethods(['fopen'])
965
-			->getMock();
966
-		$view->expects($this->never())
967
-			->method('fopen');
968
-
969
-		$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
970
-			'permissions' => Constants::PERMISSION_CREATE, // no read perm
971
-			'type' => FileInfo::TYPE_FOLDER,
972
-		], null);
973
-
974
-		$file = new  File($view, $info);
975
-
976
-		$file->get();
977
-	}
978
-
979
-	public function testSimplePutNoCreatePermissions(): void {
980
-		$this->logout();
981
-
982
-		$storage = new Temporary([]);
983
-		$storage->file_put_contents('file.txt', 'old content');
984
-		$noCreateStorage = new PermissionsMask([
985
-			'storage' => $storage,
986
-			'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE
987
-		]);
988
-
989
-		$this->registerMount($this->user, $noCreateStorage, '/' . $this->user . '/files/root');
54
+    use MountProviderTrait;
55
+    use UserTrait;
56
+
57
+    private string $user;
58
+    protected IConfig&MockObject $config;
59
+    protected IRequestId&MockObject $requestId;
60
+
61
+    protected function setUp(): void {
62
+        parent::setUp();
63
+
64
+        \OC_Hook::clear();
65
+
66
+        $this->user = 'test_user';
67
+        $this->createUser($this->user, 'pass');
68
+
69
+        self::loginAsUser($this->user);
70
+
71
+        $this->config = $this->createMock(IConfig::class);
72
+        $this->requestId = $this->createMock(IRequestId::class);
73
+    }
74
+
75
+    protected function tearDown(): void {
76
+        $userManager = Server::get(IUserManager::class);
77
+        $userManager->get($this->user)->delete();
78
+
79
+        parent::tearDown();
80
+    }
81
+
82
+    private function getMockStorage(): MockObject&IStorage {
83
+        $storage = $this->createMock(IStorage::class);
84
+        $storage->method('getId')
85
+            ->willReturn('home::someuser');
86
+        return $storage;
87
+    }
88
+
89
+    private function getStream(string $string) {
90
+        $stream = fopen('php://temp', 'r+');
91
+        fwrite($stream, $string);
92
+        fseek($stream, 0);
93
+        return $stream;
94
+    }
95
+
96
+
97
+    public static function fopenFailuresProvider(): array {
98
+        return [
99
+            [
100
+                // return false
101
+                null,
102
+                '\Sabre\Dav\Exception',
103
+                false
104
+            ],
105
+            [
106
+                new NotPermittedException(),
107
+                'Sabre\DAV\Exception\Forbidden'
108
+            ],
109
+            [
110
+                new EntityTooLargeException(),
111
+                'OCA\DAV\Connector\Sabre\Exception\EntityTooLarge'
112
+            ],
113
+            [
114
+                new InvalidContentException(),
115
+                'OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType'
116
+            ],
117
+            [
118
+                new InvalidPathException(),
119
+                'Sabre\DAV\Exception\Forbidden'
120
+            ],
121
+            [
122
+                new ForbiddenException('', true),
123
+                'OCA\DAV\Connector\Sabre\Exception\Forbidden'
124
+            ],
125
+            [
126
+                new LockNotAcquiredException('/test.txt', 1),
127
+                'OCA\DAV\Connector\Sabre\Exception\FileLocked'
128
+            ],
129
+            [
130
+                new LockedException('/test.txt'),
131
+                'OCA\DAV\Connector\Sabre\Exception\FileLocked'
132
+            ],
133
+            [
134
+                new GenericEncryptionException(),
135
+                'Sabre\DAV\Exception\ServiceUnavailable'
136
+            ],
137
+            [
138
+                new StorageNotAvailableException(),
139
+                'Sabre\DAV\Exception\ServiceUnavailable'
140
+            ],
141
+            [
142
+                new \Sabre\DAV\Exception('Generic sabre exception'),
143
+                'Sabre\DAV\Exception',
144
+                false
145
+            ],
146
+            [
147
+                new \Exception('Generic exception'),
148
+                'Sabre\DAV\Exception'
149
+            ],
150
+        ];
151
+    }
152
+
153
+    /**
154
+     * @dataProvider fopenFailuresProvider
155
+     */
156
+    public function testSimplePutFails(?\Throwable $thrownException, string $expectedException, bool $checkPreviousClass = true): void {
157
+        // setup
158
+        $storage = $this->getMockBuilder(Local::class)
159
+            ->onlyMethods(['writeStream'])
160
+            ->setConstructorArgs([['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]])
161
+            ->getMock();
162
+        Filesystem::mount($storage, [], $this->user . '/');
163
+        /** @var View&MockObject $view */
164
+        $view = $this->getMockBuilder(View::class)
165
+            ->onlyMethods(['getRelativePath', 'resolvePath'])
166
+            ->getMock();
167
+        $view->expects($this->atLeastOnce())
168
+            ->method('resolvePath')
169
+            ->willReturnCallback(
170
+                function ($path) use ($storage) {
171
+                    return [$storage, $path];
172
+                }
173
+            );
174
+
175
+        if ($thrownException !== null) {
176
+            $storage->expects($this->once())
177
+                ->method('writeStream')
178
+                ->will($this->throwException($thrownException));
179
+        } else {
180
+            $storage->expects($this->once())
181
+                ->method('writeStream')
182
+                ->willReturn(0);
183
+        }
184
+
185
+        $view->expects($this->any())
186
+            ->method('getRelativePath')
187
+            ->willReturnArgument(0);
188
+
189
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
190
+            'permissions' => Constants::PERMISSION_ALL,
191
+            'type' => FileInfo::TYPE_FOLDER,
192
+        ], null);
193
+
194
+        $file = new File($view, $info);
195
+
196
+        // action
197
+        $caughtException = null;
198
+        try {
199
+            $file->put('test data');
200
+        } catch (\Exception $e) {
201
+            $caughtException = $e;
202
+        }
203
+
204
+        $this->assertInstanceOf($expectedException, $caughtException);
205
+        if ($checkPreviousClass) {
206
+            $this->assertInstanceOf(get_class($thrownException), $caughtException->getPrevious());
207
+        }
208
+
209
+        $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
210
+    }
211
+
212
+    /**
213
+     * Simulate putting a file to the given path.
214
+     *
215
+     * @param string $path path to put the file into
216
+     * @param ?string $viewRoot root to use for the view
217
+     * @param null|Request $request the HTTP request
218
+     *
219
+     * @return null|string of the PUT operation which is usually the etag
220
+     */
221
+    private function doPut(string $path, ?string $viewRoot = null, ?Request $request = null) {
222
+        $view = Filesystem::getView();
223
+        if (!is_null($viewRoot)) {
224
+            $view = new View($viewRoot);
225
+        } else {
226
+            $viewRoot = '/' . $this->user . '/files';
227
+        }
228
+
229
+        $info = new \OC\Files\FileInfo(
230
+            $viewRoot . '/' . ltrim($path, '/'),
231
+            $this->getMockStorage(),
232
+            null,
233
+            [
234
+                'permissions' => Constants::PERMISSION_ALL,
235
+                'type' => FileInfo::TYPE_FOLDER,
236
+            ],
237
+            null
238
+        );
239
+
240
+        /** @var File&MockObject $file */
241
+        $file = $this->getMockBuilder(File::class)
242
+            ->setConstructorArgs([$view, $info, null, $request])
243
+            ->onlyMethods(['header'])
244
+            ->getMock();
245
+
246
+        // beforeMethod locks
247
+        $view->lockFile($path, ILockingProvider::LOCK_SHARED);
248
+
249
+        $result = $file->put($this->getStream('test data'));
250
+
251
+        // afterMethod unlocks
252
+        $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
253
+
254
+        return $result;
255
+    }
256
+
257
+    /**
258
+     * Test putting a single file
259
+     */
260
+    public function testPutSingleFile(): void {
261
+        $this->assertNotEmpty($this->doPut('/foo.txt'));
262
+    }
263
+
264
+    public static function legalMtimeProvider(): array {
265
+        return [
266
+            'string' => [
267
+                'requestMtime' => 'string',
268
+                'resultMtime' => null
269
+            ],
270
+            'castable string (int)' => [
271
+                'requestMtime' => '987654321',
272
+                'resultMtime' => 987654321
273
+            ],
274
+            'castable string (float)' => [
275
+                'requestMtime' => '123456789.56',
276
+                'resultMtime' => 123456789
277
+            ],
278
+            'float' => [
279
+                'requestMtime' => 123456789.56,
280
+                'resultMtime' => 123456789
281
+            ],
282
+            'zero' => [
283
+                'requestMtime' => 0,
284
+                'resultMtime' => null
285
+            ],
286
+            'zero string' => [
287
+                'requestMtime' => '0',
288
+                'resultMtime' => null
289
+            ],
290
+            'negative zero string' => [
291
+                'requestMtime' => '-0',
292
+                'resultMtime' => null
293
+            ],
294
+            'string starting with number following by char' => [
295
+                'requestMtime' => '2345asdf',
296
+                'resultMtime' => null
297
+            ],
298
+            'string castable hex int' => [
299
+                'requestMtime' => '0x45adf',
300
+                'resultMtime' => null
301
+            ],
302
+            'string that looks like invalid hex int' => [
303
+                'requestMtime' => '0x123g',
304
+                'resultMtime' => null
305
+            ],
306
+            'negative int' => [
307
+                'requestMtime' => -34,
308
+                'resultMtime' => null
309
+            ],
310
+            'negative float' => [
311
+                'requestMtime' => -34.43,
312
+                'resultMtime' => null
313
+            ],
314
+        ];
315
+    }
316
+
317
+    /**
318
+     * Test putting a file with string Mtime
319
+     * @dataProvider legalMtimeProvider
320
+     */
321
+    public function testPutSingleFileLegalMtime(mixed $requestMtime, ?int $resultMtime): void {
322
+        $request = new Request([
323
+            'server' => [
324
+                'HTTP_X_OC_MTIME' => (string)$requestMtime,
325
+            ]
326
+        ], $this->requestId, $this->config, null);
327
+        $file = 'foo.txt';
328
+
329
+        if ($resultMtime === null) {
330
+            $this->expectException(\InvalidArgumentException::class);
331
+        }
332
+
333
+        $this->doPut($file, null, $request);
334
+
335
+        if ($resultMtime !== null) {
336
+            $this->assertEquals($resultMtime, $this->getFileInfos($file)['mtime']);
337
+        }
338
+    }
339
+
340
+    /**
341
+     * Test that putting a file triggers create hooks
342
+     */
343
+    public function testPutSingleFileTriggersHooks(): void {
344
+        HookHelper::setUpHooks();
345
+
346
+        $this->assertNotEmpty($this->doPut('/foo.txt'));
347
+
348
+        $this->assertCount(4, HookHelper::$hookCalls);
349
+        $this->assertHookCall(
350
+            HookHelper::$hookCalls[0],
351
+            Filesystem::signal_create,
352
+            '/foo.txt'
353
+        );
354
+        $this->assertHookCall(
355
+            HookHelper::$hookCalls[1],
356
+            Filesystem::signal_write,
357
+            '/foo.txt'
358
+        );
359
+        $this->assertHookCall(
360
+            HookHelper::$hookCalls[2],
361
+            Filesystem::signal_post_create,
362
+            '/foo.txt'
363
+        );
364
+        $this->assertHookCall(
365
+            HookHelper::$hookCalls[3],
366
+            Filesystem::signal_post_write,
367
+            '/foo.txt'
368
+        );
369
+    }
370
+
371
+    /**
372
+     * Test that putting a file triggers update hooks
373
+     */
374
+    public function testPutOverwriteFileTriggersHooks(): void {
375
+        $view = Filesystem::getView();
376
+        $view->file_put_contents('/foo.txt', 'some content that will be replaced');
377
+
378
+        HookHelper::setUpHooks();
379
+
380
+        $this->assertNotEmpty($this->doPut('/foo.txt'));
381
+
382
+        $this->assertCount(4, HookHelper::$hookCalls);
383
+        $this->assertHookCall(
384
+            HookHelper::$hookCalls[0],
385
+            Filesystem::signal_update,
386
+            '/foo.txt'
387
+        );
388
+        $this->assertHookCall(
389
+            HookHelper::$hookCalls[1],
390
+            Filesystem::signal_write,
391
+            '/foo.txt'
392
+        );
393
+        $this->assertHookCall(
394
+            HookHelper::$hookCalls[2],
395
+            Filesystem::signal_post_update,
396
+            '/foo.txt'
397
+        );
398
+        $this->assertHookCall(
399
+            HookHelper::$hookCalls[3],
400
+            Filesystem::signal_post_write,
401
+            '/foo.txt'
402
+        );
403
+    }
404
+
405
+    /**
406
+     * Test that putting a file triggers hooks with the correct path
407
+     * if the passed view was chrooted (can happen with public webdav
408
+     * where the root is the share root)
409
+     */
410
+    public function testPutSingleFileTriggersHooksDifferentRoot(): void {
411
+        $view = Filesystem::getView();
412
+        $view->mkdir('noderoot');
413
+
414
+        HookHelper::setUpHooks();
415
+
416
+        // happens with public webdav where the view root is the share root
417
+        $this->assertNotEmpty($this->doPut('/foo.txt', '/' . $this->user . '/files/noderoot'));
418
+
419
+        $this->assertCount(4, HookHelper::$hookCalls);
420
+        $this->assertHookCall(
421
+            HookHelper::$hookCalls[0],
422
+            Filesystem::signal_create,
423
+            '/noderoot/foo.txt'
424
+        );
425
+        $this->assertHookCall(
426
+            HookHelper::$hookCalls[1],
427
+            Filesystem::signal_write,
428
+            '/noderoot/foo.txt'
429
+        );
430
+        $this->assertHookCall(
431
+            HookHelper::$hookCalls[2],
432
+            Filesystem::signal_post_create,
433
+            '/noderoot/foo.txt'
434
+        );
435
+        $this->assertHookCall(
436
+            HookHelper::$hookCalls[3],
437
+            Filesystem::signal_post_write,
438
+            '/noderoot/foo.txt'
439
+        );
440
+    }
441
+
442
+    public static function cancellingHook($params): void {
443
+        self::$hookCalls[] = [
444
+            'signal' => Filesystem::signal_post_create,
445
+            'params' => $params
446
+        ];
447
+    }
448
+
449
+    /**
450
+     * Test put file with cancelled hook
451
+     */
452
+    public function testPutSingleFileCancelPreHook(): void {
453
+        Util::connectHook(
454
+            Filesystem::CLASSNAME,
455
+            Filesystem::signal_create,
456
+            '\Test\HookHelper',
457
+            'cancellingCallback'
458
+        );
459
+
460
+        // action
461
+        $thrown = false;
462
+        try {
463
+            $this->doPut('/foo.txt');
464
+        } catch (\Sabre\DAV\Exception $e) {
465
+            $thrown = true;
466
+        }
467
+
468
+        $this->assertTrue($thrown);
469
+        $this->assertEmpty($this->listPartFiles(), 'No stray part files');
470
+    }
471
+
472
+    /**
473
+     * Test exception when the uploaded size did not match
474
+     */
475
+    public function testSimplePutFailsSizeCheck(): void {
476
+        // setup
477
+        /** @var View&MockObject */
478
+        $view = $this->getMockBuilder(View::class)
479
+            ->onlyMethods(['rename', 'getRelativePath', 'filesize'])
480
+            ->getMock();
481
+        $view->expects($this->any())
482
+            ->method('rename')
483
+            ->withAnyParameters()
484
+            ->willReturn(false);
485
+        $view->expects($this->any())
486
+            ->method('getRelativePath')
487
+            ->willReturnArgument(0);
488
+
489
+        $view->expects($this->any())
490
+            ->method('filesize')
491
+            ->willReturn(123456);
492
+
493
+        $request = new Request([
494
+            'server' => [
495
+                'CONTENT_LENGTH' => '123456',
496
+            ],
497
+            'method' => 'PUT',
498
+        ], $this->requestId, $this->config, null);
499
+
500
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
501
+            'permissions' => Constants::PERMISSION_ALL,
502
+            'type' => FileInfo::TYPE_FOLDER,
503
+        ], null);
504
+
505
+        $file = new File($view, $info, null, $request);
506
+
507
+        // action
508
+        $thrown = false;
509
+        try {
510
+            // beforeMethod locks
511
+            $file->acquireLock(ILockingProvider::LOCK_SHARED);
512
+
513
+            $file->put($this->getStream('test data'));
514
+
515
+            // afterMethod unlocks
516
+            $file->releaseLock(ILockingProvider::LOCK_SHARED);
517
+        } catch (\Sabre\DAV\Exception\BadRequest $e) {
518
+            $thrown = true;
519
+        }
520
+
521
+        $this->assertTrue($thrown);
522
+        $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
523
+    }
524
+
525
+    /**
526
+     * Test exception during final rename in simple upload mode
527
+     */
528
+    public function testSimplePutFailsMoveFromStorage(): void {
529
+        $view = new View('/' . $this->user . '/files');
530
+
531
+        // simulate situation where the target file is locked
532
+        $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE);
533
+
534
+        $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, [
535
+            'permissions' => Constants::PERMISSION_ALL,
536
+            'type' => FileInfo::TYPE_FOLDER,
537
+        ], null);
538
+
539
+        $file = new File($view, $info);
540
+
541
+        // action
542
+        $thrown = false;
543
+        try {
544
+            // beforeMethod locks
545
+            $view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
546
+
547
+            $file->put($this->getStream('test data'));
548
+
549
+            // afterMethod unlocks
550
+            $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
551
+        } catch (FileLocked $e) {
552
+            $thrown = true;
553
+        }
554
+
555
+        $this->assertTrue($thrown);
556
+        $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
557
+    }
558
+
559
+    /**
560
+     * Test put file with invalid chars
561
+     */
562
+    public function testSimplePutInvalidChars(): void {
563
+        // setup
564
+        /** @var View&MockObject */
565
+        $view = $this->getMockBuilder(View::class)
566
+            ->onlyMethods(['getRelativePath'])
567
+            ->getMock();
568
+        $view->expects($this->any())
569
+            ->method('getRelativePath')
570
+            ->willReturnArgument(0);
571
+
572
+        $info = new \OC\Files\FileInfo("/i\nvalid", $this->getMockStorage(), null, [
573
+            'permissions' => Constants::PERMISSION_ALL,
574
+            'type' => FileInfo::TYPE_FOLDER,
575
+        ], null);
576
+        $file = new File($view, $info);
577
+
578
+        // action
579
+        $thrown = false;
580
+        try {
581
+            // beforeMethod locks
582
+            $view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
583
+
584
+            $file->put($this->getStream('test data'));
585
+
586
+            // afterMethod unlocks
587
+            $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
588
+        } catch (InvalidPath $e) {
589
+            $thrown = true;
590
+        }
591
+
592
+        $this->assertTrue($thrown);
593
+        $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
594
+    }
595
+
596
+    /**
597
+     * Test setting name with setName() with invalid chars
598
+     *
599
+     */
600
+    public function testSetNameInvalidChars(): void {
601
+        $this->expectException(InvalidPath::class);
602
+
603
+        // setup
604
+        /** @var View&MockObject */
605
+        $view = $this->getMockBuilder(View::class)
606
+            ->onlyMethods(['getRelativePath'])
607
+            ->getMock();
608
+
609
+        $view->expects($this->any())
610
+            ->method('getRelativePath')
611
+            ->willReturnArgument(0);
612
+
613
+        $info = new \OC\Files\FileInfo('/valid', $this->getMockStorage(), null, [
614
+            'permissions' => Constants::PERMISSION_ALL,
615
+            'type' => FileInfo::TYPE_FOLDER,
616
+        ], null);
617
+        $file = new File($view, $info);
618
+
619
+        $file->setName("/i\nvalid");
620
+    }
621
+
622
+
623
+    public function testUploadAbort(): void {
624
+        // setup
625
+        /** @var View&MockObject */
626
+        $view = $this->getMockBuilder(View::class)
627
+            ->onlyMethods(['rename', 'getRelativePath', 'filesize'])
628
+            ->getMock();
629
+        $view->expects($this->any())
630
+            ->method('rename')
631
+            ->withAnyParameters()
632
+            ->willReturn(false);
633
+        $view->expects($this->any())
634
+            ->method('getRelativePath')
635
+            ->willReturnArgument(0);
636
+        $view->expects($this->any())
637
+            ->method('filesize')
638
+            ->willReturn(123456);
639
+
640
+        $request = new Request([
641
+            'server' => [
642
+                'CONTENT_LENGTH' => '123456',
643
+            ],
644
+            'method' => 'PUT',
645
+        ], $this->requestId, $this->config, null);
646
+
647
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
648
+            'permissions' => Constants::PERMISSION_ALL,
649
+            'type' => FileInfo::TYPE_FOLDER,
650
+        ], null);
651
+
652
+        $file = new File($view, $info, null, $request);
653
+
654
+        // action
655
+        $thrown = false;
656
+        try {
657
+            // beforeMethod locks
658
+            $view->lockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
659
+
660
+            $file->put($this->getStream('test data'));
661
+
662
+            // afterMethod unlocks
663
+            $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED);
664
+        } catch (\Sabre\DAV\Exception\BadRequest $e) {
665
+            $thrown = true;
666
+        }
667
+
668
+        $this->assertTrue($thrown);
669
+        $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files');
670
+    }
671
+
672
+
673
+    public function testDeleteWhenAllowed(): void {
674
+        // setup
675
+        /** @var View&MockObject */
676
+        $view = $this->getMockBuilder(View::class)
677
+            ->getMock();
678
+
679
+        $view->expects($this->once())
680
+            ->method('unlink')
681
+            ->willReturn(true);
682
+
683
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
684
+            'permissions' => Constants::PERMISSION_ALL,
685
+            'type' => FileInfo::TYPE_FOLDER,
686
+        ], null);
687
+
688
+        $file = new File($view, $info);
689
+
690
+        // action
691
+        $file->delete();
692
+    }
693
+
694
+
695
+    public function testDeleteThrowsWhenDeletionNotAllowed(): void {
696
+        $this->expectException(\Sabre\DAV\Exception\Forbidden::class);
697
+
698
+        // setup
699
+        /** @var View&MockObject */
700
+        $view = $this->getMockBuilder(View::class)
701
+            ->getMock();
702
+
703
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
704
+            'permissions' => 0,
705
+            'type' => FileInfo::TYPE_FOLDER,
706
+        ], null);
707
+
708
+        $file = new File($view, $info);
709
+
710
+        // action
711
+        $file->delete();
712
+    }
713
+
714
+
715
+    public function testDeleteThrowsWhenDeletionFailed(): void {
716
+        $this->expectException(\Sabre\DAV\Exception\Forbidden::class);
717
+
718
+        // setup
719
+        /** @var View&MockObject */
720
+        $view = $this->getMockBuilder(View::class)
721
+            ->getMock();
722
+
723
+        // but fails
724
+        $view->expects($this->once())
725
+            ->method('unlink')
726
+            ->willReturn(false);
727
+
728
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
729
+            'permissions' => Constants::PERMISSION_ALL,
730
+            'type' => FileInfo::TYPE_FOLDER,
731
+        ], null);
732
+
733
+        $file = new File($view, $info);
734
+
735
+        // action
736
+        $file->delete();
737
+    }
738
+
739
+
740
+    public function testDeleteThrowsWhenDeletionThrows(): void {
741
+        $this->expectException(Forbidden::class);
742
+
743
+        // setup
744
+        /** @var View&MockObject */
745
+        $view = $this->getMockBuilder(View::class)
746
+            ->getMock();
747
+
748
+        // but fails
749
+        $view->expects($this->once())
750
+            ->method('unlink')
751
+            ->willThrowException(new ForbiddenException('', true));
752
+
753
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
754
+            'permissions' => Constants::PERMISSION_ALL,
755
+            'type' => FileInfo::TYPE_FOLDER,
756
+        ], null);
757
+
758
+        $file = new File($view, $info);
759
+
760
+        // action
761
+        $file->delete();
762
+    }
763
+
764
+    /**
765
+     * Asserts hook call
766
+     *
767
+     * @param array $callData hook call data to check
768
+     * @param string $signal signal name
769
+     * @param string $hookPath hook path
770
+     */
771
+    protected function assertHookCall($callData, $signal, $hookPath) {
772
+        $this->assertEquals($signal, $callData['signal']);
773
+        $params = $callData['params'];
774
+        $this->assertEquals(
775
+            $hookPath,
776
+            $params[Filesystem::signal_param_path]
777
+        );
778
+    }
779
+
780
+    /**
781
+     * Test whether locks are set before and after the operation
782
+     */
783
+    public function testPutLocking(): void {
784
+        $view = new View('/' . $this->user . '/files/');
785
+
786
+        $path = 'test-locking.txt';
787
+        $info = new \OC\Files\FileInfo(
788
+            '/' . $this->user . '/files/' . $path,
789
+            $this->getMockStorage(),
790
+            null,
791
+            [
792
+                'permissions' => Constants::PERMISSION_ALL,
793
+                'type' => FileInfo::TYPE_FOLDER,
794
+            ],
795
+            null
796
+        );
797
+
798
+        $file = new File($view, $info);
799
+
800
+        $this->assertFalse(
801
+            $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED),
802
+            'File unlocked before put'
803
+        );
804
+        $this->assertFalse(
805
+            $this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE),
806
+            'File unlocked before put'
807
+        );
808
+
809
+        $wasLockedPre = false;
810
+        $wasLockedPost = false;
811
+        $eventHandler = $this->getMockBuilder(\stdclass::class)
812
+            ->addMethods(['writeCallback', 'postWriteCallback'])
813
+            ->getMock();
814
+
815
+        // both pre and post hooks might need access to the file,
816
+        // so only shared lock is acceptable
817
+        $eventHandler->expects($this->once())
818
+            ->method('writeCallback')
819
+            ->willReturnCallback(
820
+                function () use ($view, $path, &$wasLockedPre): void {
821
+                    $wasLockedPre = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED);
822
+                    $wasLockedPre = $wasLockedPre && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE);
823
+                }
824
+            );
825
+        $eventHandler->expects($this->once())
826
+            ->method('postWriteCallback')
827
+            ->willReturnCallback(
828
+                function () use ($view, $path, &$wasLockedPost): void {
829
+                    $wasLockedPost = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED);
830
+                    $wasLockedPost = $wasLockedPost && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE);
831
+                }
832
+            );
833
+
834
+        Util::connectHook(
835
+            Filesystem::CLASSNAME,
836
+            Filesystem::signal_write,
837
+            $eventHandler,
838
+            'writeCallback'
839
+        );
840
+        Util::connectHook(
841
+            Filesystem::CLASSNAME,
842
+            Filesystem::signal_post_write,
843
+            $eventHandler,
844
+            'postWriteCallback'
845
+        );
846
+
847
+        // beforeMethod locks
848
+        $view->lockFile($path, ILockingProvider::LOCK_SHARED);
849
+
850
+        $this->assertNotEmpty($file->put($this->getStream('test data')));
851
+
852
+        // afterMethod unlocks
853
+        $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
854
+
855
+        $this->assertTrue($wasLockedPre, 'File was locked during pre-hooks');
856
+        $this->assertTrue($wasLockedPost, 'File was locked during post-hooks');
857
+
858
+        $this->assertFalse(
859
+            $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED),
860
+            'File unlocked after put'
861
+        );
862
+        $this->assertFalse(
863
+            $this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE),
864
+            'File unlocked after put'
865
+        );
866
+    }
867
+
868
+    /**
869
+     * Returns part files in the given path
870
+     *
871
+     * @param \OC\Files\View view which root is the current user's "files" folder
872
+     * @param string $path path for which to list part files
873
+     *
874
+     * @return array list of part files
875
+     */
876
+    private function listPartFiles(?View $userView = null, $path = '') {
877
+        if ($userView === null) {
878
+            $userView = Filesystem::getView();
879
+        }
880
+        $files = [];
881
+        [$storage, $internalPath] = $userView->resolvePath($path);
882
+        if ($storage instanceof Local) {
883
+            $realPath = $storage->getSourcePath($internalPath);
884
+            $dh = opendir($realPath);
885
+            while (($file = readdir($dh)) !== false) {
886
+                if (str_ends_with($file, '.part')) {
887
+                    $files[] = $file;
888
+                }
889
+            }
890
+            closedir($dh);
891
+        }
892
+        return $files;
893
+    }
894
+
895
+    /**
896
+     * returns an array of file information filesize, mtime, filetype,  mimetype
897
+     *
898
+     * @param string $path
899
+     * @param View $userView
900
+     * @return array
901
+     */
902
+    private function getFileInfos($path = '', ?View $userView = null) {
903
+        if ($userView === null) {
904
+            $userView = Filesystem::getView();
905
+        }
906
+        return [
907
+            'filesize' => $userView->filesize($path),
908
+            'mtime' => $userView->filemtime($path),
909
+            'filetype' => $userView->filetype($path),
910
+            'mimetype' => $userView->getMimeType($path)
911
+        ];
912
+    }
913
+
914
+
915
+    public function testGetFopenFails(): void {
916
+        $this->expectException(\Sabre\DAV\Exception\ServiceUnavailable::class);
917
+
918
+        /** @var View&MockObject */
919
+        $view = $this->getMockBuilder(View::class)
920
+            ->onlyMethods(['fopen'])
921
+            ->getMock();
922
+        $view->expects($this->atLeastOnce())
923
+            ->method('fopen')
924
+            ->willReturn(false);
925
+
926
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
927
+            'permissions' => Constants::PERMISSION_ALL,
928
+            'type' => FileInfo::TYPE_FILE,
929
+        ], null);
930
+
931
+        $file = new File($view, $info);
932
+
933
+        $file->get();
934
+    }
935
+
936
+
937
+    public function testGetFopenThrows(): void {
938
+        $this->expectException(Forbidden::class);
939
+
940
+        /** @var View&MockObject */
941
+        $view = $this->getMockBuilder(View::class)
942
+            ->onlyMethods(['fopen'])
943
+            ->getMock();
944
+        $view->expects($this->atLeastOnce())
945
+            ->method('fopen')
946
+            ->willThrowException(new ForbiddenException('', true));
947
+
948
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
949
+            'permissions' => Constants::PERMISSION_ALL,
950
+            'type' => FileInfo::TYPE_FILE,
951
+        ], null);
952
+
953
+        $file = new File($view, $info);
954
+
955
+        $file->get();
956
+    }
957
+
958
+
959
+    public function testGetThrowsIfNoPermission(): void {
960
+        $this->expectException(\Sabre\DAV\Exception\NotFound::class);
961
+
962
+        /** @var View&MockObject */
963
+        $view = $this->getMockBuilder(View::class)
964
+            ->onlyMethods(['fopen'])
965
+            ->getMock();
966
+        $view->expects($this->never())
967
+            ->method('fopen');
968
+
969
+        $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
970
+            'permissions' => Constants::PERMISSION_CREATE, // no read perm
971
+            'type' => FileInfo::TYPE_FOLDER,
972
+        ], null);
973
+
974
+        $file = new  File($view, $info);
975
+
976
+        $file->get();
977
+    }
978
+
979
+    public function testSimplePutNoCreatePermissions(): void {
980
+        $this->logout();
981
+
982
+        $storage = new Temporary([]);
983
+        $storage->file_put_contents('file.txt', 'old content');
984
+        $noCreateStorage = new PermissionsMask([
985
+            'storage' => $storage,
986
+            'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE
987
+        ]);
988
+
989
+        $this->registerMount($this->user, $noCreateStorage, '/' . $this->user . '/files/root');
990 990
 
991
-		$this->loginAsUser($this->user);
991
+        $this->loginAsUser($this->user);
992 992
 
993
-		$view = new View('/' . $this->user . '/files');
993
+        $view = new View('/' . $this->user . '/files');
994 994
 
995
-		$info = $view->getFileInfo('root/file.txt');
995
+        $info = $view->getFileInfo('root/file.txt');
996 996
 
997
-		$file = new File($view, $info);
997
+        $file = new File($view, $info);
998 998
 
999
-		// beforeMethod locks
1000
-		$view->lockFile('root/file.txt', ILockingProvider::LOCK_SHARED);
999
+        // beforeMethod locks
1000
+        $view->lockFile('root/file.txt', ILockingProvider::LOCK_SHARED);
1001 1001
 
1002
-		$file->put($this->getStream('new content'));
1002
+        $file->put($this->getStream('new content'));
1003 1003
 
1004
-		// afterMethod unlocks
1005
-		$view->unlockFile('root/file.txt', ILockingProvider::LOCK_SHARED);
1004
+        // afterMethod unlocks
1005
+        $view->unlockFile('root/file.txt', ILockingProvider::LOCK_SHARED);
1006 1006
 
1007
-		$this->assertEquals('new content', $view->file_get_contents('root/file.txt'));
1008
-	}
1007
+        $this->assertEquals('new content', $view->file_get_contents('root/file.txt'));
1008
+    }
1009 1009
 
1010
-	public function testPutLockExpired(): void {
1011
-		$view = new View('/' . $this->user . '/files/');
1010
+    public function testPutLockExpired(): void {
1011
+        $view = new View('/' . $this->user . '/files/');
1012 1012
 
1013
-		$path = 'test-locking.txt';
1014
-		$info = new \OC\Files\FileInfo(
1015
-			'/' . $this->user . '/files/' . $path,
1016
-			$this->getMockStorage(),
1017
-			null,
1018
-			[
1019
-				'permissions' => Constants::PERMISSION_ALL,
1020
-				'type' => FileInfo::TYPE_FOLDER,
1021
-			],
1022
-			null
1023
-		);
1013
+        $path = 'test-locking.txt';
1014
+        $info = new \OC\Files\FileInfo(
1015
+            '/' . $this->user . '/files/' . $path,
1016
+            $this->getMockStorage(),
1017
+            null,
1018
+            [
1019
+                'permissions' => Constants::PERMISSION_ALL,
1020
+                'type' => FileInfo::TYPE_FOLDER,
1021
+            ],
1022
+            null
1023
+        );
1024 1024
 
1025
-		$file = new File($view, $info);
1025
+        $file = new File($view, $info);
1026 1026
 
1027
-		// don't lock before the PUT to simulate an expired shared lock
1028
-		$this->assertNotEmpty($file->put($this->getStream('test data')));
1027
+        // don't lock before the PUT to simulate an expired shared lock
1028
+        $this->assertNotEmpty($file->put($this->getStream('test data')));
1029 1029
 
1030
-		// afterMethod unlocks
1031
-		$view->unlockFile($path, ILockingProvider::LOCK_SHARED);
1032
-	}
1030
+        // afterMethod unlocks
1031
+        $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
1032
+    }
1033 1033
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
 			->onlyMethods(['writeStream'])
160 160
 			->setConstructorArgs([['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]])
161 161
 			->getMock();
162
-		Filesystem::mount($storage, [], $this->user . '/');
162
+		Filesystem::mount($storage, [], $this->user.'/');
163 163
 		/** @var View&MockObject $view */
164 164
 		$view = $this->getMockBuilder(View::class)
165 165
 			->onlyMethods(['getRelativePath', 'resolvePath'])
@@ -167,7 +167,7 @@  discard block
 block discarded – undo
167 167
 		$view->expects($this->atLeastOnce())
168 168
 			->method('resolvePath')
169 169
 			->willReturnCallback(
170
-				function ($path) use ($storage) {
170
+				function($path) use ($storage) {
171 171
 					return [$storage, $path];
172 172
 				}
173 173
 			);
@@ -223,11 +223,11 @@  discard block
 block discarded – undo
223 223
 		if (!is_null($viewRoot)) {
224 224
 			$view = new View($viewRoot);
225 225
 		} else {
226
-			$viewRoot = '/' . $this->user . '/files';
226
+			$viewRoot = '/'.$this->user.'/files';
227 227
 		}
228 228
 
229 229
 		$info = new \OC\Files\FileInfo(
230
-			$viewRoot . '/' . ltrim($path, '/'),
230
+			$viewRoot.'/'.ltrim($path, '/'),
231 231
 			$this->getMockStorage(),
232 232
 			null,
233 233
 			[
@@ -321,7 +321,7 @@  discard block
 block discarded – undo
321 321
 	public function testPutSingleFileLegalMtime(mixed $requestMtime, ?int $resultMtime): void {
322 322
 		$request = new Request([
323 323
 			'server' => [
324
-				'HTTP_X_OC_MTIME' => (string)$requestMtime,
324
+				'HTTP_X_OC_MTIME' => (string) $requestMtime,
325 325
 			]
326 326
 		], $this->requestId, $this->config, null);
327 327
 		$file = 'foo.txt';
@@ -414,7 +414,7 @@  discard block
 block discarded – undo
414 414
 		HookHelper::setUpHooks();
415 415
 
416 416
 		// happens with public webdav where the view root is the share root
417
-		$this->assertNotEmpty($this->doPut('/foo.txt', '/' . $this->user . '/files/noderoot'));
417
+		$this->assertNotEmpty($this->doPut('/foo.txt', '/'.$this->user.'/files/noderoot'));
418 418
 
419 419
 		$this->assertCount(4, HookHelper::$hookCalls);
420 420
 		$this->assertHookCall(
@@ -526,12 +526,12 @@  discard block
 block discarded – undo
526 526
 	 * Test exception during final rename in simple upload mode
527 527
 	 */
528 528
 	public function testSimplePutFailsMoveFromStorage(): void {
529
-		$view = new View('/' . $this->user . '/files');
529
+		$view = new View('/'.$this->user.'/files');
530 530
 
531 531
 		// simulate situation where the target file is locked
532 532
 		$view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE);
533 533
 
534
-		$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, [
534
+		$info = new \OC\Files\FileInfo('/'.$this->user.'/files/test.txt', $this->getMockStorage(), null, [
535 535
 			'permissions' => Constants::PERMISSION_ALL,
536 536
 			'type' => FileInfo::TYPE_FOLDER,
537 537
 		], null);
@@ -781,11 +781,11 @@  discard block
 block discarded – undo
781 781
 	 * Test whether locks are set before and after the operation
782 782
 	 */
783 783
 	public function testPutLocking(): void {
784
-		$view = new View('/' . $this->user . '/files/');
784
+		$view = new View('/'.$this->user.'/files/');
785 785
 
786 786
 		$path = 'test-locking.txt';
787 787
 		$info = new \OC\Files\FileInfo(
788
-			'/' . $this->user . '/files/' . $path,
788
+			'/'.$this->user.'/files/'.$path,
789 789
 			$this->getMockStorage(),
790 790
 			null,
791 791
 			[
@@ -817,7 +817,7 @@  discard block
 block discarded – undo
817 817
 		$eventHandler->expects($this->once())
818 818
 			->method('writeCallback')
819 819
 			->willReturnCallback(
820
-				function () use ($view, $path, &$wasLockedPre): void {
820
+				function() use ($view, $path, &$wasLockedPre): void {
821 821
 					$wasLockedPre = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED);
822 822
 					$wasLockedPre = $wasLockedPre && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE);
823 823
 				}
@@ -825,7 +825,7 @@  discard block
 block discarded – undo
825 825
 		$eventHandler->expects($this->once())
826 826
 			->method('postWriteCallback')
827 827
 			->willReturnCallback(
828
-				function () use ($view, $path, &$wasLockedPost): void {
828
+				function() use ($view, $path, &$wasLockedPost): void {
829 829
 					$wasLockedPost = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED);
830 830
 					$wasLockedPost = $wasLockedPost && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE);
831 831
 				}
@@ -986,11 +986,11 @@  discard block
 block discarded – undo
986 986
 			'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE
987 987
 		]);
988 988
 
989
-		$this->registerMount($this->user, $noCreateStorage, '/' . $this->user . '/files/root');
989
+		$this->registerMount($this->user, $noCreateStorage, '/'.$this->user.'/files/root');
990 990
 
991 991
 		$this->loginAsUser($this->user);
992 992
 
993
-		$view = new View('/' . $this->user . '/files');
993
+		$view = new View('/'.$this->user.'/files');
994 994
 
995 995
 		$info = $view->getFileInfo('root/file.txt');
996 996
 
@@ -1008,11 +1008,11 @@  discard block
 block discarded – undo
1008 1008
 	}
1009 1009
 
1010 1010
 	public function testPutLockExpired(): void {
1011
-		$view = new View('/' . $this->user . '/files/');
1011
+		$view = new View('/'.$this->user.'/files/');
1012 1012
 
1013 1013
 		$path = 'test-locking.txt';
1014 1014
 		$info = new \OC\Files\FileInfo(
1015
-			'/' . $this->user . '/files/' . $path,
1015
+			'/'.$this->user.'/files/'.$path,
1016 1016
 			$this->getMockStorage(),
1017 1017
 			null,
1018 1018
 			[
Please login to merge, or discard this patch.
apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php 2 patches
Indentation   +814 added lines, -814 removed lines patch added patch discarded remove patch
@@ -38,818 +38,818 @@
 block discarded – undo
38 38
 
39 39
 class FilesReportPluginTest extends \Test\TestCase {
40 40
 
41
-	private \Sabre\DAV\Server&MockObject $server;
42
-	private Tree&MockObject $tree;
43
-	private ISystemTagObjectMapper&MockObject $tagMapper;
44
-	private ISystemTagManager&MockObject $tagManager;
45
-	private ITags&MockObject $privateTags;
46
-	private ITagManager&MockObject $privateTagManager;
47
-	private IUserSession&MockObject $userSession;
48
-	private FilesReportPluginImplementation $plugin;
49
-	private View&MockObject $view;
50
-	private IGroupManager&MockObject $groupManager;
51
-	private Folder&MockObject $userFolder;
52
-	private IPreview&MockObject $previewManager;
53
-	private IAppManager&MockObject $appManager;
54
-
55
-	protected function setUp(): void {
56
-		parent::setUp();
57
-
58
-		$this->tree = $this->createMock(Tree::class);
59
-		$this->view = $this->createMock(View::class);
60
-
61
-		$this->server = $this->getMockBuilder(Server::class)
62
-			->setConstructorArgs([$this->tree])
63
-			->onlyMethods(['getRequestUri', 'getBaseUri'])
64
-			->getMock();
65
-
66
-		$this->server->expects($this->any())
67
-			->method('getBaseUri')
68
-			->willReturn('http://example.com/owncloud/remote.php/dav');
69
-
70
-		$this->groupManager = $this->createMock(IGroupManager::class);
71
-		$this->userFolder = $this->createMock(Folder::class);
72
-		$this->previewManager = $this->createMock(IPreview::class);
73
-		$this->appManager = $this->createMock(IAppManager::class);
74
-		$this->tagManager = $this->createMock(ISystemTagManager::class);
75
-		$this->tagMapper = $this->createMock(ISystemTagObjectMapper::class);
76
-		$this->userSession = $this->createMock(IUserSession::class);
77
-		$this->privateTags = $this->createMock(ITags::class);
78
-		$this->privateTagManager = $this->createMock(ITagManager::class);
79
-		$this->privateTagManager->expects($this->any())
80
-			->method('load')
81
-			->with('files')
82
-			->willReturn($this->privateTags);
83
-
84
-		$user = $this->createMock(IUser::class);
85
-		$user->expects($this->any())
86
-			->method('getUID')
87
-			->willReturn('testuser');
88
-		$this->userSession->expects($this->any())
89
-			->method('getUser')
90
-			->willReturn($user);
91
-
92
-		$this->plugin = new FilesReportPluginImplementation(
93
-			$this->tree,
94
-			$this->view,
95
-			$this->tagManager,
96
-			$this->tagMapper,
97
-			$this->privateTagManager,
98
-			$this->userSession,
99
-			$this->groupManager,
100
-			$this->userFolder,
101
-			$this->appManager
102
-		);
103
-	}
104
-
105
-	public function testOnReportInvalidNode(): void {
106
-		$path = 'totally/unrelated/13';
107
-
108
-		$this->tree->expects($this->any())
109
-			->method('getNodeForPath')
110
-			->with('/' . $path)
111
-			->willReturn($this->createMock(INode::class));
112
-
113
-		$this->server->expects($this->any())
114
-			->method('getRequestUri')
115
-			->willReturn($path);
116
-		$this->plugin->initialize($this->server);
117
-
118
-		$this->assertNull($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path));
119
-	}
120
-
121
-	public function testOnReportInvalidReportName(): void {
122
-		$path = 'test';
123
-
124
-		$this->tree->expects($this->any())
125
-			->method('getNodeForPath')
126
-			->with('/' . $path)
127
-			->willReturn(
128
-				$this->getMockBuilder(INode::class)
129
-					->disableOriginalConstructor()
130
-					->getMock()
131
-			);
132
-
133
-		$this->server->expects($this->any())
134
-			->method('getRequestUri')
135
-			->willReturn($path);
136
-		$this->plugin->initialize($this->server);
137
-
138
-		$this->assertNull($this->plugin->onReport('{whoever}whatever', [], '/' . $path));
139
-	}
140
-
141
-	public function testOnReport(): void {
142
-		$path = 'test';
143
-
144
-		$parameters = [
145
-			[
146
-				'name' => '{DAV:}prop',
147
-				'value' => [
148
-					['name' => '{DAV:}getcontentlength', 'value' => ''],
149
-					['name' => '{http://owncloud.org/ns}size', 'value' => ''],
150
-				],
151
-			],
152
-			[
153
-				'name' => '{http://owncloud.org/ns}filter-rules',
154
-				'value' => [
155
-					['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
156
-					['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
157
-				],
158
-			],
159
-		];
160
-
161
-		$this->groupManager->expects($this->any())
162
-			->method('isAdmin')
163
-			->willReturn(true);
164
-
165
-		$reportTargetNode = $this->createMock(Directory::class);
166
-		$reportTargetNode->expects($this->any())
167
-			->method('getPath')
168
-			->willReturn('');
169
-
170
-		$response = $this->createMock(ResponseInterface::class);
171
-
172
-		$response->expects($this->once())
173
-			->method('setHeader')
174
-			->with('Content-Type', 'application/xml; charset=utf-8');
175
-
176
-		$response->expects($this->once())
177
-			->method('setStatus')
178
-			->with(207);
179
-
180
-		$response->expects($this->once())
181
-			->method('setBody');
182
-
183
-		$this->tree->expects($this->any())
184
-			->method('getNodeForPath')
185
-			->with('/' . $path)
186
-			->willReturn($reportTargetNode);
187
-
188
-		$filesNode1 = $this->createMock(File::class);
189
-		$filesNode1->expects($this->any())
190
-			->method('getSize')
191
-			->willReturn(12);
192
-		$filesNode2 = $this->createMock(Folder::class);
193
-		$filesNode2->expects($this->any())
194
-			->method('getSize')
195
-			->willReturn(10);
196
-
197
-		$tag123 = $this->createMock(ISystemTag::class);
198
-		$tag123->expects($this->any())
199
-			->method('getName')
200
-			->willReturn('OneTwoThree');
201
-		$tag123->expects($this->any())
202
-			->method('isUserVisible')
203
-			->willReturn(true);
204
-		$tag456 = $this->createMock(ISystemTag::class);
205
-		$tag456->expects($this->any())
206
-			->method('getName')
207
-			->willReturn('FourFiveSix');
208
-		$tag456->expects($this->any())
209
-			->method('isUserVisible')
210
-			->willReturn(true);
211
-
212
-		$this->tagManager->expects($this->once())
213
-			->method('getTagsByIds')
214
-			->with(['123', '456'])
215
-			->willReturn([$tag123, $tag456]);
216
-
217
-		$this->userFolder->expects($this->exactly(2))
218
-			->method('searchBySystemTag')
219
-			->willReturnMap([
220
-				['OneTwoThree', 'testuser', 0, 0, [$filesNode1]],
221
-				['FourFiveSix', 'testuser', 0, 0, [$filesNode2]],
222
-			]);
223
-
224
-		$this->server->expects($this->any())
225
-			->method('getRequestUri')
226
-			->willReturn($path);
227
-		$this->server->httpResponse = $response;
228
-		$this->plugin->initialize($this->server);
229
-
230
-		$this->assertFalse($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path));
231
-	}
232
-
233
-	public function testFindNodesByFileIdsRoot(): void {
234
-		$filesNode1 = $this->createMock(Folder::class);
235
-		$filesNode1->expects($this->once())
236
-			->method('getName')
237
-			->willReturn('first node');
238
-
239
-		$filesNode2 = $this->createMock(File::class);
240
-		$filesNode2->expects($this->once())
241
-			->method('getName')
242
-			->willReturn('second node');
243
-
244
-		$reportTargetNode = $this->createMock(Directory::class);
245
-		$reportTargetNode->expects($this->any())
246
-			->method('getPath')
247
-			->willReturn('/');
248
-
249
-		$this->userFolder->expects($this->exactly(2))
250
-			->method('getFirstNodeById')
251
-			->willReturnMap([
252
-				[111, $filesNode1],
253
-				[222, $filesNode2],
254
-			]);
255
-
256
-		/** @var Directory&MockObject $reportTargetNode */
257
-		$result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
258
-
259
-		$this->assertCount(2, $result);
260
-		$this->assertInstanceOf(Directory::class, $result[0]);
261
-		$this->assertEquals('first node', $result[0]->getName());
262
-		$this->assertInstanceOf(\OCA\DAV\Connector\Sabre\File::class, $result[1]);
263
-		$this->assertEquals('second node', $result[1]->getName());
264
-	}
265
-
266
-	public function testFindNodesByFileIdsSubDir(): void {
267
-		$filesNode1 = $this->createMock(Folder::class);
268
-		$filesNode1->expects($this->once())
269
-			->method('getName')
270
-			->willReturn('first node');
271
-
272
-		$filesNode2 = $this->createMock(File::class);
273
-		$filesNode2->expects($this->once())
274
-			->method('getName')
275
-			->willReturn('second node');
276
-
277
-		$reportTargetNode = $this->createMock(Directory::class);
278
-		$reportTargetNode->expects($this->any())
279
-			->method('getPath')
280
-			->willReturn('/sub1/sub2');
281
-
282
-
283
-		$subNode = $this->createMock(Folder::class);
284
-
285
-		$this->userFolder->expects($this->once())
286
-			->method('get')
287
-			->with('/sub1/sub2')
288
-			->willReturn($subNode);
289
-
290
-		$subNode->expects($this->exactly(2))
291
-			->method('getFirstNodeById')
292
-			->willReturnMap([
293
-				[111, $filesNode1],
294
-				[222, $filesNode2],
295
-			]);
296
-
297
-		/** @var Directory&MockObject $reportTargetNode */
298
-		$result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
299
-
300
-		$this->assertCount(2, $result);
301
-		$this->assertInstanceOf(Directory::class, $result[0]);
302
-		$this->assertEquals('first node', $result[0]->getName());
303
-		$this->assertInstanceOf(\OCA\DAV\Connector\Sabre\File::class, $result[1]);
304
-		$this->assertEquals('second node', $result[1]->getName());
305
-	}
306
-
307
-	public function testPrepareResponses(): void {
308
-		$requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype'];
309
-
310
-		$fileInfo = $this->createMock(FileInfo::class);
311
-		$fileInfo->method('isReadable')->willReturn(true);
312
-
313
-		$node1 = $this->createMock(Directory::class);
314
-		$node2 = $this->createMock(\OCA\DAV\Connector\Sabre\File::class);
315
-
316
-		$node1->expects($this->once())
317
-			->method('getInternalFileId')
318
-			->willReturn('111');
319
-		$node1->expects($this->any())
320
-			->method('getPath')
321
-			->willReturn('/node1');
322
-		$node1->method('getFileInfo')->willReturn($fileInfo);
323
-		$node2->expects($this->once())
324
-			->method('getInternalFileId')
325
-			->willReturn('222');
326
-		$node2->expects($this->once())
327
-			->method('getSize')
328
-			->willReturn(1024);
329
-		$node2->expects($this->any())
330
-			->method('getPath')
331
-			->willReturn('/sub/node2');
332
-		$node2->method('getFileInfo')->willReturn($fileInfo);
333
-
334
-		$config = $this->createMock(IConfig::class);
335
-		$validator = $this->createMock(IFilenameValidator::class);
336
-		$accountManager = $this->createMock(IAccountManager::class);
337
-
338
-		$this->server->addPlugin(
339
-			new FilesPlugin(
340
-				$this->tree,
341
-				$config,
342
-				$this->createMock(IRequest::class),
343
-				$this->previewManager,
344
-				$this->createMock(IUserSession::class),
345
-				$validator,
346
-				$accountManager,
347
-			)
348
-		);
349
-		$this->plugin->initialize($this->server);
350
-		$responses = $this->plugin->prepareResponses('/files/username', $requestedProps, [$node1, $node2]);
351
-
352
-		$this->assertCount(2, $responses);
353
-
354
-		$this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/node1', $responses[0]->getHref());
355
-		$this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/sub/node2', $responses[1]->getHref());
356
-
357
-		$props1 = $responses[0]->getResponseProperties();
358
-		$this->assertEquals('111', $props1[200]['{http://owncloud.org/ns}fileid']);
359
-		$this->assertNull($props1[404]['{DAV:}getcontentlength']);
360
-		$this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props1[200]['{DAV:}resourcetype']);
361
-		$resourceType1 = $props1[200]['{DAV:}resourcetype']->getValue();
362
-		$this->assertEquals('{DAV:}collection', $resourceType1[0]);
363
-
364
-		$props2 = $responses[1]->getResponseProperties();
365
-		$this->assertEquals('1024', $props2[200]['{DAV:}getcontentlength']);
366
-		$this->assertEquals('222', $props2[200]['{http://owncloud.org/ns}fileid']);
367
-		$this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props2[200]['{DAV:}resourcetype']);
368
-		$this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue());
369
-	}
370
-
371
-	public function testProcessFilterRulesSingle(): void {
372
-		$this->groupManager->expects($this->any())
373
-			->method('isAdmin')
374
-			->willReturn(true);
375
-
376
-		$rules = [
377
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
378
-		];
379
-
380
-		$filesNode1 = $this->createMock(File::class);
381
-		$filesNode1->expects($this->any())
382
-			->method('getSize')
383
-			->willReturn(12);
384
-		$filesNode2 = $this->createMock(Folder::class);
385
-		$filesNode2->expects($this->any())
386
-			->method('getSize')
387
-			->willReturn(10);
388
-
389
-		$tag123 = $this->createMock(ISystemTag::class);
390
-		$tag123->expects($this->any())
391
-			->method('getName')
392
-			->willReturn('OneTwoThree');
393
-		$tag123->expects($this->any())
394
-			->method('isUserVisible')
395
-			->willReturn(true);
396
-
397
-		$this->tagManager->expects($this->once())
398
-			->method('getTagsByIds')
399
-			->with(['123'])
400
-			->willReturn([$tag123]);
401
-
402
-		$this->userFolder->expects($this->once())
403
-			->method('searchBySystemTag')
404
-			->with('OneTwoThree')
405
-			->willReturn([$filesNode1, $filesNode2]);
406
-
407
-		$this->assertEquals([$filesNode1, $filesNode2], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, 0, 0]));
408
-	}
409
-
410
-	public function testProcessFilterRulesAndCondition(): void {
411
-		$this->groupManager->expects($this->any())
412
-			->method('isAdmin')
413
-			->willReturn(true);
414
-
415
-		$filesNode1 = $this->createMock(File::class);
416
-		$filesNode1->expects($this->any())
417
-			->method('getSize')
418
-			->willReturn(12);
419
-		$filesNode1->expects($this->any())
420
-			->method('getId')
421
-			->willReturn(111);
422
-		$filesNode2 = $this->createMock(Folder::class);
423
-		$filesNode2->expects($this->any())
424
-			->method('getSize')
425
-			->willReturn(10);
426
-		$filesNode2->expects($this->any())
427
-			->method('getId')
428
-			->willReturn(222);
429
-		$filesNode3 = $this->createMock(File::class);
430
-		$filesNode3->expects($this->any())
431
-			->method('getSize')
432
-			->willReturn(14);
433
-		$filesNode3->expects($this->any())
434
-			->method('getId')
435
-			->willReturn(333);
436
-
437
-		$tag123 = $this->createMock(ISystemTag::class);
438
-		$tag123->expects($this->any())
439
-			->method('getName')
440
-			->willReturn('OneTwoThree');
441
-		$tag123->expects($this->any())
442
-			->method('isUserVisible')
443
-			->willReturn(true);
444
-		$tag456 = $this->createMock(ISystemTag::class);
445
-		$tag456->expects($this->any())
446
-			->method('getName')
447
-			->willReturn('FourFiveSix');
448
-		$tag456->expects($this->any())
449
-			->method('isUserVisible')
450
-			->willReturn(true);
451
-
452
-		$this->tagManager->expects($this->once())
453
-			->method('getTagsByIds')
454
-			->with(['123', '456'])
455
-			->willReturn([$tag123, $tag456]);
456
-
457
-		$this->userFolder->expects($this->exactly(2))
458
-			->method('searchBySystemTag')
459
-			->willReturnMap([
460
-				['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
461
-				['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]],
462
-			]);
463
-
464
-		$rules = [
465
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
466
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
467
-		];
468
-
469
-		$this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
470
-	}
471
-
472
-	public function testProcessFilterRulesAndConditionWithOneEmptyResult(): void {
473
-		$this->groupManager->expects($this->any())
474
-			->method('isAdmin')
475
-			->willReturn(true);
476
-
477
-		$filesNode1 = $this->createMock(File::class);
478
-		$filesNode1->expects($this->any())
479
-			->method('getSize')
480
-			->willReturn(12);
481
-		$filesNode1->expects($this->any())
482
-			->method('getId')
483
-			->willReturn(111);
484
-		$filesNode2 = $this->createMock(Folder::class);
485
-		$filesNode2->expects($this->any())
486
-			->method('getSize')
487
-			->willReturn(10);
488
-		$filesNode2->expects($this->any())
489
-			->method('getId')
490
-			->willReturn(222);
491
-
492
-		$tag123 = $this->createMock(ISystemTag::class);
493
-		$tag123->expects($this->any())
494
-			->method('getName')
495
-			->willReturn('OneTwoThree');
496
-		$tag123->expects($this->any())
497
-			->method('isUserVisible')
498
-			->willReturn(true);
499
-		$tag456 = $this->createMock(ISystemTag::class);
500
-		$tag456->expects($this->any())
501
-			->method('getName')
502
-			->willReturn('FourFiveSix');
503
-		$tag456->expects($this->any())
504
-			->method('isUserVisible')
505
-			->willReturn(true);
506
-
507
-		$this->tagManager->expects($this->once())
508
-			->method('getTagsByIds')
509
-			->with(['123', '456'])
510
-			->willReturn([$tag123, $tag456]);
511
-
512
-		$this->userFolder->expects($this->exactly(2))
513
-			->method('searchBySystemTag')
514
-			->willReturnMap([
515
-				['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
516
-				['FourFiveSix', 'testuser', 0, 0, []],
517
-			]);
518
-
519
-		$rules = [
520
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
521
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
522
-		];
523
-
524
-		$this->assertEquals([], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]));
525
-	}
526
-
527
-	public function testProcessFilterRulesAndConditionWithFirstEmptyResult(): void {
528
-		$this->groupManager->expects($this->any())
529
-			->method('isAdmin')
530
-			->willReturn(true);
531
-
532
-		$filesNode1 = $this->createMock(File::class);
533
-		$filesNode1->expects($this->any())
534
-			->method('getSize')
535
-			->willReturn(12);
536
-		$filesNode1->expects($this->any())
537
-			->method('getId')
538
-			->willReturn(111);
539
-		$filesNode2 = $this->createMock(Folder::class);
540
-		$filesNode2->expects($this->any())
541
-			->method('getSize')
542
-			->willReturn(10);
543
-		$filesNode2->expects($this->any())
544
-			->method('getId')
545
-			->willReturn(222);
546
-
547
-		$tag123 = $this->createMock(ISystemTag::class);
548
-		$tag123->expects($this->any())
549
-			->method('getName')
550
-			->willReturn('OneTwoThree');
551
-		$tag123->expects($this->any())
552
-			->method('isUserVisible')
553
-			->willReturn(true);
554
-		$tag456 = $this->createMock(ISystemTag::class);
555
-		$tag456->expects($this->any())
556
-			->method('getName')
557
-			->willReturn('FourFiveSix');
558
-		$tag456->expects($this->any())
559
-			->method('isUserVisible')
560
-			->willReturn(true);
561
-
562
-		$this->tagManager->expects($this->once())
563
-			->method('getTagsByIds')
564
-			->with(['123', '456'])
565
-			->willReturn([$tag123, $tag456]);
566
-
567
-		$this->userFolder->expects($this->once())
568
-			->method('searchBySystemTag')
569
-			->willReturnMap([
570
-				['OneTwoThree', 'testuser', 0, 0, []],
571
-			]);
572
-
573
-		$rules = [
574
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
575
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
576
-		];
577
-
578
-		$this->assertEquals([], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]));
579
-	}
580
-
581
-	public function testProcessFilterRulesAndConditionWithEmptyMidResult(): void {
582
-		$this->groupManager->expects($this->any())
583
-			->method('isAdmin')
584
-			->willReturn(true);
585
-
586
-		$filesNode1 = $this->createMock(File::class);
587
-		$filesNode1->expects($this->any())
588
-			->method('getSize')
589
-			->willReturn(12);
590
-		$filesNode1->expects($this->any())
591
-			->method('getId')
592
-			->willReturn(111);
593
-		$filesNode2 = $this->createMock(Folder::class);
594
-		$filesNode2->expects($this->any())
595
-			->method('getSize')
596
-			->willReturn(10);
597
-		$filesNode2->expects($this->any())
598
-			->method('getId')
599
-			->willReturn(222);
600
-		$filesNode3 = $this->createMock(Folder::class);
601
-		$filesNode3->expects($this->any())
602
-			->method('getSize')
603
-			->willReturn(13);
604
-		$filesNode3->expects($this->any())
605
-			->method('getId')
606
-			->willReturn(333);
607
-
608
-		$tag123 = $this->createMock(ISystemTag::class);
609
-		$tag123->expects($this->any())
610
-			->method('getName')
611
-			->willReturn('OneTwoThree');
612
-		$tag123->expects($this->any())
613
-			->method('isUserVisible')
614
-			->willReturn(true);
615
-		$tag456 = $this->createMock(ISystemTag::class);
616
-		$tag456->expects($this->any())
617
-			->method('getName')
618
-			->willReturn('FourFiveSix');
619
-		$tag456->expects($this->any())
620
-			->method('isUserVisible')
621
-			->willReturn(true);
622
-		$tag789 = $this->createMock(ISystemTag::class);
623
-		$tag789->expects($this->any())
624
-			->method('getName')
625
-			->willReturn('SevenEightNine');
626
-		$tag789->expects($this->any())
627
-			->method('isUserVisible')
628
-			->willReturn(true);
629
-
630
-		$this->tagManager->expects($this->once())
631
-			->method('getTagsByIds')
632
-			->with(['123', '456', '789'])
633
-			->willReturn([$tag123, $tag456, $tag789]);
634
-
635
-		$this->userFolder->expects($this->exactly(2))
636
-			->method('searchBySystemTag')
637
-			->willReturnMap([
638
-				['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
639
-				['FourFiveSix', 'testuser', 0, 0, [$filesNode3]],
640
-			]);
641
-
642
-		$rules = [
643
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
644
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
645
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'],
646
-		];
647
-
648
-		$this->assertEquals([], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
649
-	}
650
-
651
-	public function testProcessFilterRulesInvisibleTagAsAdmin(): void {
652
-		$this->groupManager->expects($this->any())
653
-			->method('isAdmin')
654
-			->willReturn(true);
655
-
656
-		$filesNode1 = $this->createMock(File::class);
657
-		$filesNode1->expects($this->any())
658
-			->method('getSize')
659
-			->willReturn(12);
660
-		$filesNode1->expects($this->any())
661
-			->method('getId')
662
-			->willReturn(111);
663
-		$filesNode2 = $this->createMock(Folder::class);
664
-		$filesNode2->expects($this->any())
665
-			->method('getSize')
666
-			->willReturn(10);
667
-		$filesNode2->expects($this->any())
668
-			->method('getId')
669
-			->willReturn(222);
670
-		$filesNode3 = $this->createMock(Folder::class);
671
-		$filesNode3->expects($this->any())
672
-			->method('getSize')
673
-			->willReturn(13);
674
-		$filesNode3->expects($this->any())
675
-			->method('getId')
676
-			->willReturn(333);
677
-
678
-		$tag123 = $this->createMock(ISystemTag::class);
679
-		$tag123->expects($this->any())
680
-			->method('getName')
681
-			->willReturn('OneTwoThree');
682
-		$tag123->expects($this->any())
683
-			->method('isUserVisible')
684
-			->willReturn(true);
685
-		$tag456 = $this->createMock(ISystemTag::class);
686
-		$tag456->expects($this->any())
687
-			->method('getName')
688
-			->willReturn('FourFiveSix');
689
-		$tag456->expects($this->any())
690
-			->method('isUserVisible')
691
-			->willReturn(false);
692
-
693
-		$this->tagManager->expects($this->once())
694
-			->method('getTagsByIds')
695
-			->with(['123', '456'])
696
-			->willReturn([$tag123, $tag456]);
697
-
698
-		$this->userFolder->expects($this->exactly(2))
699
-			->method('searchBySystemTag')
700
-			->willReturnMap([
701
-				['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
702
-				['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]],
703
-			]);
704
-
705
-		$rules = [
706
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
707
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
708
-		];
709
-
710
-		$this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
711
-	}
712
-
713
-
714
-	public function testProcessFilterRulesInvisibleTagAsUser(): void {
715
-		$this->expectException(TagNotFoundException::class);
716
-
717
-		$this->groupManager->expects($this->any())
718
-			->method('isAdmin')
719
-			->willReturn(false);
720
-
721
-		$tag123 = $this->createMock(ISystemTag::class);
722
-		$tag123->expects($this->any())
723
-			->method('getName')
724
-			->willReturn('OneTwoThree');
725
-		$tag123->expects($this->any())
726
-			->method('isUserVisible')
727
-			->willReturn(true);
728
-		$tag456 = $this->createMock(ISystemTag::class);
729
-		$tag456->expects($this->any())
730
-			->method('getName')
731
-			->willReturn('FourFiveSix');
732
-		$tag456->expects($this->any())
733
-			->method('isUserVisible')
734
-			->willReturn(false);
735
-
736
-		$this->tagManager->expects($this->once())
737
-			->method('getTagsByIds')
738
-			->with(['123', '456'])
739
-			->willThrowException(new TagNotFoundException());
740
-
741
-		$this->userFolder->expects($this->never())
742
-			->method('searchBySystemTag');
743
-
744
-		$rules = [
745
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
746
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
747
-		];
748
-
749
-		self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]);
750
-	}
751
-
752
-	public function testProcessFilterRulesVisibleTagAsUser(): void {
753
-		$this->groupManager->expects($this->any())
754
-			->method('isAdmin')
755
-			->willReturn(false);
756
-
757
-		$tag1 = $this->createMock(ISystemTag::class);
758
-		$tag1->expects($this->any())
759
-			->method('getId')
760
-			->willReturn('123');
761
-		$tag1->expects($this->any())
762
-			->method('isUserVisible')
763
-			->willReturn(true);
764
-		$tag1->expects($this->any())
765
-			->method('getName')
766
-			->willReturn('OneTwoThree');
767
-
768
-		$tag2 = $this->createMock(ISystemTag::class);
769
-		$tag2->expects($this->any())
770
-			->method('getId')
771
-			->willReturn('123');
772
-		$tag2->expects($this->any())
773
-			->method('isUserVisible')
774
-			->willReturn(true);
775
-		$tag2->expects($this->any())
776
-			->method('getName')
777
-			->willReturn('FourFiveSix');
778
-
779
-		$this->tagManager->expects($this->once())
780
-			->method('getTagsByIds')
781
-			->with(['123', '456'])
782
-			->willReturn([$tag1, $tag2]);
783
-
784
-		$filesNode1 = $this->createMock(File::class);
785
-		$filesNode1->expects($this->any())
786
-			->method('getId')
787
-			->willReturn(111);
788
-		$filesNode1->expects($this->any())
789
-			->method('getSize')
790
-			->willReturn(12);
791
-		$filesNode2 = $this->createMock(Folder::class);
792
-		$filesNode2->expects($this->any())
793
-			->method('getId')
794
-			->willReturn(222);
795
-		$filesNode2->expects($this->any())
796
-			->method('getSize')
797
-			->willReturn(10);
798
-		$filesNode3 = $this->createMock(Folder::class);
799
-		$filesNode3->expects($this->any())
800
-			->method('getId')
801
-			->willReturn(333);
802
-		$filesNode3->expects($this->any())
803
-			->method('getSize')
804
-			->willReturn(33);
805
-
806
-		$this->tagManager->expects($this->once())
807
-			->method('getTagsByIds')
808
-			->with(['123', '456'])
809
-			->willReturn([$tag1, $tag2]);
810
-
811
-		// main assertion: only user visible tags are being passed through.
812
-		$this->userFolder->expects($this->exactly(2))
813
-			->method('searchBySystemTag')
814
-			->willReturnMap([
815
-				['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
816
-				['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]],
817
-			]);
818
-
819
-		$rules = [
820
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
821
-			['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
822
-		];
823
-
824
-		$this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
825
-	}
826
-
827
-	public function testProcessFavoriteFilter(): void {
828
-		$rules = [
829
-			['name' => '{http://owncloud.org/ns}favorite', 'value' => '1'],
830
-		];
831
-
832
-		$this->privateTags->expects($this->once())
833
-			->method('getFavorites')
834
-			->willReturn(['456', '789']);
835
-
836
-		$this->assertEquals(['456', '789'], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileIDs', [$rules])));
837
-	}
838
-
839
-	public static function filesBaseUriProvider(): array {
840
-		return [
841
-			['', '', ''],
842
-			['files/username', '', '/files/username'],
843
-			['files/username/test', '/test', '/files/username'],
844
-			['files/username/test/sub', '/test/sub', '/files/username'],
845
-			['test', '/test', ''],
846
-		];
847
-	}
848
-
849
-	/**
850
-	 * @dataProvider filesBaseUriProvider
851
-	 */
852
-	public function testFilesBaseUri(string $uri, string $reportPath, string $expectedUri): void {
853
-		$this->assertEquals($expectedUri, self::invokePrivate($this->plugin, 'getFilesBaseUri', [$uri, $reportPath]));
854
-	}
41
+    private \Sabre\DAV\Server&MockObject $server;
42
+    private Tree&MockObject $tree;
43
+    private ISystemTagObjectMapper&MockObject $tagMapper;
44
+    private ISystemTagManager&MockObject $tagManager;
45
+    private ITags&MockObject $privateTags;
46
+    private ITagManager&MockObject $privateTagManager;
47
+    private IUserSession&MockObject $userSession;
48
+    private FilesReportPluginImplementation $plugin;
49
+    private View&MockObject $view;
50
+    private IGroupManager&MockObject $groupManager;
51
+    private Folder&MockObject $userFolder;
52
+    private IPreview&MockObject $previewManager;
53
+    private IAppManager&MockObject $appManager;
54
+
55
+    protected function setUp(): void {
56
+        parent::setUp();
57
+
58
+        $this->tree = $this->createMock(Tree::class);
59
+        $this->view = $this->createMock(View::class);
60
+
61
+        $this->server = $this->getMockBuilder(Server::class)
62
+            ->setConstructorArgs([$this->tree])
63
+            ->onlyMethods(['getRequestUri', 'getBaseUri'])
64
+            ->getMock();
65
+
66
+        $this->server->expects($this->any())
67
+            ->method('getBaseUri')
68
+            ->willReturn('http://example.com/owncloud/remote.php/dav');
69
+
70
+        $this->groupManager = $this->createMock(IGroupManager::class);
71
+        $this->userFolder = $this->createMock(Folder::class);
72
+        $this->previewManager = $this->createMock(IPreview::class);
73
+        $this->appManager = $this->createMock(IAppManager::class);
74
+        $this->tagManager = $this->createMock(ISystemTagManager::class);
75
+        $this->tagMapper = $this->createMock(ISystemTagObjectMapper::class);
76
+        $this->userSession = $this->createMock(IUserSession::class);
77
+        $this->privateTags = $this->createMock(ITags::class);
78
+        $this->privateTagManager = $this->createMock(ITagManager::class);
79
+        $this->privateTagManager->expects($this->any())
80
+            ->method('load')
81
+            ->with('files')
82
+            ->willReturn($this->privateTags);
83
+
84
+        $user = $this->createMock(IUser::class);
85
+        $user->expects($this->any())
86
+            ->method('getUID')
87
+            ->willReturn('testuser');
88
+        $this->userSession->expects($this->any())
89
+            ->method('getUser')
90
+            ->willReturn($user);
91
+
92
+        $this->plugin = new FilesReportPluginImplementation(
93
+            $this->tree,
94
+            $this->view,
95
+            $this->tagManager,
96
+            $this->tagMapper,
97
+            $this->privateTagManager,
98
+            $this->userSession,
99
+            $this->groupManager,
100
+            $this->userFolder,
101
+            $this->appManager
102
+        );
103
+    }
104
+
105
+    public function testOnReportInvalidNode(): void {
106
+        $path = 'totally/unrelated/13';
107
+
108
+        $this->tree->expects($this->any())
109
+            ->method('getNodeForPath')
110
+            ->with('/' . $path)
111
+            ->willReturn($this->createMock(INode::class));
112
+
113
+        $this->server->expects($this->any())
114
+            ->method('getRequestUri')
115
+            ->willReturn($path);
116
+        $this->plugin->initialize($this->server);
117
+
118
+        $this->assertNull($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path));
119
+    }
120
+
121
+    public function testOnReportInvalidReportName(): void {
122
+        $path = 'test';
123
+
124
+        $this->tree->expects($this->any())
125
+            ->method('getNodeForPath')
126
+            ->with('/' . $path)
127
+            ->willReturn(
128
+                $this->getMockBuilder(INode::class)
129
+                    ->disableOriginalConstructor()
130
+                    ->getMock()
131
+            );
132
+
133
+        $this->server->expects($this->any())
134
+            ->method('getRequestUri')
135
+            ->willReturn($path);
136
+        $this->plugin->initialize($this->server);
137
+
138
+        $this->assertNull($this->plugin->onReport('{whoever}whatever', [], '/' . $path));
139
+    }
140
+
141
+    public function testOnReport(): void {
142
+        $path = 'test';
143
+
144
+        $parameters = [
145
+            [
146
+                'name' => '{DAV:}prop',
147
+                'value' => [
148
+                    ['name' => '{DAV:}getcontentlength', 'value' => ''],
149
+                    ['name' => '{http://owncloud.org/ns}size', 'value' => ''],
150
+                ],
151
+            ],
152
+            [
153
+                'name' => '{http://owncloud.org/ns}filter-rules',
154
+                'value' => [
155
+                    ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
156
+                    ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
157
+                ],
158
+            ],
159
+        ];
160
+
161
+        $this->groupManager->expects($this->any())
162
+            ->method('isAdmin')
163
+            ->willReturn(true);
164
+
165
+        $reportTargetNode = $this->createMock(Directory::class);
166
+        $reportTargetNode->expects($this->any())
167
+            ->method('getPath')
168
+            ->willReturn('');
169
+
170
+        $response = $this->createMock(ResponseInterface::class);
171
+
172
+        $response->expects($this->once())
173
+            ->method('setHeader')
174
+            ->with('Content-Type', 'application/xml; charset=utf-8');
175
+
176
+        $response->expects($this->once())
177
+            ->method('setStatus')
178
+            ->with(207);
179
+
180
+        $response->expects($this->once())
181
+            ->method('setBody');
182
+
183
+        $this->tree->expects($this->any())
184
+            ->method('getNodeForPath')
185
+            ->with('/' . $path)
186
+            ->willReturn($reportTargetNode);
187
+
188
+        $filesNode1 = $this->createMock(File::class);
189
+        $filesNode1->expects($this->any())
190
+            ->method('getSize')
191
+            ->willReturn(12);
192
+        $filesNode2 = $this->createMock(Folder::class);
193
+        $filesNode2->expects($this->any())
194
+            ->method('getSize')
195
+            ->willReturn(10);
196
+
197
+        $tag123 = $this->createMock(ISystemTag::class);
198
+        $tag123->expects($this->any())
199
+            ->method('getName')
200
+            ->willReturn('OneTwoThree');
201
+        $tag123->expects($this->any())
202
+            ->method('isUserVisible')
203
+            ->willReturn(true);
204
+        $tag456 = $this->createMock(ISystemTag::class);
205
+        $tag456->expects($this->any())
206
+            ->method('getName')
207
+            ->willReturn('FourFiveSix');
208
+        $tag456->expects($this->any())
209
+            ->method('isUserVisible')
210
+            ->willReturn(true);
211
+
212
+        $this->tagManager->expects($this->once())
213
+            ->method('getTagsByIds')
214
+            ->with(['123', '456'])
215
+            ->willReturn([$tag123, $tag456]);
216
+
217
+        $this->userFolder->expects($this->exactly(2))
218
+            ->method('searchBySystemTag')
219
+            ->willReturnMap([
220
+                ['OneTwoThree', 'testuser', 0, 0, [$filesNode1]],
221
+                ['FourFiveSix', 'testuser', 0, 0, [$filesNode2]],
222
+            ]);
223
+
224
+        $this->server->expects($this->any())
225
+            ->method('getRequestUri')
226
+            ->willReturn($path);
227
+        $this->server->httpResponse = $response;
228
+        $this->plugin->initialize($this->server);
229
+
230
+        $this->assertFalse($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path));
231
+    }
232
+
233
+    public function testFindNodesByFileIdsRoot(): void {
234
+        $filesNode1 = $this->createMock(Folder::class);
235
+        $filesNode1->expects($this->once())
236
+            ->method('getName')
237
+            ->willReturn('first node');
238
+
239
+        $filesNode2 = $this->createMock(File::class);
240
+        $filesNode2->expects($this->once())
241
+            ->method('getName')
242
+            ->willReturn('second node');
243
+
244
+        $reportTargetNode = $this->createMock(Directory::class);
245
+        $reportTargetNode->expects($this->any())
246
+            ->method('getPath')
247
+            ->willReturn('/');
248
+
249
+        $this->userFolder->expects($this->exactly(2))
250
+            ->method('getFirstNodeById')
251
+            ->willReturnMap([
252
+                [111, $filesNode1],
253
+                [222, $filesNode2],
254
+            ]);
255
+
256
+        /** @var Directory&MockObject $reportTargetNode */
257
+        $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
258
+
259
+        $this->assertCount(2, $result);
260
+        $this->assertInstanceOf(Directory::class, $result[0]);
261
+        $this->assertEquals('first node', $result[0]->getName());
262
+        $this->assertInstanceOf(\OCA\DAV\Connector\Sabre\File::class, $result[1]);
263
+        $this->assertEquals('second node', $result[1]->getName());
264
+    }
265
+
266
+    public function testFindNodesByFileIdsSubDir(): void {
267
+        $filesNode1 = $this->createMock(Folder::class);
268
+        $filesNode1->expects($this->once())
269
+            ->method('getName')
270
+            ->willReturn('first node');
271
+
272
+        $filesNode2 = $this->createMock(File::class);
273
+        $filesNode2->expects($this->once())
274
+            ->method('getName')
275
+            ->willReturn('second node');
276
+
277
+        $reportTargetNode = $this->createMock(Directory::class);
278
+        $reportTargetNode->expects($this->any())
279
+            ->method('getPath')
280
+            ->willReturn('/sub1/sub2');
281
+
282
+
283
+        $subNode = $this->createMock(Folder::class);
284
+
285
+        $this->userFolder->expects($this->once())
286
+            ->method('get')
287
+            ->with('/sub1/sub2')
288
+            ->willReturn($subNode);
289
+
290
+        $subNode->expects($this->exactly(2))
291
+            ->method('getFirstNodeById')
292
+            ->willReturnMap([
293
+                [111, $filesNode1],
294
+                [222, $filesNode2],
295
+            ]);
296
+
297
+        /** @var Directory&MockObject $reportTargetNode */
298
+        $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']);
299
+
300
+        $this->assertCount(2, $result);
301
+        $this->assertInstanceOf(Directory::class, $result[0]);
302
+        $this->assertEquals('first node', $result[0]->getName());
303
+        $this->assertInstanceOf(\OCA\DAV\Connector\Sabre\File::class, $result[1]);
304
+        $this->assertEquals('second node', $result[1]->getName());
305
+    }
306
+
307
+    public function testPrepareResponses(): void {
308
+        $requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype'];
309
+
310
+        $fileInfo = $this->createMock(FileInfo::class);
311
+        $fileInfo->method('isReadable')->willReturn(true);
312
+
313
+        $node1 = $this->createMock(Directory::class);
314
+        $node2 = $this->createMock(\OCA\DAV\Connector\Sabre\File::class);
315
+
316
+        $node1->expects($this->once())
317
+            ->method('getInternalFileId')
318
+            ->willReturn('111');
319
+        $node1->expects($this->any())
320
+            ->method('getPath')
321
+            ->willReturn('/node1');
322
+        $node1->method('getFileInfo')->willReturn($fileInfo);
323
+        $node2->expects($this->once())
324
+            ->method('getInternalFileId')
325
+            ->willReturn('222');
326
+        $node2->expects($this->once())
327
+            ->method('getSize')
328
+            ->willReturn(1024);
329
+        $node2->expects($this->any())
330
+            ->method('getPath')
331
+            ->willReturn('/sub/node2');
332
+        $node2->method('getFileInfo')->willReturn($fileInfo);
333
+
334
+        $config = $this->createMock(IConfig::class);
335
+        $validator = $this->createMock(IFilenameValidator::class);
336
+        $accountManager = $this->createMock(IAccountManager::class);
337
+
338
+        $this->server->addPlugin(
339
+            new FilesPlugin(
340
+                $this->tree,
341
+                $config,
342
+                $this->createMock(IRequest::class),
343
+                $this->previewManager,
344
+                $this->createMock(IUserSession::class),
345
+                $validator,
346
+                $accountManager,
347
+            )
348
+        );
349
+        $this->plugin->initialize($this->server);
350
+        $responses = $this->plugin->prepareResponses('/files/username', $requestedProps, [$node1, $node2]);
351
+
352
+        $this->assertCount(2, $responses);
353
+
354
+        $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/node1', $responses[0]->getHref());
355
+        $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/sub/node2', $responses[1]->getHref());
356
+
357
+        $props1 = $responses[0]->getResponseProperties();
358
+        $this->assertEquals('111', $props1[200]['{http://owncloud.org/ns}fileid']);
359
+        $this->assertNull($props1[404]['{DAV:}getcontentlength']);
360
+        $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props1[200]['{DAV:}resourcetype']);
361
+        $resourceType1 = $props1[200]['{DAV:}resourcetype']->getValue();
362
+        $this->assertEquals('{DAV:}collection', $resourceType1[0]);
363
+
364
+        $props2 = $responses[1]->getResponseProperties();
365
+        $this->assertEquals('1024', $props2[200]['{DAV:}getcontentlength']);
366
+        $this->assertEquals('222', $props2[200]['{http://owncloud.org/ns}fileid']);
367
+        $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props2[200]['{DAV:}resourcetype']);
368
+        $this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue());
369
+    }
370
+
371
+    public function testProcessFilterRulesSingle(): void {
372
+        $this->groupManager->expects($this->any())
373
+            ->method('isAdmin')
374
+            ->willReturn(true);
375
+
376
+        $rules = [
377
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
378
+        ];
379
+
380
+        $filesNode1 = $this->createMock(File::class);
381
+        $filesNode1->expects($this->any())
382
+            ->method('getSize')
383
+            ->willReturn(12);
384
+        $filesNode2 = $this->createMock(Folder::class);
385
+        $filesNode2->expects($this->any())
386
+            ->method('getSize')
387
+            ->willReturn(10);
388
+
389
+        $tag123 = $this->createMock(ISystemTag::class);
390
+        $tag123->expects($this->any())
391
+            ->method('getName')
392
+            ->willReturn('OneTwoThree');
393
+        $tag123->expects($this->any())
394
+            ->method('isUserVisible')
395
+            ->willReturn(true);
396
+
397
+        $this->tagManager->expects($this->once())
398
+            ->method('getTagsByIds')
399
+            ->with(['123'])
400
+            ->willReturn([$tag123]);
401
+
402
+        $this->userFolder->expects($this->once())
403
+            ->method('searchBySystemTag')
404
+            ->with('OneTwoThree')
405
+            ->willReturn([$filesNode1, $filesNode2]);
406
+
407
+        $this->assertEquals([$filesNode1, $filesNode2], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, 0, 0]));
408
+    }
409
+
410
+    public function testProcessFilterRulesAndCondition(): void {
411
+        $this->groupManager->expects($this->any())
412
+            ->method('isAdmin')
413
+            ->willReturn(true);
414
+
415
+        $filesNode1 = $this->createMock(File::class);
416
+        $filesNode1->expects($this->any())
417
+            ->method('getSize')
418
+            ->willReturn(12);
419
+        $filesNode1->expects($this->any())
420
+            ->method('getId')
421
+            ->willReturn(111);
422
+        $filesNode2 = $this->createMock(Folder::class);
423
+        $filesNode2->expects($this->any())
424
+            ->method('getSize')
425
+            ->willReturn(10);
426
+        $filesNode2->expects($this->any())
427
+            ->method('getId')
428
+            ->willReturn(222);
429
+        $filesNode3 = $this->createMock(File::class);
430
+        $filesNode3->expects($this->any())
431
+            ->method('getSize')
432
+            ->willReturn(14);
433
+        $filesNode3->expects($this->any())
434
+            ->method('getId')
435
+            ->willReturn(333);
436
+
437
+        $tag123 = $this->createMock(ISystemTag::class);
438
+        $tag123->expects($this->any())
439
+            ->method('getName')
440
+            ->willReturn('OneTwoThree');
441
+        $tag123->expects($this->any())
442
+            ->method('isUserVisible')
443
+            ->willReturn(true);
444
+        $tag456 = $this->createMock(ISystemTag::class);
445
+        $tag456->expects($this->any())
446
+            ->method('getName')
447
+            ->willReturn('FourFiveSix');
448
+        $tag456->expects($this->any())
449
+            ->method('isUserVisible')
450
+            ->willReturn(true);
451
+
452
+        $this->tagManager->expects($this->once())
453
+            ->method('getTagsByIds')
454
+            ->with(['123', '456'])
455
+            ->willReturn([$tag123, $tag456]);
456
+
457
+        $this->userFolder->expects($this->exactly(2))
458
+            ->method('searchBySystemTag')
459
+            ->willReturnMap([
460
+                ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
461
+                ['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]],
462
+            ]);
463
+
464
+        $rules = [
465
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
466
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
467
+        ];
468
+
469
+        $this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
470
+    }
471
+
472
+    public function testProcessFilterRulesAndConditionWithOneEmptyResult(): void {
473
+        $this->groupManager->expects($this->any())
474
+            ->method('isAdmin')
475
+            ->willReturn(true);
476
+
477
+        $filesNode1 = $this->createMock(File::class);
478
+        $filesNode1->expects($this->any())
479
+            ->method('getSize')
480
+            ->willReturn(12);
481
+        $filesNode1->expects($this->any())
482
+            ->method('getId')
483
+            ->willReturn(111);
484
+        $filesNode2 = $this->createMock(Folder::class);
485
+        $filesNode2->expects($this->any())
486
+            ->method('getSize')
487
+            ->willReturn(10);
488
+        $filesNode2->expects($this->any())
489
+            ->method('getId')
490
+            ->willReturn(222);
491
+
492
+        $tag123 = $this->createMock(ISystemTag::class);
493
+        $tag123->expects($this->any())
494
+            ->method('getName')
495
+            ->willReturn('OneTwoThree');
496
+        $tag123->expects($this->any())
497
+            ->method('isUserVisible')
498
+            ->willReturn(true);
499
+        $tag456 = $this->createMock(ISystemTag::class);
500
+        $tag456->expects($this->any())
501
+            ->method('getName')
502
+            ->willReturn('FourFiveSix');
503
+        $tag456->expects($this->any())
504
+            ->method('isUserVisible')
505
+            ->willReturn(true);
506
+
507
+        $this->tagManager->expects($this->once())
508
+            ->method('getTagsByIds')
509
+            ->with(['123', '456'])
510
+            ->willReturn([$tag123, $tag456]);
511
+
512
+        $this->userFolder->expects($this->exactly(2))
513
+            ->method('searchBySystemTag')
514
+            ->willReturnMap([
515
+                ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
516
+                ['FourFiveSix', 'testuser', 0, 0, []],
517
+            ]);
518
+
519
+        $rules = [
520
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
521
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
522
+        ];
523
+
524
+        $this->assertEquals([], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]));
525
+    }
526
+
527
+    public function testProcessFilterRulesAndConditionWithFirstEmptyResult(): void {
528
+        $this->groupManager->expects($this->any())
529
+            ->method('isAdmin')
530
+            ->willReturn(true);
531
+
532
+        $filesNode1 = $this->createMock(File::class);
533
+        $filesNode1->expects($this->any())
534
+            ->method('getSize')
535
+            ->willReturn(12);
536
+        $filesNode1->expects($this->any())
537
+            ->method('getId')
538
+            ->willReturn(111);
539
+        $filesNode2 = $this->createMock(Folder::class);
540
+        $filesNode2->expects($this->any())
541
+            ->method('getSize')
542
+            ->willReturn(10);
543
+        $filesNode2->expects($this->any())
544
+            ->method('getId')
545
+            ->willReturn(222);
546
+
547
+        $tag123 = $this->createMock(ISystemTag::class);
548
+        $tag123->expects($this->any())
549
+            ->method('getName')
550
+            ->willReturn('OneTwoThree');
551
+        $tag123->expects($this->any())
552
+            ->method('isUserVisible')
553
+            ->willReturn(true);
554
+        $tag456 = $this->createMock(ISystemTag::class);
555
+        $tag456->expects($this->any())
556
+            ->method('getName')
557
+            ->willReturn('FourFiveSix');
558
+        $tag456->expects($this->any())
559
+            ->method('isUserVisible')
560
+            ->willReturn(true);
561
+
562
+        $this->tagManager->expects($this->once())
563
+            ->method('getTagsByIds')
564
+            ->with(['123', '456'])
565
+            ->willReturn([$tag123, $tag456]);
566
+
567
+        $this->userFolder->expects($this->once())
568
+            ->method('searchBySystemTag')
569
+            ->willReturnMap([
570
+                ['OneTwoThree', 'testuser', 0, 0, []],
571
+            ]);
572
+
573
+        $rules = [
574
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
575
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
576
+        ];
577
+
578
+        $this->assertEquals([], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]));
579
+    }
580
+
581
+    public function testProcessFilterRulesAndConditionWithEmptyMidResult(): void {
582
+        $this->groupManager->expects($this->any())
583
+            ->method('isAdmin')
584
+            ->willReturn(true);
585
+
586
+        $filesNode1 = $this->createMock(File::class);
587
+        $filesNode1->expects($this->any())
588
+            ->method('getSize')
589
+            ->willReturn(12);
590
+        $filesNode1->expects($this->any())
591
+            ->method('getId')
592
+            ->willReturn(111);
593
+        $filesNode2 = $this->createMock(Folder::class);
594
+        $filesNode2->expects($this->any())
595
+            ->method('getSize')
596
+            ->willReturn(10);
597
+        $filesNode2->expects($this->any())
598
+            ->method('getId')
599
+            ->willReturn(222);
600
+        $filesNode3 = $this->createMock(Folder::class);
601
+        $filesNode3->expects($this->any())
602
+            ->method('getSize')
603
+            ->willReturn(13);
604
+        $filesNode3->expects($this->any())
605
+            ->method('getId')
606
+            ->willReturn(333);
607
+
608
+        $tag123 = $this->createMock(ISystemTag::class);
609
+        $tag123->expects($this->any())
610
+            ->method('getName')
611
+            ->willReturn('OneTwoThree');
612
+        $tag123->expects($this->any())
613
+            ->method('isUserVisible')
614
+            ->willReturn(true);
615
+        $tag456 = $this->createMock(ISystemTag::class);
616
+        $tag456->expects($this->any())
617
+            ->method('getName')
618
+            ->willReturn('FourFiveSix');
619
+        $tag456->expects($this->any())
620
+            ->method('isUserVisible')
621
+            ->willReturn(true);
622
+        $tag789 = $this->createMock(ISystemTag::class);
623
+        $tag789->expects($this->any())
624
+            ->method('getName')
625
+            ->willReturn('SevenEightNine');
626
+        $tag789->expects($this->any())
627
+            ->method('isUserVisible')
628
+            ->willReturn(true);
629
+
630
+        $this->tagManager->expects($this->once())
631
+            ->method('getTagsByIds')
632
+            ->with(['123', '456', '789'])
633
+            ->willReturn([$tag123, $tag456, $tag789]);
634
+
635
+        $this->userFolder->expects($this->exactly(2))
636
+            ->method('searchBySystemTag')
637
+            ->willReturnMap([
638
+                ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
639
+                ['FourFiveSix', 'testuser', 0, 0, [$filesNode3]],
640
+            ]);
641
+
642
+        $rules = [
643
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
644
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
645
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'],
646
+        ];
647
+
648
+        $this->assertEquals([], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
649
+    }
650
+
651
+    public function testProcessFilterRulesInvisibleTagAsAdmin(): void {
652
+        $this->groupManager->expects($this->any())
653
+            ->method('isAdmin')
654
+            ->willReturn(true);
655
+
656
+        $filesNode1 = $this->createMock(File::class);
657
+        $filesNode1->expects($this->any())
658
+            ->method('getSize')
659
+            ->willReturn(12);
660
+        $filesNode1->expects($this->any())
661
+            ->method('getId')
662
+            ->willReturn(111);
663
+        $filesNode2 = $this->createMock(Folder::class);
664
+        $filesNode2->expects($this->any())
665
+            ->method('getSize')
666
+            ->willReturn(10);
667
+        $filesNode2->expects($this->any())
668
+            ->method('getId')
669
+            ->willReturn(222);
670
+        $filesNode3 = $this->createMock(Folder::class);
671
+        $filesNode3->expects($this->any())
672
+            ->method('getSize')
673
+            ->willReturn(13);
674
+        $filesNode3->expects($this->any())
675
+            ->method('getId')
676
+            ->willReturn(333);
677
+
678
+        $tag123 = $this->createMock(ISystemTag::class);
679
+        $tag123->expects($this->any())
680
+            ->method('getName')
681
+            ->willReturn('OneTwoThree');
682
+        $tag123->expects($this->any())
683
+            ->method('isUserVisible')
684
+            ->willReturn(true);
685
+        $tag456 = $this->createMock(ISystemTag::class);
686
+        $tag456->expects($this->any())
687
+            ->method('getName')
688
+            ->willReturn('FourFiveSix');
689
+        $tag456->expects($this->any())
690
+            ->method('isUserVisible')
691
+            ->willReturn(false);
692
+
693
+        $this->tagManager->expects($this->once())
694
+            ->method('getTagsByIds')
695
+            ->with(['123', '456'])
696
+            ->willReturn([$tag123, $tag456]);
697
+
698
+        $this->userFolder->expects($this->exactly(2))
699
+            ->method('searchBySystemTag')
700
+            ->willReturnMap([
701
+                ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
702
+                ['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]],
703
+            ]);
704
+
705
+        $rules = [
706
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
707
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
708
+        ];
709
+
710
+        $this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
711
+    }
712
+
713
+
714
+    public function testProcessFilterRulesInvisibleTagAsUser(): void {
715
+        $this->expectException(TagNotFoundException::class);
716
+
717
+        $this->groupManager->expects($this->any())
718
+            ->method('isAdmin')
719
+            ->willReturn(false);
720
+
721
+        $tag123 = $this->createMock(ISystemTag::class);
722
+        $tag123->expects($this->any())
723
+            ->method('getName')
724
+            ->willReturn('OneTwoThree');
725
+        $tag123->expects($this->any())
726
+            ->method('isUserVisible')
727
+            ->willReturn(true);
728
+        $tag456 = $this->createMock(ISystemTag::class);
729
+        $tag456->expects($this->any())
730
+            ->method('getName')
731
+            ->willReturn('FourFiveSix');
732
+        $tag456->expects($this->any())
733
+            ->method('isUserVisible')
734
+            ->willReturn(false);
735
+
736
+        $this->tagManager->expects($this->once())
737
+            ->method('getTagsByIds')
738
+            ->with(['123', '456'])
739
+            ->willThrowException(new TagNotFoundException());
740
+
741
+        $this->userFolder->expects($this->never())
742
+            ->method('searchBySystemTag');
743
+
744
+        $rules = [
745
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
746
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
747
+        ];
748
+
749
+        self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]);
750
+    }
751
+
752
+    public function testProcessFilterRulesVisibleTagAsUser(): void {
753
+        $this->groupManager->expects($this->any())
754
+            ->method('isAdmin')
755
+            ->willReturn(false);
756
+
757
+        $tag1 = $this->createMock(ISystemTag::class);
758
+        $tag1->expects($this->any())
759
+            ->method('getId')
760
+            ->willReturn('123');
761
+        $tag1->expects($this->any())
762
+            ->method('isUserVisible')
763
+            ->willReturn(true);
764
+        $tag1->expects($this->any())
765
+            ->method('getName')
766
+            ->willReturn('OneTwoThree');
767
+
768
+        $tag2 = $this->createMock(ISystemTag::class);
769
+        $tag2->expects($this->any())
770
+            ->method('getId')
771
+            ->willReturn('123');
772
+        $tag2->expects($this->any())
773
+            ->method('isUserVisible')
774
+            ->willReturn(true);
775
+        $tag2->expects($this->any())
776
+            ->method('getName')
777
+            ->willReturn('FourFiveSix');
778
+
779
+        $this->tagManager->expects($this->once())
780
+            ->method('getTagsByIds')
781
+            ->with(['123', '456'])
782
+            ->willReturn([$tag1, $tag2]);
783
+
784
+        $filesNode1 = $this->createMock(File::class);
785
+        $filesNode1->expects($this->any())
786
+            ->method('getId')
787
+            ->willReturn(111);
788
+        $filesNode1->expects($this->any())
789
+            ->method('getSize')
790
+            ->willReturn(12);
791
+        $filesNode2 = $this->createMock(Folder::class);
792
+        $filesNode2->expects($this->any())
793
+            ->method('getId')
794
+            ->willReturn(222);
795
+        $filesNode2->expects($this->any())
796
+            ->method('getSize')
797
+            ->willReturn(10);
798
+        $filesNode3 = $this->createMock(Folder::class);
799
+        $filesNode3->expects($this->any())
800
+            ->method('getId')
801
+            ->willReturn(333);
802
+        $filesNode3->expects($this->any())
803
+            ->method('getSize')
804
+            ->willReturn(33);
805
+
806
+        $this->tagManager->expects($this->once())
807
+            ->method('getTagsByIds')
808
+            ->with(['123', '456'])
809
+            ->willReturn([$tag1, $tag2]);
810
+
811
+        // main assertion: only user visible tags are being passed through.
812
+        $this->userFolder->expects($this->exactly(2))
813
+            ->method('searchBySystemTag')
814
+            ->willReturnMap([
815
+                ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]],
816
+                ['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]],
817
+            ]);
818
+
819
+        $rules = [
820
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
821
+            ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
822
+        ];
823
+
824
+        $this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])));
825
+    }
826
+
827
+    public function testProcessFavoriteFilter(): void {
828
+        $rules = [
829
+            ['name' => '{http://owncloud.org/ns}favorite', 'value' => '1'],
830
+        ];
831
+
832
+        $this->privateTags->expects($this->once())
833
+            ->method('getFavorites')
834
+            ->willReturn(['456', '789']);
835
+
836
+        $this->assertEquals(['456', '789'], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileIDs', [$rules])));
837
+    }
838
+
839
+    public static function filesBaseUriProvider(): array {
840
+        return [
841
+            ['', '', ''],
842
+            ['files/username', '', '/files/username'],
843
+            ['files/username/test', '/test', '/files/username'],
844
+            ['files/username/test/sub', '/test/sub', '/files/username'],
845
+            ['test', '/test', ''],
846
+        ];
847
+    }
848
+
849
+    /**
850
+     * @dataProvider filesBaseUriProvider
851
+     */
852
+    public function testFilesBaseUri(string $uri, string $reportPath, string $expectedUri): void {
853
+        $this->assertEquals($expectedUri, self::invokePrivate($this->plugin, 'getFilesBaseUri', [$uri, $reportPath]));
854
+    }
855 855
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -107,7 +107,7 @@  discard block
 block discarded – undo
107 107
 
108 108
 		$this->tree->expects($this->any())
109 109
 			->method('getNodeForPath')
110
-			->with('/' . $path)
110
+			->with('/'.$path)
111 111
 			->willReturn($this->createMock(INode::class));
112 112
 
113 113
 		$this->server->expects($this->any())
@@ -115,7 +115,7 @@  discard block
 block discarded – undo
115 115
 			->willReturn($path);
116 116
 		$this->plugin->initialize($this->server);
117 117
 
118
-		$this->assertNull($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path));
118
+		$this->assertNull($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/'.$path));
119 119
 	}
120 120
 
121 121
 	public function testOnReportInvalidReportName(): void {
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
 
124 124
 		$this->tree->expects($this->any())
125 125
 			->method('getNodeForPath')
126
-			->with('/' . $path)
126
+			->with('/'.$path)
127 127
 			->willReturn(
128 128
 				$this->getMockBuilder(INode::class)
129 129
 					->disableOriginalConstructor()
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
 			->willReturn($path);
136 136
 		$this->plugin->initialize($this->server);
137 137
 
138
-		$this->assertNull($this->plugin->onReport('{whoever}whatever', [], '/' . $path));
138
+		$this->assertNull($this->plugin->onReport('{whoever}whatever', [], '/'.$path));
139 139
 	}
140 140
 
141 141
 	public function testOnReport(): void {
@@ -182,7 +182,7 @@  discard block
 block discarded – undo
182 182
 
183 183
 		$this->tree->expects($this->any())
184 184
 			->method('getNodeForPath')
185
-			->with('/' . $path)
185
+			->with('/'.$path)
186 186
 			->willReturn($reportTargetNode);
187 187
 
188 188
 		$filesNode1 = $this->createMock(File::class);
@@ -227,7 +227,7 @@  discard block
 block discarded – undo
227 227
 		$this->server->httpResponse = $response;
228 228
 		$this->plugin->initialize($this->server);
229 229
 
230
-		$this->assertFalse($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path));
230
+		$this->assertFalse($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/'.$path));
231 231
 	}
232 232
 
233 233
 	public function testFindNodesByFileIdsRoot(): void {
Please login to merge, or discard this patch.