SetSiteLinkTest::testSetSiteLink()   D
last analyzed

Complexity

Conditions 11
Paths 288

Size

Total Lines 93

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 93
rs 4.4593
c 0
b 0
f 0
cc 11
nc 288
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace Wikibase\Repo\Tests\Api;
6
7
use ApiUsageException;
8
use MediaWiki\MediaWikiServices;
9
use User;
10
use Wikibase\DataModel\Entity\Item;
11
use Wikibase\DataModel\Entity\ItemId;
12
use Wikibase\Repo\WikibaseRepo;
13
14
/**
15
 * @covers \Wikibase\Repo\Api\SetSiteLink
16
 * @covers \Wikibase\Repo\Api\ModifyEntity
17
 *
18
 * @license GPL-2.0-or-later
19
 *
20
 * @group API
21
 * @group Wikibase
22
 * @group WikibaseAPI
23
 * @group BreakingTheSlownessBarrier
24
 *
25
 * The database group has as a side effect that temporal database tables are created. This makes
26
 * it possible to test without poisoning a production database.
27
 * @group Database
28
 *
29
 * Some of the tests takes more time, and needs therefore longer time before they can be aborted
30
 * as non-functional. The reason why tests are aborted is assumed to be set up of temporal databases
31
 * that hold the first tests in a pending state awaiting access to the database.
32
 * @group medium
33
 */
34
class SetSiteLinkTest extends WikibaseApiTestCase {
35
36
	/**
37
	 * @var bool
38
	 */
39
	private static $hasSetup;
40
41
	/** @var ItemId */
42
	private static $gaItemId;
43
	/** @var ItemId */
44
	private static $faItemId;
45
	/** @var ItemId */
46
	private static $otherItemId;
47
48
	public function provideData() {
49
		return [
50
			[ //0 set new link using id
51
				'p' => [
52
					'handle' => 'Leipzig',
53
					'linksite' => 'dewiki',
54
					'linktitle' => 'leipzig',
55
					'badges' => '{gaItem}|{faItem}'
56
				],
57
				'e' => [
58
					'value' => [ 'dewiki' => [
59
						'title' => 'Leipzig',
60
						'badges' => [ '{gaItem}', '{faItem}' ]
61
					] ]
62
				]
63
			],
64
			[ //1 set new link using sitelink
65
				'p' => [
66
					'site' => 'dewiki',
67
					'title' => 'Berlin',
68
					'linksite' => 'nowiki',
69
					'linktitle' => 'berlin'
70
				],
71
				'e' => [
72
					'value' => [ 'nowiki' => [
73
						'title' => 'Berlin',
74
						'badges' => []
75
					] ],
76
					'indb' => 5
77
				]
78
			],
79
			[ //2 modify link using id
80
				'p' => [
81
					'handle' => 'Leipzig',
82
					'linksite' => 'dewiki',
83
					'linktitle' => 'Leipzig_Two',
84
					'badges' => ''
85
				],
86
				'e' => [
87
					'value' => [ 'dewiki' => [
88
						'title' => 'Leipzig Two',
89
						'badges' => []
90
					] ]
91
				]
92
			],
93
			[ //3 modify link using sitelink
94
				'p' => [
95
					'site' => 'dewiki',
96
					'title' => 'Berlin',
97
					'linksite' => 'nowiki',
98
					'linktitle' => 'Berlin_Two'
99
				],
100
				'e' => [
101
					'value' => [ 'nowiki' => [
102
						'title' => 'Berlin Two',
103
						'badges' => []
104
					] ],
105
					'indb' => 5
106
				]
107
			],
108
			[ //4 remove link using id (with a summary)
109
				'p' => [
110
					'handle' => 'Leipzig',
111
					'linksite' => 'dewiki',
112
					'linktitle' => '',
113
					'summary' => 'WooSummary'
114
				],
115
				'e' => [ 'value' => [] ] ],
116
			[ //5 remove link using sitelink
117
				'p' => [
118
					'site' => 'dewiki',
119
					'title' => 'Berlin',
120
					'linksite' => 'nowiki',
121
					'linktitle' => ''
122
				],
123
				'e' => [ 'value' => [], 'indb' => 4 ] ],
124
			[ //6 add badges to existing sitelink
125
				'p' => [
126
					'site' => 'dewiki',
127
					'title' => 'Berlin',
128
					'linksite' => 'dewiki',
129
					'linktitle' => 'Berlin',
130
					'badges' => '{faItem}|{gaItem}'
131
				],
132
				'e' => [
133
					'value' => [ 'dewiki' => [
134
						'title' => 'Berlin',
135
						'badges' => [ '{faItem}', '{gaItem}' ]
136
					] ],
137
					'indb' => 4
138
				]
139
			],
140
			[ //7 add duplicate badges to existing sitelink
141
				'p' => [
142
					'site' => 'dewiki',
143
					'title' => 'Berlin',
144
					'linksite' => 'dewiki',
145
					'linktitle' => 'Berlin',
146
					'badges' => '{gaItem}|{gaItem}|{faItem}|{gaItem}'
147
				],
148
				'e' => [
149
					'value' => [ 'dewiki' => [
150
						'title' => 'Berlin',
151
						'badges' => [ '{gaItem}', '{faItem}' ]
152
					] ],
153
					'indb' => 4
154
				]
155
			],
156
			[ //8 no change
157
				'p' => [
158
					'site' => 'dewiki',
159
					'title' => 'Berlin',
160
					'linksite' => 'dewiki',
161
					'linktitle' => 'Berlin',
162
					'badges' => '{gaItem}|{faItem}'
163
				],
164
				'e' => [
165
					'value' => [ 'dewiki' => [
166
						'title' => 'Berlin',
167
						'badges' => [ '{gaItem}', '{faItem}' ]
168
					] ],
169
					'indb' => 4
170
				]
171
			],
172
			[ //9 change only title, badges should be intact
173
				'p' => [
174
					'site' => 'dewiki',
175
					'title' => 'Berlin',
176
					'linksite' => 'dewiki',
177
					'linktitle' => 'Berlin_Two'
178
				],
179
				'e' => [
180
					'value' => [ 'dewiki' => [
181
						'title' => 'Berlin Two',
182
						'badges' => [ '{gaItem}', '{faItem}' ]
183
					] ],
184
					'indb' => 4
185
				]
186
			],
187
			[ //10 change both title and badges
188
				'p' => [
189
					'site' => 'dewiki',
190
					'title' => 'Berlin Two',
191
					'linksite' => 'dewiki',
192
					'linktitle' => 'Berlin',
193
					'badges' => '{gaItem}'
194
				],
195
				'e' => [
196
					'value' => [ 'dewiki' => [
197
						'title' => 'Berlin',
198
						'badges' => [ '{gaItem}' ]
199
					] ],
200
					'indb' => 4
201
				]
202
			],
203
			[ //11 change only badges, title intact
204
				'p' => [
205
					'site' => 'dewiki',
206
					'title' => 'Berlin',
207
					'linksite' => 'dewiki',
208
					'badges' => '{gaItem}|{faItem}'
209
				],
210
				'e' => [
211
					'value' => [ 'dewiki' => [
212
						'title' => 'Berlin',
213
						'badges' => [ '{gaItem}', '{faItem}' ]
214
					] ],
215
					'indb' => 4
216
				]
217
			],
218
			[ //12 set new link using id (without badges)
219
				'p' => [
220
					'handle' => 'Berlin',
221
					'linksite' => 'svwiki',
222
					'linktitle' => 'Berlin'
223
				],
224
				'e' => [
225
					'value' => [ 'svwiki' => [
226
						'title' => 'Berlin',
227
						'badges' => []
228
					] ],
229
					'indb' => 5
230
				]
231
			],
232
			[ //13 delete link by not providing neither title nor badges
233
				'p' => [ 'handle' => 'Berlin', 'linksite' => 'svwiki' ],
234
				'e' => [ 'value' => [], 'indb' => 4 ]
235
			],
236
		];
237
	}
238
239
	public function provideExceptionData() {
240
		return [
241
			[ //0 badtoken
242
				'p' => [
243
					'site' => 'dewiki',
244
					'title' => 'Berlin',
245
					'linksite' => 'svwiki',
246
					'linktitle' => 'testSetSiteLinkWithNoToken'
247
				],
248
				'e' => [ 'exception' => [
249
					'type' => ApiUsageException::class,
250
					'code' => $this->logicalOr(
251
						$this->equalTo( 'notoken' ),
252
						$this->equalTo( 'missingparam' )
253
					),
254
					'message' => 'The "token" parameter must be set'
255
				] ],
256
				'token' => false
257
			],
258
			[ //1
259
				'p' => [
260
					'site' => 'dewiki',
261
					'title' => 'Berlin',
262
					'linksite' => 'svwiki',
263
					'linktitle' => 'testSetSiteLinkWithBadToken',
264
					'token' => '88888888888888888888888888888888+\\'
265
				],
266
				'e' => [ 'exception' => [
267
					'type' => ApiUsageException::class,
268
					'code' => 'badtoken',
269
					'message' => 'Invalid CSRF token.'
270
				] ],
271
				'token' => false
272
			],
273
			[ //2 testSetSiteLinkWithNoId
274
				'p' => [
275
					'linksite' => 'enwiki',
276
					'linktitle' => 'testSetSiteLinkWithNoId'
277
				],
278
				'e' => [ 'exception' => [ 'type' => ApiUsageException::class ] ] ],
279
			[ //3 testSetSiteLinkWithBadId
280
				'p' => [
281
					'id' => 123456789,
282
					'linksite' => 'enwiki',
283
					'linktitle' => 'testSetSiteLinkWithNoId'
284
				],
285
				'e' => [ 'exception' => [ 'type' => ApiUsageException::class ] ] ],
286
			[ //4 testSetSiteLinkWithBadSite
287
				'p' => [
288
					'site' => 'dewiktionary',
289
					'title' => 'Berlin',
290
					'linksite' => 'enwiki',
291
					'linktitle' => 'Berlin'
292
				],
293
				'e' => [ 'exception' => [ 'type' => ApiUsageException::class ] ] ],
294
			[ //5 testSetSiteLinkWithBadTitle
295
				'p' => [
296
					'site' => 'dewiki',
297
					'title' => 'BadTitle_de',
298
					'linksite' => 'enwiki',
299
					'linktitle' => 'BadTitle_en'
300
				],
301
				'e' => [ 'exception' => [ 'type' => ApiUsageException::class ] ] ],
302
			[ //6 testSetSiteLinkWithBadTargetSite
303
				'p' => [
304
					'site' => 'dewiki',
305
					'title' => 'Berlin',
306
					'linksite' => 'enwiktionary',
307
					'linktitle' => 'Berlin'
308
				],
309
				'e' => [ 'exception' => [ 'type' => ApiUsageException::class ] ] ],
310
			[ //7 badge item does not exist
311
				'p' => [
312
					'site' => 'enwiki',
313
					'title' => 'Berlin',
314
					'linksite' => 'enwiki',
315
					'linktitle' => 'Berlin',
316
					'badges' => 'Q99999|{faItem}'
317
				],
318
				'e' => [ 'exception' => [
319
					'type' => ApiUsageException::class,
320
					'code' => 'no-such-entity'
321
				] ]
322
			],
323
			[ //8 no sitelink - cannot change badges
324
				'p' => [
325
					'site' => 'enwiki',
326
					'title' => 'Berlin',
327
					'linksite' => 'svwiki',
328
					'badges' => '{gaItem}|{faItem}'
329
				],
330
				'e' => [ 'exception' => [
331
					'type' => ApiUsageException::class,
332
					'code' => 'modification-failed',
333
					'message' => wfMessage( 'wikibase-validator-no-such-sitelink', 'svwiki' )->inLanguage( 'en' )->text(),
334
				] ]
335
			],
336
		];
337
	}
338
339
	public function provideBadBadgeData() {
340
		return [
341
			[ //0 bad badge id
342
				[ 'site' => 'enwiki',
343
					'title' => 'Berlin',
344
					'linksite' => 'enwiki',
345
					'linktitle' => 'Berlin',
346
					'badges' => 'abc|{faItem}'
347
				],
348
			],
349
			[ //1 badge id is not an item id
350
				[ 'site' => 'enwiki',
351
					'title' => 'Berlin',
352
					'linksite' => 'enwiki',
353
					'linktitle' => 'Berlin',
354
					'badges' => 'P2|{faItem}'
355
				],
356
			],
357
			[ //2 badge id is not specified
358
				[ 'site' => 'enwiki',
359
					'title' => 'Berlin',
360
					'linksite' => 'enwiki',
361
					'linktitle' => 'Berlin',
362
					'badges' => '{faItem}|{otherItem}'
363
				]
364
			]
365
		];
366
	}
367
368
	protected function setUp(): void {
369
		parent::setUp();
370
371
		$wikibaseRepo = WikibaseRepo::getDefaultInstance();
372
373
		// XXX: This test doesn't mark tablesUsed so things created here will remain through all tests in the class.
374
		if ( !isset( self::$hasSetup ) ) {
375
			$store = $wikibaseRepo->getEntityStore();
376
377
			$this->initTestEntities( [ 'StringProp', 'Leipzig', 'Berlin' ] );
378
379
			$badge = new Item();
380
			$store->saveEntity( $badge, 'SetSiteLinkTestGA', $this->user, EDIT_NEW );
381
			self::$gaItemId = $badge->getId();
382
383
			$badge = new Item();
384
			$store->saveEntity( $badge, 'SetSiteLinkTestFA', $this->user, EDIT_NEW );
385
			self::$faItemId = $badge->getId();
386
387
			$badge = new Item();
388
			$store->saveEntity( $badge, 'SetSiteLinkTestOther', $this->user, EDIT_NEW );
389
			self::$otherItemId = $badge->getId();
390
		}
391
392
		$wikibaseRepo->getSettings()->setSetting( 'badgeItems', [
393
			self::$gaItemId->getSerialization() => '',
394
			self::$faItemId->getSerialization() => '',
395
			'Q99999' => '', // Just in case we have a wrong config
396
		] );
397
398
		self::$hasSetup = true;
399
	}
400
401
	/**
402
	 * Replace badge item id placeholders in expected results
403
	 *
404
	 * @param array $value
405
	 * @return array
406
	 */
407
	private function exceptionPlaceholder( array $value ) {
408
		foreach ( $value as &$site ) {
409
			if ( !isset( $site['badges'] ) ) {
410
				continue;
411
			}
412
			foreach ( $site['badges'] as &$dummy ) {
413
				if ( $dummy === '{gaItem}' ) {
414
					$dummy = self::$gaItemId->getSerialization();
415
				} elseif ( $dummy === '{faItem}' ) {
416
					$dummy = self::$faItemId->getSerialization();
417
				} elseif ( $dummy === '{otherItem}' ) {
418
					$dummy = self::$otherItemId->getSerialization();
419
				}
420
			}
421
		}
422
		return $value;
423
	}
424
425
	/**
426
	 * @dataProvider provideData
427
	 */
428
	public function testSetSiteLink( array $params, array $expected ) {
429
		// -- set any defaults ------------------------------------
430
		if ( array_key_exists( 'handle', $params ) ) {
431
			$params['id'] = EntityTestHelper::getId( $params['handle'] );
432
			unset( $params['handle'] );
433
		}
434
		$params['action'] = 'wbsetsitelink';
435
436
		// Replace the placeholder item ids in the API params
437
		if ( isset( $params['badges'] ) ) {
438
			$params['badges'] = str_replace(
439
				[ '{gaItem}', '{faItem}', '{otherItem}' ],
440
				[ self::$gaItemId->getSerialization(), self::$faItemId->getSerialization(), self::$otherItemId->getSerialization() ],
441
				$params['badges']
442
			);
443
		}
444
445
		// -- do the request --------------------------------------------------
446
		list( $result, , ) = $this->doApiRequestWithToken( $params );
447
448
		//@todo all of the below is very similar to the code in ModifyTermTestCase
449
		//This might be able to go in the same place
450
451
		// Replace the placeholder item ids in the expected results... this sucks
452
		if ( is_array( $expected['value'] ) ) {
453
			$expected['value'] = $this->exceptionPlaceholder( $expected['value'] );
454
		}
455
456
		// -- check the result ------------------------------------------------
457
		$this->assertArrayHasKey( 'success', $result, "Missing 'success' marker in response." );
458
		$this->assertResultHasEntityType( $result );
459
		$this->assertArrayHasKey( 'entity', $result, "Missing 'entity' section in response." );
460
		$this->assertArrayHasKey( 'lastrevid', $result['entity'], 'entity should contain lastrevid key' );
461
462
		// -- check the result only has our changed data (if any)  ------------
463
		$linkSite = $params['linksite'];
464
		$siteLinks = $result['entity']['sitelinks'];
465
466
		$this->assertCount( 1, $siteLinks,
467
			"Entity return contained more than a single site"
468
		);
469
470
		$this->assertArrayHasKey( $linkSite, $siteLinks,
471
			"Entity doesn't return expected site"
472
		);
473
474
		$siteLink = $siteLinks[$linkSite];
475
476
		$this->assertSame( $linkSite, $siteLink['site'],
477
			"Returned incorrect site"
478
		);
479
480
		if ( array_key_exists( $linkSite, $expected['value'] ) ) {
481
			$expectedSiteLink = $expected['value'][$linkSite];
482
483
			$this->assertArrayHasKey( 'url', $siteLink );
484
			$this->assertSame( $expectedSiteLink['title'], $siteLink['title'],
485
				"Returned incorrect title"
486
			);
487
488
			$this->assertArrayHasKey( 'badges', $siteLink );
489
			$this->assertArrayEquals( $expectedSiteLink['badges'], $siteLink['badges'] );
490
		} elseif ( empty( $expected['value'] ) ) {
491
			$this->assertArrayHasKey( 'removed', $siteLink,
492
				"Entity doesn't return expected 'removed' marker"
493
			);
494
		}
495
496
		// -- check any warnings ----------------------------------------------
497
		if ( array_key_exists( 'warning', $expected ) ) {
498
			$this->assertArrayHasKey( 'warnings', $result, "Missing 'warnings' section in response." );
499
			$this->assertSame( $expected['warning'], $result['warnings']['messages']['0']['name'] );
500
			$this->assertArrayHasKey( 'html', $result['warnings']['messages'] );
501
		}
502
503
		// -- check item in database -------------------------------------------
504
		$dbEntity = $this->loadEntity( $result['entity']['id'] );
505
		$expectedInDb = count( $expected['value'] );
506
		if ( array_key_exists( 'indb', $expected ) ) {
507
			$expectedInDb = $expected['indb'];
508
		}
509
		$this->assertArrayHasKey( 'sitelinks', $dbEntity );
510
		$this->assertCount( $expectedInDb, $dbEntity['sitelinks'] );
511
		$this->assertContainsAllSiteLinks( $expected['value'], $dbEntity['sitelinks'] );
512
513
		// -- check the edit summary --------------------------------------------
514
		if ( !array_key_exists( 'warning', $expected ) || $expected['warning'] != 'edit-no-change' ) {
515
			$this->assertRevisionSummary( [ 'wbsetsitelink', $params['linksite'] ], $result['entity']['lastrevid'] );
516
			if ( array_key_exists( 'summary', $params ) ) {
517
				$this->assertRevisionSummary( "/{$params['summary']}/", $result['entity']['lastrevid'] );
518
			}
519
		}
520
	}
521
522
	public function testSetSiteLinkWithTag() {
523
		$this->assertCanTagSuccessfulRequest( [
524
			'action' => 'wbsetsitelink',
525
			'site' => 'dewiki',
526
			'title' => 'Berlin',
527
			'linksite' => 'nowiki',
528
			'linktitle' => 'berlin'
529
		] );
530
	}
531
532
	/**
533
	 * @param array[] $expectedSiteLinks
534
	 * @param array[] $dbSiteLinks
535
	 */
536
	private function assertContainsAllSiteLinks( array $expectedSiteLinks, array $dbSiteLinks ) {
537
		foreach ( $expectedSiteLinks as $site => $expectedSiteLink ) {
538
			$this->assertArrayHasKey( $site, $dbSiteLinks );
539
			$dbSiteLink = $dbSiteLinks[$site];
540
541
			$this->assertArrayHasKey( 'title', $dbSiteLink );
542
			$this->assertIsString( $dbSiteLink['title'] );
543
			$this->assertSame( $expectedSiteLink['title'], $dbSiteLink['title'] );
544
545
			$this->assertArrayHasKey( 'badges', $dbSiteLink );
546
			$this->assertIsArray( $dbSiteLink['badges'] );
547
			$this->assertArrayEquals( $expectedSiteLink['badges'], $dbSiteLink['badges'] );
548
		}
549
	}
550
551
	/**
552
	 * @dataProvider provideExceptionData
553
	 */
554
	public function testSetSiteLinkExceptions( array $params, array $expected, $token = true ) {
555
		// -- set any defaults ------------------------------------
556
		$params['action'] = 'wbsetsitelink';
557
558
		// Replace the placeholder item ids in the API params
559
		if ( isset( $params['badges'] ) ) {
560
			$params['badges'] = str_replace(
561
				[ '{gaItem}', '{faItem}', '{otherItem}' ],
562
				[ self::$gaItemId->getSerialization(), self::$faItemId->getSerialization(), self::$otherItemId->getSerialization() ],
563
				$params['badges']
564
			);
565
		}
566
567
		$this->doTestQueryExceptions( $params, $expected['exception'], null, $token );
568
	}
569
570
	/**
571
	 * @dataProvider provideBadBadgeData
572
	 */
573
	public function testBadBadges( array $params ) {
574
		// -- set any defaults ------------------------------------
575
		$params['action'] = 'wbsetsitelink';
576
577
		// Replace the placeholder item ids in the API params
578
		if ( isset( $params['badges'] ) ) {
579
			$params['badges'] = str_replace(
580
				[ '{gaItem}', '{faItem}', '{otherItem}' ],
581
				[ self::$gaItemId->getSerialization(), self::$faItemId->getSerialization(), self::$otherItemId->getSerialization() ],
582
				$params['badges']
583
			);
584
		}
585
586
		list( $result, ) = $this->doApiRequestWithToken( $params );
587
588
		$warning = $result['warnings']['wbsetsitelink']['warnings'];
589
		$this->assertStringContainsString( 'Unrecognized value for parameter "badges"', $warning );
590
	}
591
592
	public function testUserCanSetSiteLinkWhenTheyHaveSufficientPermission() {
593
		$userWithAllPermissions = $this->createUserWithGroup( 'all-permission' );
594
595
		$this->setMwGlobals( 'wgGroupPermissions', [
596
			'all-permission' => [ 'edit' => true, ],
597
			'*' => [ 'read' => true, 'writeapi' => true ]
598
		] );
599
600
		$newItem = $this->createItemUsing( $userWithAllPermissions );
601
602
		list( $result, ) = $this->doApiRequestWithToken(
603
			$this->getSetSiteLinkRequestParams( $newItem->getId() ),
604
			null,
605
			$userWithAllPermissions
606
		);
607
608
		$this->assertSame( 1, $result['success'] );
609
	}
610
611
	public function testUserCannotSetSiteLinkWhenTheyLackPermission() {
612
		$userWithInsufficientPermissions = $this->createUserWithGroup( 'no-permission' );
613
		$userWithAllPermissions = $this->createUserWithGroup( 'all-permission' );
614
615
		$this->setMwGlobals( 'wgGroupPermissions', [
616
			'no-permission' => [ 'edit' => false ],
617
			'all-permission' => [ 'edit' => true, ],
618
			'*' => [ 'read' => true, 'writeapi' => true ]
619
		] );
620
621
		MediaWikiServices::getInstance()->resetServiceForTesting( 'PermissionManager' );
622
623
		// And an item
624
		$newItem = $this->createItemUsing( $userWithAllPermissions );
625
626
		// Then the request is denied
627
		$expected = [
628
			'type' => ApiUsageException::class,
629
			'code' => 'permissiondenied'
630
		];
631
632
		MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache(
633
			$userWithAllPermissions
634
		);
635
		MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache(
636
			$userWithInsufficientPermissions
637
		);
638
639
		$this->doTestQueryExceptions(
640
			$this->getSetSiteLinkRequestParams( $newItem->getId() ),
641
			$expected,
642
			$userWithInsufficientPermissions
643
		);
644
	}
645
646
	public function testUserCanCreateItemWithSiteLinkWhenTheyHaveSufficientPermissions() {
647
		$userWithAllPermissions = $this->createUserWithGroup( 'all-permission' );
648
649
		$this->setMwGlobals( 'wgGroupPermissions', [
650
			'all-permission' => [ 'edit' => true, 'createpage' => true ],
651
			'*' => [ 'read' => true, 'writeapi' => true ]
652
		] );
653
654
		list( $result, ) = $this->doApiRequestWithToken(
655
			$this->getCreateItemAndSetSiteLinkRequestParams(),
656
			null,
657
			$userWithAllPermissions
658
		);
659
660
		$this->assertSame( 1, $result['success'] );
661
		$this->assertSame( 'Another Cool Page', $result['entity']['sitelinks']['enwiki']['title'] );
662
	}
663
664
	public function testUserCannotCreateItemWhenTheyLackPermission() {
665
		$userWithInsufficientPermissions = $this->createUserWithGroup( 'no-permission' );
666
667
		$this->setMwGlobals( 'wgGroupPermissions', [
668
			'no-permission' => [ 'createpage' => false ],
669
			'*' => [ 'read' => true, 'edit' => true, 'writeapi' => true ]
670
		] );
671
672
		MediaWikiServices::getInstance()->resetServiceForTesting( 'PermissionManager' );
673
674
		// Then the request is denied
675
		$expected = [
676
			'type' => ApiUsageException::class,
677
			'code' => 'permissiondenied'
678
		];
679
680
		$this->doTestQueryExceptions(
681
			$this->getCreateItemAndSetSiteLinkRequestParams(),
682
			$expected,
683
			$userWithInsufficientPermissions
684
		);
685
	}
686
687
	/**
688
	 * @param User $user
689
	 *
690
	 * @return Item
691
	 */
692
	private function createItemUsing( User $user ) {
693
		$store = WikibaseRepo::getDefaultInstance()->getEntityStore();
694
695
		$itemRevision = $store->saveEntity( new Item(), 'SetSiteLinkTest', $user, EDIT_NEW );
696
		return $itemRevision->getEntity();
697
	}
698
699
	/**
700
	 * @param string $groupName
701
	 *
702
	 * @return User
703
	 */
704
	private function createUserWithGroup( $groupName ) {
705
		return $this->getTestUser( [ 'wbeditor', $groupName ] )->getUser();
706
	}
707
708
	private function getSetSiteLinkRequestParams( ItemId $id ) {
709
		return [
710
			'action' => 'wbsetsitelink',
711
			'id' => $id->getSerialization(),
712
			'linksite' => 'enwiki',
713
			'linktitle' => 'Some Cool Page',
714
		];
715
	}
716
717
	private function getCreateItemAndSetSiteLinkRequestParams() {
718
		return [
719
			'action' => 'wbsetsitelink',
720
			'new' => 'item',
721
			'linksite' => 'enwiki',
722
			'linktitle' => 'Another Cool Page',
723
		];
724
	}
725
726
}
727