Merge "RevisionStoreDbTestBase, remove redundant needsDB override"
[lhc/web/wiklou.git] / tests / phpunit / includes / Storage / RevisionStoreRecordTest.php
1 <?php
2
3 namespace MediaWiki\Tests\Storage;
4
5 use CommentStoreComment;
6 use InvalidArgumentException;
7 use MediaWiki\Storage\RevisionRecord;
8 use MediaWiki\Storage\RevisionSlots;
9 use MediaWiki\Storage\RevisionStoreRecord;
10 use MediaWiki\Storage\SlotRecord;
11 use MediaWiki\User\UserIdentity;
12 use MediaWiki\User\UserIdentityValue;
13 use MediaWikiTestCase;
14 use TextContent;
15 use Title;
16
17 /**
18 * @covers \MediaWiki\Storage\RevisionStoreRecord
19 * @covers \MediaWiki\Storage\RevisionRecord
20 */
21 class RevisionStoreRecordTest extends MediaWikiTestCase {
22
23 use RevisionRecordTests;
24
25 /**
26 * @param array $rowOverrides
27 *
28 * @return RevisionStoreRecord
29 */
30 protected function newRevision( array $rowOverrides = [] ) {
31 $title = Title::newFromText( 'Dummy' );
32 $title->resetArticleID( 17 );
33
34 $user = new UserIdentityValue( 11, 'Tester', 0 );
35 $comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
36
37 $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) );
38 $aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) );
39 $slots = new RevisionSlots( [ $main, $aux ] );
40
41 $row = [
42 'rev_id' => '7',
43 'rev_page' => strval( $title->getArticleID() ),
44 'rev_timestamp' => '20200101000000',
45 'rev_deleted' => 0,
46 'rev_minor_edit' => 0,
47 'rev_parent_id' => '5',
48 'rev_len' => $slots->computeSize(),
49 'rev_sha1' => $slots->computeSha1(),
50 'page_latest' => '18',
51 ];
52
53 $row = array_merge( $row, $rowOverrides );
54
55 return new RevisionStoreRecord( $title, $user, $comment, (object)$row, $slots );
56 }
57
58 public function provideConstructor() {
59 $title = Title::newFromText( 'Dummy' );
60 $title->resetArticleID( 17 );
61
62 $user = new UserIdentityValue( 11, 'Tester', 0 );
63 $comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
64
65 $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) );
66 $aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) );
67 $slots = new RevisionSlots( [ $main, $aux ] );
68
69 $protoRow = [
70 'rev_id' => '7',
71 'rev_page' => strval( $title->getArticleID() ),
72 'rev_timestamp' => '20200101000000',
73 'rev_deleted' => 0,
74 'rev_minor_edit' => 0,
75 'rev_parent_id' => '5',
76 'rev_len' => $slots->computeSize(),
77 'rev_sha1' => $slots->computeSha1(),
78 'page_latest' => '18',
79 ];
80
81 $row = $protoRow;
82 yield 'all info' => [
83 $title,
84 $user,
85 $comment,
86 (object)$row,
87 $slots,
88 'acmewiki'
89 ];
90
91 $row = $protoRow;
92 $row['rev_minor_edit'] = '1';
93 $row['rev_deleted'] = strval( RevisionRecord::DELETED_USER );
94
95 yield 'minor deleted' => [
96 $title,
97 $user,
98 $comment,
99 (object)$row,
100 $slots
101 ];
102
103 $row = $protoRow;
104 $row['page_latest'] = $row['rev_id'];
105
106 yield 'latest' => [
107 $title,
108 $user,
109 $comment,
110 (object)$row,
111 $slots
112 ];
113
114 $row = $protoRow;
115 unset( $row['rev_parent'] );
116
117 yield 'no parent' => [
118 $title,
119 $user,
120 $comment,
121 (object)$row,
122 $slots
123 ];
124
125 $row = $protoRow;
126 $row['rev_len'] = null;
127 $row['rev_sha1'] = '';
128
129 yield 'rev_len is null, rev_sha1 is ""' => [
130 $title,
131 $user,
132 $comment,
133 (object)$row,
134 $slots
135 ];
136
137 $row = $protoRow;
138 yield 'no length, no hash' => [
139 Title::newFromText( 'DummyDoesNotExist' ),
140 $user,
141 $comment,
142 (object)$row,
143 $slots
144 ];
145 }
146
147 /**
148 * @dataProvider provideConstructor
149 *
150 * @param Title $title
151 * @param UserIdentity $user
152 * @param CommentStoreComment $comment
153 * @param object $row
154 * @param RevisionSlots $slots
155 * @param bool $wikiId
156 */
157 public function testConstructorAndGetters(
158 Title $title,
159 UserIdentity $user,
160 CommentStoreComment $comment,
161 $row,
162 RevisionSlots $slots,
163 $wikiId = false
164 ) {
165 $rec = new RevisionStoreRecord( $title, $user, $comment, $row, $slots, $wikiId );
166
167 $this->assertSame( $title, $rec->getPageAsLinkTarget(), 'getPageAsLinkTarget' );
168 $this->assertSame( $user, $rec->getUser( RevisionRecord::RAW ), 'getUser' );
169 $this->assertSame( $comment, $rec->getComment(), 'getComment' );
170
171 $this->assertSame( $slots, $rec->getSlots(), 'getSlots' );
172 $this->assertSame( $slots->getSlotRoles(), $rec->getSlotRoles(), 'getSlotRoles' );
173 $this->assertSame( $slots->getSlots(), $rec->getSlots()->getSlots(), 'getSlots' );
174 $this->assertSame( $wikiId, $rec->getWikiId(), 'getWikiId' );
175
176 $this->assertSame( (int)$row->rev_id, $rec->getId(), 'getId' );
177 $this->assertSame( (int)$row->rev_page, $rec->getPageId(), 'getId' );
178 $this->assertSame( $row->rev_timestamp, $rec->getTimestamp(), 'getTimestamp' );
179 $this->assertSame( (int)$row->rev_deleted, $rec->getVisibility(), 'getVisibility' );
180 $this->assertSame( (bool)$row->rev_minor_edit, $rec->isMinor(), 'getIsMinor' );
181
182 if ( isset( $row->rev_parent_id ) ) {
183 $this->assertSame( (int)$row->rev_parent_id, $rec->getParentId(), 'getParentId' );
184 } else {
185 $this->assertSame( 0, $rec->getParentId(), 'getParentId' );
186 }
187
188 if ( isset( $row->rev_len ) ) {
189 $this->assertSame( (int)$row->rev_len, $rec->getSize(), 'getSize' );
190 } else {
191 $this->assertSame( $slots->computeSize(), $rec->getSize(), 'getSize' );
192 }
193
194 if ( !empty( $row->rev_sha1 ) ) {
195 $this->assertSame( $row->rev_sha1, $rec->getSha1(), 'getSha1' );
196 } else {
197 $this->assertSame( $slots->computeSha1(), $rec->getSha1(), 'getSha1' );
198 }
199
200 if ( isset( $row->page_latest ) ) {
201 $this->assertSame(
202 (int)$row->rev_id === (int)$row->page_latest,
203 $rec->isCurrent(),
204 'isCurrent'
205 );
206 } else {
207 $this->assertSame(
208 false,
209 $rec->isCurrent(),
210 'isCurrent'
211 );
212 }
213 }
214
215 public function provideConstructorFailure() {
216 $title = Title::newFromText( 'Dummy' );
217 $title->resetArticleID( 17 );
218
219 $user = new UserIdentityValue( 11, 'Tester', 0 );
220
221 $comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
222
223 $main = SlotRecord::newUnsaved( 'main', new TextContent( 'Lorem Ipsum' ) );
224 $aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) );
225 $slots = new RevisionSlots( [ $main, $aux ] );
226
227 $protoRow = [
228 'rev_id' => '7',
229 'rev_page' => strval( $title->getArticleID() ),
230 'rev_timestamp' => '20200101000000',
231 'rev_deleted' => 0,
232 'rev_minor_edit' => 0,
233 'rev_parent_id' => '5',
234 'rev_len' => $slots->computeSize(),
235 'rev_sha1' => $slots->computeSha1(),
236 'page_latest' => '18',
237 ];
238
239 yield 'not a row' => [
240 $title,
241 $user,
242 $comment,
243 'not a row',
244 $slots,
245 'acmewiki'
246 ];
247
248 $row = $protoRow;
249 $row['rev_timestamp'] = 'kittens';
250
251 yield 'bad timestamp' => [
252 $title,
253 $user,
254 $comment,
255 (object)$row,
256 $slots
257 ];
258
259 $row = $protoRow;
260 $row['rev_page'] = 99;
261
262 yield 'page ID mismatch' => [
263 $title,
264 $user,
265 $comment,
266 (object)$row,
267 $slots
268 ];
269
270 $row = $protoRow;
271
272 yield 'bad wiki' => [
273 $title,
274 $user,
275 $comment,
276 (object)$row,
277 $slots,
278 12345
279 ];
280 }
281
282 /**
283 * @dataProvider provideConstructorFailure
284 *
285 * @param Title $title
286 * @param UserIdentity $user
287 * @param CommentStoreComment $comment
288 * @param object $row
289 * @param RevisionSlots $slots
290 * @param bool $wikiId
291 */
292 public function testConstructorFailure(
293 Title $title,
294 UserIdentity $user,
295 CommentStoreComment $comment,
296 $row,
297 RevisionSlots $slots,
298 $wikiId = false
299 ) {
300 $this->setExpectedException( InvalidArgumentException::class );
301 new RevisionStoreRecord( $title, $user, $comment, $row, $slots, $wikiId );
302 }
303
304 public function provideIsCurrent() {
305 yield [
306 [
307 'rev_id' => 11,
308 'page_latest' => 11,
309 ],
310 true,
311 ];
312 yield [
313 [
314 'rev_id' => 11,
315 'page_latest' => 10,
316 ],
317 false,
318 ];
319 }
320
321 /**
322 * @dataProvider provideIsCurrent
323 */
324 public function testIsCurrent( $row, $current ) {
325 $rev = $this->newRevision( $row );
326
327 $this->assertSame( $current, $rev->isCurrent(), 'isCurrent()' );
328 }
329
330 public function provideGetSlot_audience_latest() {
331 return $this->provideAudienceCheckData( RevisionRecord::DELETED_TEXT );
332 }
333
334 /**
335 * @dataProvider provideGetSlot_audience_latest
336 */
337 public function testGetSlot_audience_latest( $visibility, $groups, $userCan, $publicCan ) {
338 $this->forceStandardPermissions();
339
340 $user = $this->getTestUser( $groups )->getUser();
341 $rev = $this->newRevision(
342 [
343 'rev_deleted' => $visibility,
344 'rev_id' => 11,
345 'page_latest' => 11, // revision is current
346 ]
347 );
348
349 // NOTE: slot meta-data is never suppressed, just the content is!
350 $this->assertNotNull( $rev->getSlot( 'main', RevisionRecord::RAW ), 'raw can' );
351 $this->assertNotNull( $rev->getSlot( 'main', RevisionRecord::FOR_PUBLIC ), 'public can' );
352
353 $this->assertNotNull(
354 $rev->getSlot( 'main', RevisionRecord::FOR_THIS_USER, $user ),
355 'user can'
356 );
357
358 $rev->getSlot( 'main', RevisionRecord::RAW )->getContent();
359 // NOTE: the content of the current revision is never suppressed!
360 // Check that getContent() doesn't throw SuppressedDataException
361 $rev->getSlot( 'main', RevisionRecord::FOR_PUBLIC )->getContent();
362 $rev->getSlot( 'main', RevisionRecord::FOR_THIS_USER, $user )->getContent();
363 }
364
365 }