Completed
Push — master ( 307ffa...21cd7c )
by
unknown
06:57 queued 10s
created

EditEntityActionTest::applyPermissions()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php
2
3
namespace Wikibase\Repo\Tests\Actions;
4
5
use ApiQueryInfo;
6
use MediaWiki\MediaWikiServices;
7
use MWException;
8
use Title;
9
use User;
10
use Wikibase\Repo\Actions\EditEntityAction;
11
use Wikibase\Repo\Actions\SubmitEntityAction;
12
use Wikibase\Repo\WikibaseRepo;
13
use WikiPage;
14
15
/**
16
 * @covers \Wikibase\Repo\Actions\EditEntityAction
17
 * @covers \Wikibase\Repo\Actions\SubmitEntityAction
18
 *
19
 * @license GPL-2.0-or-later
20
 * @author Daniel Kinzler
21
 *
22
 * @group Action
23
 * @group Wikibase
24
 * @group WikibaseAction
25
 *
26
 * @group Database
27
 * @group medium
28
 */
29
class EditEntityActionTest extends ActionTestCase {
30
31
	protected function setUp(): void {
32
		parent::setUp();
33
34
		// Remove handlers for the "OutputPageParserOutput" hook
35
		$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'OutputPageParserOutput' => [] ] );
36
	}
37
38
	public function testActionForPage() {
39
		$page = $this->getTestItemPage( 'Berlin' );
40
41
		$action = $this->createAction( 'edit', $page );
42
		$this->assertInstanceOf( EditEntityAction::class, $action );
43
44
		$action = $this->createAction( 'submit', $page );
45
		$this->assertInstanceOf( SubmitEntityAction::class, $action );
46
	}
47
48
	protected function adjustRevisionParam( $key, array &$params, WikiPage $page ) {
49
		if ( !isset( $params[$key] ) || ( is_int( $params[$key] ) && $params[$key] > 0 ) ) {
50
			return;
51
		}
52
53
		if ( is_array( $params[$key] ) ) {
54
			$page = $this->getTestItemPage( $params[$key][0] );
55
			$ofs = (int)$params[$key][1];
56
57
			$params[$key] = 0;
58
		} else {
59
			$ofs = (int)$params[$key];
60
		}
61
62
		$rev = $page->getRevisionRecord();
63
64
		if ( !$rev ) {
65
			return;
66
		}
67
68
		$revLookup = MediaWikiServices::getInstance()->getRevisionLookup();
69
		for ( $i = abs( $ofs ); $i > 0; $i -= 1 ) {
70
			$rev = $revLookup->getPreviousRevision( $rev );
71
			if ( !$rev ) {
72
				throw new MWException( 'Page ' . $page->getTitle()->getPrefixedDBkey()
73
					. ' does not have ' . ( abs( $ofs ) + 1 ) . ' revisions' );
74
			}
75
		}
76
77
		$params[ $key ] = $rev->getId();
78
	}
79
80
	public function provideUndoForm() {
81
		// based upon well known test items defined in ActionTestCase::makeTestItemData
82
83
		$cases = [
84
			[ //0: edit, no parameters
85
				'edit', // action
86
				'Berlin', // handle
87
				[], // params
88
				false, // post
89
				null, // user
90
				'/id="[^"]*\bwb-item\b[^"]*"/', // htmlPattern: should show an item
91
			],
92
93
			[ //1: submit, no parameters
94
				'submit', // action
95
				'Berlin', // handle
96
				[], // params
97
				false, // post
98
				null, // user
99
				'/id="[^"]*\bwb-item\b[^"]*"/', // htmlPattern: should show an item
100
			],
101
102
			// -- show undo form -----------------------------------
103
			[ //2: // undo form with legal undo
104
				'edit', // action
105
				'Berlin', // handle
106
				[ // params
107
					'undo' => 0, // current revision
108
				],
109
				false, // post
110
				null, // user
111
				'/undo-success/', // htmlPattern: should be a success
112
			],
113
114
			[ //3: // undo form with legal undo and undoafter
115
				'edit', // action
116
				'Berlin', // handle
117
				[ // params
118
					'undo' => 0, // current revision
119
					'undoafter' => -1, // previous revision
120
				],
121
				false, // post
122
				null, // user
123
				'/undo-success/', // htmlPattern: should be a success
124
			],
125
126
			[ //4: // undo form with illegal undo == undoafter
127
				'edit', // action
128
				'Berlin', // handle
129
				[ // params
130
					'undo' => -1, // previous revision
131
					'undoafter' => -1, // previous revision
132
				],
133
				false, // post
134
				null, // user
135
				'/wikibase-undo-samerev/', // htmlPattern: should contain error
136
			],
137
138
			[ //5: // undo form with legal undoafter
139
				'edit', // action
140
				'Berlin', // handle
141
				[ // params
142
					'undoafter' => -1, // previous revision
143
				],
144
				false, // post
145
				null, // user
146
				'/undo-success/', // htmlPattern: should be a success
147
			],
148
149
			[ //6: // undo form with illegal undo
150
				'edit', // action
151
				'Berlin', // handle
152
				[ // params
153
					'undo' => -2, // first revision
154
				],
155
				false, // post
156
				null, // user
157
				'/wikibase-undo-firstrev/', // htmlPattern: should contain error
158
			],
159
160
			[ //7: // undo form with illegal undoafter
161
				'edit', // action
162
				'Berlin', // handle
163
				[ // params
164
					'undoafter' => 0, // current revision
165
				],
166
				false, // post
167
				null, // user
168
				'/wikibase-undo-samerev/', // htmlPattern: should contain error
169
			],
170
171
			// -- show restore form -----------------------------------
172
			[ //8: // restore form with legal restore
173
				'edit', // action
174
				'Berlin', // handle
175
				[ // params
176
					'restore' => -1, // previous revision
177
				],
178
				false, // post
179
				null, // user
180
				'/class="diff/', // htmlPattern: should be a success and contain a diff (undo-success is not shown for restore)
181
			],
182
183
			[ //9: // restore form with illegal restore
184
				'edit', // action
185
				'Berlin', // handle
186
				[ // params
187
					'restore' => 0, // current revision
188
				],
189
				false, // post
190
				null, // user
191
				'/wikibase-undo-samerev/', // htmlPattern: should contain error
192
			],
193
194
			// -- bad revision -----------------------------------
195
			[ //10: // undo bad revision
196
				'edit', // action
197
				'Berlin', // handle
198
				[ // params
199
					'undo' => 12345678, // bad revision
200
				],
201
				false, // post
202
				null, // user
203
				'/undo-norev/', // htmlPattern: should contain error
204
			],
205
206
			[ //11: // undoafter bad revision with good undo
207
				'edit', // action
208
				'Berlin', // handle
209
				[ // params
210
					'undo' => 0, // current revision
211
					'undoafter' => 12345678, // bad revision
212
				],
213
				false, // post
214
				null, // user
215
				'/undo-norev/', // htmlPattern: should contain error
216
			],
217
218
			[ //12: // undoafter bad revision
219
				'edit', // action
220
				'Berlin', // handle
221
				[ // params
222
					'undoafter' => 12345678, // bad revision
223
				],
224
				false, // post
225
				null, // user
226
				'/undo-norev/', // htmlPattern: should contain error
227
			],
228
229
			[ //13: // restore bad revision
230
				'edit', // action
231
				'Berlin', // handle
232
				[ // params
233
					'restore' => 12345678, // bad revision
234
				],
235
				false, // post
236
				null, // user
237
				'/undo-norev/', // htmlPattern: should contain error
238
			],
239
240
			// -- bad page -----------------------------------
241
			[ //14: // non-existing page
242
				'edit', // action
243
				Title::newFromText( 'XXX', $this->getItemNamespace() ),
244
				[ // params
245
					'restore' => [ 'London', 0 ], // ok revision
246
				],
247
				false, // post
248
				null, // user
249
				'/missing-article/', // htmlPattern: should contain error
250
			],
251
252
			[ //15: // undo revision from different pages
253
				'edit', // action class
254
				'Berlin', // handle
255
				[ // params
256
					'undo' => [ 'London', 0 ], // wrong page
257
				],
258
				false, // post
259
				null, // user
260
				'/wikibase-undo-badpage/', // htmlPattern: should contain error
261
			],
262
263
			[ //16: // undoafter revision from different pages
264
				'edit', // action class
265
				'Berlin', // handle
266
				[ // params
267
					'undoafter' => [ 'London', -1 ], // wrong page
268
				],
269
				false, // post
270
				null, // user
271
				'/wikibase-undo-badpage/', // htmlPattern: should contain error
272
			],
273
274
			[ //17: // restore revision from different pages
275
				'edit', // action class
276
				'Berlin', // handle
277
				[ // params
278
					'restore' => [ 'London', -1 ], // wrong page
279
				],
280
				false, // post
281
				null, // user
282
				'/wikibase-undo-badpage/', // htmlPattern: should contain error
283
			],
284
285
		];
286
287
		// -- show undo form for redirect -----------------------------------
288
		$cases[] = [ //18: // undo form with legal undo
289
			'edit', // action
290
			'Berlin2', // handle
291
			[ // params
292
				'undo' => 0, // current revision
293
			],
294
			false, // post
295
			null, // user
296
			'/undo-success/', // htmlPattern: should be a success
297
		];
298
299
		return $cases;
300
	}
301
302
	/**
303
	 * @dataProvider provideUndoForm
304
	 */
305
	public function testUndoForm(
306
		$action,
307
		$page,
308
		array $params,
309
		$post = false,
310
		User $user = null,
311
		$htmlPattern = null,
312
		array $expectedProps = null
313
	) {
314
		$this->tryUndoAction( $action, $page, $params, $post, $user, $htmlPattern, $expectedProps );
315
	}
316
317
	public function provideUndoSubmit() {
318
		// based upon well known test items defined in ActionTestCase::makeTestItemData
319
		return [
320
			[ //0: submit with legal undo, but don't post
321
				'submit', // action
322
				'Berlin', // handle
323
				[ // params
324
					'wpSave' => 1,
325
					'wpEditToken' => true, // automatic token
326
					'undo' => 0, // current revision
327
				],
328
				false, // post
329
				null, // user
330
				null, // htmlPattern
331
				[
332
					'redirect' => '/[&?]action=edit&undo=\d+/', // redirect to undo form
333
				]
334
			],
335
336
			[ //1: submit with legal undo, but omit wpSave
337
				'submit', // action
338
				'Berlin', // handle
339
				[ // params
340
					'wpEditToken' => true, // automatic token
341
					'undo' => 0, // current revision
342
				],
343
				true, // post
344
				null, // user
345
				null, // htmlPattern
346
				[
347
					'redirect' => '/[&?]action=edit&undo=\d+/', // redirect to undo form
348
				]
349
			],
350
351
			// -- show undo form -----------------------------------
352
			[ //2: // undo form with legal undo
353
				'submit', // action
354
				'Berlin', // handle
355
				[ // params
356
					'wpSave' => 1,
357
					'wpEditToken' => true, // automatic token
358
					'undo' => 0, // current revision
359
				],
360
				true, // post
361
				null, // user
362
				null, // htmlPattern
363
				[
364
					'redirect' => '![:/=]Q\d+$!' // expect success and redirect to page
365
				],
366
			],
367
368
			[ //3: // undo form with legal undo and undoafter
369
				'submit', // action
370
				'Berlin', // handle
371
				[ // params
372
					'wpSave' => 1,
373
					'wpEditToken' => true, // automatic token
374
					'undo' => 0, // current revision
375
					'undoafter' => -1, // previous revision
376
				],
377
				true, // post
378
				null, // user
379
				null, // htmlPattern
380
				[
381
					'redirect' => '![:/=]Q\d+$!' // expect success and redirect to page
382
				],
383
			],
384
385
			[ //4: // undo form with illegal undo == undoafter
386
				'submit', // action
387
				'Berlin', // handle
388
				[ // params
389
					'wpSave' => 1,
390
					'wpEditToken' => true, // automatic token
391
					'undo' => -1, // previous revision
392
					'undoafter' => -1, // previous revision
393
				],
394
				true, // post
395
				null, // user
396
				'/wikibase-undo-samerev/', // htmlPattern: should contain error
397
			],
398
399
			[ //5: // undo form with legal undoafter
400
				'submit', // action
401
				'Berlin', // handle
402
				[ // params
403
					'wpSave' => 1,
404
					'wpEditToken' => true, // automatic token
405
					'undoafter' => -1, // previous revision
406
				],
407
				true, // post
408
				null, // user
409
				null, // htmlPattern
410
				[
411
					'redirect' => '![:/=]Q\d+$!' // expect success and redirect to page
412
				],
413
			],
414
415
			[ //6: // undo form with illegal undo
416
				'submit', // action
417
				'Berlin', // handle
418
				[ // params
419
					'wpSave' => 1,
420
					'wpEditToken' => true, // automatic token
421
					'undo' => -2, // first revision
422
				],
423
				true, // post
424
				null, // user
425
				'/wikibase-undo-firstrev/', // htmlPattern: should contain error
426
			],
427
428
			[ //7: // undo form with illegal undoafter
429
				'submit', // action
430
				'Berlin', // handle
431
				[ // params
432
					'wpSave' => 1,
433
					'wpEditToken' => true, // automatic token
434
					'undoafter' => 0, // current revision
435
				],
436
				true, // post
437
				null, // user
438
				'/wikibase-undo-samerev/', // htmlPattern: should contain error
439
			],
440
441
			// -- show restore form -----------------------------------
442
			[ //8: // restore form with legal restore
443
				'submit', // action
444
				'Berlin', // handle
445
				[ // params
446
					'wpSave' => 1,
447
					'wpEditToken' => true, // automatic token
448
					'restore' => -1, // previous revision
449
				],
450
				true, // post
451
				null, // user
452
				null, // htmlPattern
453
				[
454
					'redirect' => '![:/=]Q\d+$!' // expect success and redirect to page
455
				],
456
			],
457
458
			[ //9: // restore form with illegal restore
459
				'submit', // action
460
				'Berlin', // handle
461
				[ // params
462
					'wpSave' => 1,
463
					'wpEditToken' => true, // automatic token
464
					'restore' => 0, // current revision
465
				],
466
				true, // post
467
				null, // user
468
				'/wikibase-undo-samerev/', // htmlPattern: should contain error
469
			],
470
471
			// -- bad revision -----------------------------------
472
			[ //10: // undo bad revision
473
				'submit', // action
474
				'Berlin', // handle
475
				[ // params
476
					'wpSave' => 1,
477
					'wpEditToken' => true, // automatic token
478
					'undo' => 12345678, // bad revision
479
				],
480
				true, // post
481
				null, // user
482
				'/undo-norev/', // htmlPattern: should contain error
483
			],
484
485
			[ //11: // undoafter bad revision with good undo
486
				'submit', // action
487
				'Berlin', // handle
488
				[ // params
489
					'wpSave' => 1,
490
					'wpEditToken' => true, // automatic token
491
					'undo' => 0, // current revision
492
					'undoafter' => 12345678, // bad revision
493
				],
494
				true, // post
495
				null, // user
496
				'/undo-norev/', // htmlPattern: should contain error
497
			],
498
499
			[ //12: // undoafter bad revision
500
				'submit', // action
501
				'Berlin', // handle
502
				[ // params
503
					'wpSave' => 1,
504
					'wpEditToken' => true, // automatic token
505
					'undoafter' => 12345678, // bad revision
506
				],
507
				true, // post
508
				null, // user
509
				'/undo-norev/', // htmlPattern: should contain error
510
			],
511
512
			[ //13: // restore bad revision
513
				'submit', // action
514
				'Berlin', // handle
515
				[ // params
516
					'wpSave' => 1,
517
					'wpEditToken' => true, // automatic token
518
					'restore' => 12345678, // bad revision
519
				],
520
				true, // post
521
				null, // user
522
				'/undo-norev/', // htmlPattern: should contain error
523
			],
524
525
			// -- bad page -----------------------------------
526
			[ //14: // non-existing page
527
				'submit', // action
528
				Title::newFromText( 'XXX', $this->getItemNamespace() ),
529
				[ // params
530
					'wpSave' => 1,
531
					'wpEditToken' => true, // automatic token
532
					'restore' => [ 'London', 0 ], // ok revision
533
				],
534
				true, // post
535
				null, // user
536
				'/missing-article/', // htmlPattern: should contain error
537
			],
538
539
			[ //15: // undo revision from different pages
540
				'submit', // action
541
				'Berlin', // handle
542
				[ // params
543
					'wpSave' => 1,
544
					'wpEditToken' => true, // automatic token
545
					'undo' => [ 'London', 0 ], // wrong page
546
				],
547
				true, // post
548
				null, // user
549
				'/wikibase-undo-badpage/', // htmlPattern: should contain error
550
			],
551
552
			[ //16: // undoafter revision from different pages
553
				'submit', // action
554
				'Berlin', // handle
555
				[ // params
556
					'wpSave' => 1,
557
					'wpEditToken' => true, // automatic token
558
					'undoafter' => [ 'London', -1 ], // wrong page
559
				],
560
				true, // post
561
				null, // user
562
				'/wikibase-undo-badpage/', // htmlPattern: should contain error
563
			],
564
565
			[ //17: // restore revision from different pages
566
				'submit', // action
567
				'Berlin', // handle
568
				[ // params
569
					'wpSave' => 1,
570
					'wpEditToken' => true, // automatic token
571
					'restore' => [ 'London', -1 ], // wrong page
572
				],
573
				true, // post
574
				null, // user
575
				'/wikibase-undo-badpage/', // htmlPattern: should contain error
576
			],
577
578
			// -- bad token -----------------------------------
579
			[ //18: submit with legal undo, but wrong token
580
				'submit', // action
581
				'Berlin', // handle
582
				[ // params
583
					'wpSave' => 1,
584
					'wpEditToken' => 'xyz', // bad token
585
					'undo' => 0, // current revision
586
				],
587
				true, // post
588
				null, // user
589
				'/token_suffix_mismatch/', // htmlPattern: should contain error
590
			],
591
592
			// -- incomplete form -----------------------------------
593
			[ //19: submit without undo/undoafter/restore
594
				'submit', // action
595
				'Berlin', // handle
596
				[ // params
597
					'wpSave' => 1,
598
					'wpEditToken' => true, // bad token
599
				],
600
				true, // post
601
				null, // user
602
				'/id="[^"]*\bwb-item\b[^"]*"/', // htmlPattern: should show item
603
			],
604
605
		];
606
	}
607
608
	/**
609
	 * @dataProvider provideUndoSubmit
610
	 */
611
	public function testUndoSubmit(
612
		$action,
613
		$page,
614
		array $params,
615
		$post = false,
616
		User $user = null,
617
		$htmlPattern = null,
618
		array $expectedProps = null
619
	) {
620
		if ( is_string( $page ) ) {
621
			self::resetTestItem( $page );
622
		}
623
624
		$this->tryUndoAction( $action, $page, $params, $post, $user, $htmlPattern, $expectedProps );
625
626
		if ( is_string( $page ) ) {
627
			self::resetTestItem( $page );
628
		}
629
	}
630
631
	/**
632
	 * @param string $action
633
	 * @param WikiPage|Title|string $page
634
	 * @param array $params
635
	 * @param bool $post
636
	 * @param User|null $user
637
	 * @param string|bool|null $htmlPattern
638
	 * @param string[]|null $expectedProps
639
	 */
640
	protected function tryUndoAction(
641
		$action,
642
		$page,
643
		array $params,
644
		$post = false,
645
		User $user = null,
646
		$htmlPattern = null,
647
		array $expectedProps = null
648
	) {
649
		if ( $user ) {
650
			$this->setUser( $user );
651
		}
652
653
		if ( is_string( $page ) ) {
654
			$page = $this->getTestItemPage( $page );
655
		} elseif ( $page instanceof Title ) {
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
656
			$page = WikiPage::factory( $page );
657
		}
658
659
		$this->adjustRevisionParam( 'undo', $params, $page );
660
		$this->adjustRevisionParam( 'undoafter', $params, $page );
661
		$this->adjustRevisionParam( 'restore', $params, $page );
662
663
		if ( isset( $params['wpEditToken'] ) && $params['wpEditToken'] === true ) {
664
			$params['wpEditToken'] = $this->user->getEditToken(); //TODO: $user
665
		}
666
667
		$out = $this->callAction( $action, $page, $params, $post );
668
669
		if ( $htmlPattern !== null && $htmlPattern !== false ) {
670
			$this->assertRegExp( $htmlPattern, $out->getHTML() );
671
		}
672
673
		if ( $expectedProps ) {
674
			foreach ( $expectedProps as $p => $pattern ) {
675
				$func = 'get' . ucfirst( $p );
676
				$act = call_user_func( [ $out, $func ] );
677
678
				if ( $pattern === true ) {
679
					$this->assertNotSame( '', $act, $p );
680
				} elseif ( $pattern === false ) {
681
					$this->assertSame( '', $act, $p );
682
				} else {
683
					$this->assertRegExp( $pattern, $act, $p );
684
				}
685
			}
686
		}
687
	}
688
689
	public function provideUndoRevisions() {
690
		// based upon well known test items defined in ActionTestCase::makeTestItemData
691
692
		return [
693
			[ //0: undo last revision
694
				'Berlin', //handle
695
				[
696
					'undo' => 0, // last revision
697
				],
698
				[ //expected
699
					'descriptions' => [
700
						'de' => 'Stadt in Brandenburg',
701
						'en' => 'City in Germany',
702
					],
703
				],
704
			],
705
706
			[ //1: undo previous revision
707
				'Berlin', //handle
708
				[
709
					'undo' => -1, // previous revision
710
				],
711
				[ //expected
712
					'descriptions' => [
713
						'de' => 'Hauptstadt von Deutschland',
714
					],
715
				]
716
			],
717
718
			[ //2: undo last and previous revision
719
				'Berlin', //handle
720
				[
721
					'undo' => 0, // current revision
722
					'undoafter' => -2, // first revision
723
				],
724
				[ //expected
725
					'descriptions' => [
726
						'de' => 'Stadt in Deutschland',
727
					],
728
				]
729
			],
730
731
			[ //3: undoafter first revision (conflict, no change)
732
				'Berlin', //handle
733
				[
734
					'undoafter' => -2, // first revision
735
				],
736
				[ //expected
737
					'descriptions' => [
738
						'de' => 'Stadt in Deutschland',
739
					],
740
				]
741
			],
742
743
			[ //4: restore previous revision
744
				'Berlin', //handle
745
				[
746
					'restore' => -1, // previous revision
747
				],
748
				[ //expected
749
					'descriptions' => [
750
						'de' => 'Stadt in Brandenburg',
751
						'en' => 'City in Germany',
752
					],
753
				]
754
			],
755
756
			[ //5: restore first revision
757
				'Berlin', //handle
758
				[
759
					'restore' => -2, // first revision
760
				],
761
				[ //expected
762
					'descriptions' => [
763
						'de' => 'Stadt in Deutschland',
764
					],
765
				]
766
			],
767
		];
768
	}
769
770
	/**
771
	 * @dataProvider provideUndoRevisions
772
	 */
773
	public function testUndoRevisions( $handle, array $params, array $expected ) {
774
		self::resetTestItem( $handle );
775
776
		$page = $this->getTestItemPage( $handle );
777
778
		$this->adjustRevisionParam( 'undo', $params, $page );
779
		$this->adjustRevisionParam( 'undoafter', $params, $page );
780
		$this->adjustRevisionParam( 'restore', $params, $page );
781
782
		if ( !isset( $params['wpEditToken'] ) ) {
783
			$params['wpEditToken'] = $this->user->getEditToken();
784
		}
785
786
		if ( !isset( $params['wpSave'] ) ) {
787
			$params['wpSave'] = 1;
788
		}
789
790
		$out = $this->callAction( 'submit', $page, $params, true );
791
792
		$this->assertRegExp( '![:/=]Q\d+$!', $out->getRedirect(), 'successful operation should return a redirect' );
793
794
		$item = $this->loadTestItem( $handle );
795
796
		if ( isset( $expected['labels'] ) ) {
797
			$this->assertArrayEquals( $expected['labels'], $item->getLabels()->toTextArray(), false, true );
798
		}
799
800
		if ( isset( $expected['descriptions'] ) ) {
801
			$this->assertArrayEquals( $expected['descriptions'], $item->getDescriptions()->toTextArray(), false, true );
802
		}
803
804
		if ( isset( $expected['aliases'] ) ) {
805
			$this->assertArrayEquals( $expected['aliases'], $item->getAliasGroups()->toTextArray(), false, true );
806
		}
807
808
		if ( isset( $expected['sitelinks'] ) ) {
809
			$actual = [];
810
811
			foreach ( $item->getSiteLinkList()->toArray() as $siteLink ) {
812
				$actual[$siteLink->getSiteId()] = $siteLink->getPageName();
813
			}
814
815
			$this->assertArrayEquals( $expected['sitelinks'], $actual, false, true );
816
		}
817
818
		self::resetTestItem( $handle );
819
	}
820
821
	public function provideUndoPermissions() {
822
		return [
823
			[ //0
824
				'edit',
825
				[
826
					'*' => [ 'edit' => false ],
827
					'user' => [ 'edit' => false ],
828
				],
829
				'/permissions-errors/'
830
			],
831
832
			[ //1
833
				'submit',
834
				[
835
					'*' => [ 'edit' => false ],
836
					'user' => [ 'edit' => false ],
837
				],
838
				'/permissions-errors/'
839
			],
840
		];
841
	}
842
843
	/**
844
	 * @dataProvider provideUndoPermissions
845
	 */
846
	public function testUndoPermissions( $action, array $permissions, $error ) {
847
		$handle = 'London';
848
849
		self::resetTestItem( $handle );
850
851
		$this->mergeMwGlobalArrayValue( 'wgGroupPermissions', $permissions );
852
853
		$page = $this->getTestItemPage( $handle );
854
855
		$params = [
856
			'wpEditToken' => $this->user->getEditToken(),
857
			'wpSave' => 1,
858
			'undo' => $page->getLatest(),
859
		];
860
861
		$out = $this->callAction( $action, $page, $params, true );
862
863
		if ( $error ) {
864
			$this->assertRegExp( $error, $out->getHTML() );
865
866
			$this->assertSame( '', $out->getRedirect(), 'operation should not trigger a redirect' );
867
		} else {
868
			$this->assertRegExp( '![:/=]Q\d+$!', $out->getRedirect(), 'successful operation should return a redirect' );
869
		}
870
871
		self::resetTestItem( $handle );
872
	}
873
874
	/**
875
	 * @return int
876
	 */
877
	private function getItemNamespace() {
878
		 $entityNamespaceLookup = WikibaseRepo::getDefaultInstance()->getEntityNamespaceLookup();
879
		 return $entityNamespaceLookup->getEntityNamespace( 'item' );
880
	}
881
882
	/**
883
	 * Changes $this->user and resets any associated state
884
	 *
885
	 * @param User $user the desired user
886
	 */
887
	private function setUser( User $user ) {
888
		if ( $user->getName() !== $this->user->getName() ) {
889
			$this->user = $user;
890
			ApiQueryInfo::resetTokenCache();
891
		}
892
	}
893
}
894