Fix field names and behavior in SlotRecord.
[lhc/web/wiklou.git] / tests / phpunit / includes / Storage / SlotRecordTest.php
index 27fcd0c..ef31315 100644 (file)
@@ -2,10 +2,12 @@
 
 namespace MediaWiki\Tests\Storage;
 
+use InvalidArgumentException;
+use LogicException;
+use MediaWiki\Storage\IncompleteRevisionException;
 use MediaWiki\Storage\SlotRecord;
+use MediaWiki\Storage\SuppressedDataException;
 use MediaWikiTestCase;
-use RuntimeException;
-use Wikimedia\Assert\ParameterTypeException;
 use WikitextContent;
 
 /**
@@ -13,52 +15,88 @@ use WikitextContent;
  */
 class SlotRecordTest extends MediaWikiTestCase {
 
-       public function provideAContent() {
-               yield [ new WikitextContent( 'A' ) ];
-               yield [
-                       function ( SlotRecord $slotRecord ) {
-                               if ( $slotRecord->getAddress() === 'tt:456' ) {
-                                       return new WikitextContent( 'A' );
-                               }
-                               throw new RuntimeException( 'Got Wrong SlotRecord for callback' );
-                       },
-               ];
-       }
-
-       /**
-        * @dataProvider provideAContent
-        */
-       public function testValidConstruction( $content ) {
-               $row = (object)[
-                       'cont_size' => '1',
-                       'cont_sha1' => 'someHash',
-                       'cont_address' => 'tt:456',
-                       'model_name' => 'aModelname',
-                       'slot_revision' => '2',
-                       'format_name' => 'someFormatName',
+       private function makeRow( $data = [] ) {
+               $data = $data + [
+                       'slot_id' => 1234,
+                       'slot_content_id' => 33,
+                       'content_size' => '5',
+                       'content_sha1' => 'someHash',
+                       'content_address' => 'tt:456',
+                       'model_name' => CONTENT_MODEL_WIKITEXT,
+                       'format_name' => CONTENT_FORMAT_WIKITEXT,
+                       'slot_revision_id' => '2',
+                       'slot_inherited' => '1',
                        'role_name' => 'myRole',
-                       'slot_inherited' => '99'
                ];
+               return (object)$data;
+       }
 
-               $record = new SlotRecord( $row, $content );
+       public function testCompleteConstruction() {
+               $row = $this->makeRow();
+               $record = new SlotRecord( $row, new WikitextContent( 'A' ) );
 
+               $this->assertTrue( $record->hasAddress() );
+               $this->assertTrue( $record->hasRevision() );
+               $this->assertTrue( $record->isInherited() );
                $this->assertSame( 'A', $record->getContent()->getNativeData() );
-               $this->assertSame( 1, $record->getSize() );
+               $this->assertSame( 5, $record->getSize() );
                $this->assertSame( 'someHash', $record->getSha1() );
-               $this->assertSame( 'aModelname', $record->getModel() );
+               $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() );
                $this->assertSame( 2, $record->getRevision() );
                $this->assertSame( 'tt:456', $record->getAddress() );
-               $this->assertSame( 'someFormatName', $record->getFormat() );
+               $this->assertSame( 33, $record->getContentId() );
+               $this->assertSame( CONTENT_FORMAT_WIKITEXT, $record->getFormat() );
                $this->assertSame( 'myRole', $record->getRole() );
+       }
+
+       public function testConstructionDeferred() {
+               $row = $this->makeRow( [
+                       'content_size' => null, // to be computed
+                       'content_sha1' => null, // to be computed
+                       'format_name' => function () {
+                               return CONTENT_FORMAT_WIKITEXT;
+                       },
+                       'slot_inherited' => '0'
+               ] );
+
+               $content = function () {
+                       return new WikitextContent( 'A' );
+               };
+
+               $record = new SlotRecord( $row, $content );
+
                $this->assertTrue( $record->hasAddress() );
                $this->assertTrue( $record->hasRevision() );
-               $this->assertTrue( $record->isInherited() );
+               $this->assertFalse( $record->isInherited() );
+               $this->assertSame( 'A', $record->getContent()->getNativeData() );
+               $this->assertSame( 1, $record->getSize() );
+               $this->assertNotNull( $record->getSha1() );
+               $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() );
+               $this->assertSame( 2, $record->getRevision() );
+               $this->assertSame( 'tt:456', $record->getAddress() );
+               $this->assertSame( 33, $record->getContentId() );
+               $this->assertSame( CONTENT_FORMAT_WIKITEXT, $record->getFormat() );
+               $this->assertSame( 'myRole', $record->getRole() );
+       }
+
+       public function testNewUnsaved() {
+               $record = SlotRecord::newUnsaved( 'myRole', new WikitextContent( 'A' ) );
+
+               $this->assertFalse( $record->hasAddress() );
+               $this->assertFalse( $record->hasRevision() );
+               $this->assertFalse( $record->isInherited() );
+               $this->assertSame( 'A', $record->getContent()->getNativeData() );
+               $this->assertSame( 1, $record->getSize() );
+               $this->assertNotNull( $record->getSha1() );
+               $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() );
+               $this->assertSame( 'myRole', $record->getRole() );
        }
 
        public function provideInvalidConstruction() {
                yield 'both null' => [ null, null ];
                yield 'null row' => [ null, new WikitextContent( 'A' ) ];
-               yield 'array row' => [ null, new WikitextContent( 'A' ) ];
+               yield 'array row' => [ [], new WikitextContent( 'A' ) ];
+               yield 'empty row' => [ (object)[], new WikitextContent( 'A' ) ];
                yield 'null content' => [ (object)[], null ];
        }
 
@@ -66,25 +104,168 @@ class SlotRecordTest extends MediaWikiTestCase {
         * @dataProvider provideInvalidConstruction
         */
        public function testInvalidConstruction( $row, $content ) {
-               $this->setExpectedException( ParameterTypeException::class );
+               $this->setExpectedException( InvalidArgumentException::class );
                new SlotRecord( $row, $content );
        }
 
-       public function testHasAddress_false() {
-               $record = new SlotRecord( (object)[], new WikitextContent( 'A' ) );
-               $this->assertFalse( $record->hasAddress() );
+       public function testGetContentId_fails() {
+               $record = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
+               $this->setExpectedException( IncompleteRevisionException::class );
+
+               $record->getContentId();
        }
 
-       public function testHasRevision_false() {
-               $record = new SlotRecord( (object)[], new WikitextContent( 'A' ) );
-               $this->assertFalse( $record->hasRevision() );
+       public function testGetAddress_fails() {
+               $record = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
+               $this->setExpectedException( IncompleteRevisionException::class );
+
+               $record->getAddress();
        }
 
-       public function testInInherited_false() {
-               // TODO unskip me once fixed.
-               $this->markTestSkipped( 'Should probably return false, needs fixing?' );
-               $record = new SlotRecord( (object)[], new WikitextContent( 'A' ) );
-               $this->assertFalse( $record->isInherited() );
+       public function testGetRevision_fails() {
+               $record = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
+               $this->setExpectedException( IncompleteRevisionException::class );
+
+               $record->getRevision();
+       }
+
+       public function provideHashStability() {
+               yield [ '', 'phoiac9h4m842xq45sp7s6u21eteeq1' ];
+               yield [ 'Lorem ipsum', 'hcr5u40uxr81d3nx89nvwzclfz6r9c5' ];
+       }
+
+       /**
+        * @dataProvider provideHashStability
+        */
+       public function testHashStability( $text, $hash ) {
+               // Changing the output of the hash function will break things horribly!
+
+               $this->assertSame( $hash, SlotRecord::base36Sha1( $text ) );
+
+               $record = SlotRecord::newUnsaved( 'main', new WikitextContent( $text ) );
+               $this->assertSame( $hash, $record->getSha1() );
+       }
+
+       public function testNewWithSuppressedContent() {
+               $input = new SlotRecord( $this->makeRow(), new WikitextContent( 'A' ) );
+               $output = SlotRecord::newWithSuppressedContent( $input );
+
+               $this->setExpectedException( SuppressedDataException::class );
+               $output->getContent();
+       }
+
+       public function testNewInherited() {
+               $row = $this->makeRow( [ 'slot_revision_id' => 7, 'slot_inherited' => 0 ] );
+               $parent = new SlotRecord( $row, new WikitextContent( 'A' ) );
+
+               // This would happen while doing an edit, before saving revision meta-data.
+               $inherited = SlotRecord::newInherited( $parent );
+
+               $this->assertSame( $parent->getContentId(), $inherited->getContentId() );
+               $this->assertSame( $parent->getAddress(), $inherited->getAddress() );
+               $this->assertSame( $parent->getContent(), $inherited->getContent() );
+               $this->assertTrue( $inherited->isInherited() );
+               $this->assertFalse( $inherited->hasRevision() );
+
+               // make sure we didn't mess with the internal state of $parent
+               $this->assertFalse( $parent->isInherited() );
+               $this->assertSame( 7, $parent->getRevision() );
+
+               // This would happen while doing an edit, after saving the revision meta-data
+               // and content meta-data.
+               $saved = SlotRecord::newSaved(
+                       10,
+                       $inherited->getContentId(),
+                       $inherited->getAddress(),
+                       $inherited
+               );
+               $this->assertSame( $parent->getContentId(), $saved->getContentId() );
+               $this->assertSame( $parent->getAddress(), $saved->getAddress() );
+               $this->assertSame( $parent->getContent(), $saved->getContent() );
+               $this->assertTrue( $saved->isInherited() );
+               $this->assertTrue( $saved->hasRevision() );
+               $this->assertSame( 10, $saved->getRevision() );
+
+               // make sure we didn't mess with the internal state of $parent or $inherited
+               $this->assertSame( 7, $parent->getRevision() );
+               $this->assertFalse( $inherited->hasRevision() );
+       }
+
+       public function testNewSaved() {
+               // This would happen while doing an edit, before saving revision meta-data.
+               $unsaved = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
+
+               // This would happen while doing an edit, after saving the revision meta-data
+               // and content meta-data.
+               $saved = SlotRecord::newSaved( 10, 20, 'theNewAddress', $unsaved );
+               $this->assertFalse( $saved->isInherited() );
+               $this->assertTrue( $saved->hasRevision() );
+               $this->assertTrue( $saved->hasAddress() );
+               $this->assertSame( 'theNewAddress', $saved->getAddress() );
+               $this->assertSame( 20, $saved->getContentId() );
+               $this->assertSame( 'A', $saved->getContent()->getNativeData() );
+               $this->assertSame( 10, $saved->getRevision() );
+
+               // make sure we didn't mess with the internal state of $unsaved
+               $this->assertFalse( $unsaved->hasAddress() );
+               $this->assertFalse( $unsaved->hasRevision() );
+       }
+
+       public function provideNewSaved_LogicException() {
+               $freshRow = $this->makeRow( [
+                       'content_id' => 10,
+                       'content_address' => 'address:1',
+                       'slot_inherited' => 0,
+                       'slot_revision_id' => 1,
+               ] );
+
+               $freshSlot = new SlotRecord( $freshRow, new WikitextContent( 'A' ) );
+               yield 'mismatching address' => [ 1, 10, 'address:BAD', $freshSlot ];
+               yield 'mismatching revision' => [ 5, 10, 'address:1', $freshSlot ];
+               yield 'mismatching content ID' => [ 1, 17, 'address:1', $freshSlot ];
+
+               $inheritedRow = $this->makeRow( [
+                       'content_id' => null,
+                       'content_address' => null,
+                       'slot_inherited' => 1
+               ] );
+
+               $inheritedSlot = new SlotRecord( $inheritedRow, new WikitextContent( 'A' ) );
+               yield 'inherited, but no address' => [ 1, 10, 'address:2', $inheritedSlot ];
+       }
+
+       /**
+        * @dataProvider provideNewSaved_LogicException
+        */
+       public function testNewSaved_LogicException(
+               $revisionId,
+               $contentId,
+               $contentAddress,
+               SlotRecord $protoSlot
+       ) {
+               $this->setExpectedException( LogicException::class );
+               SlotRecord::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot );
+       }
+
+       public function provideNewSaved_InvalidArgumentException() {
+               $unsaved = SlotRecord::newUnsaved( 'main', new WikitextContent( 'A' ) );
+
+               yield 'bad revision id' => [ 'xyzzy', 5, 'address', $unsaved ];
+               yield 'bad content id' => [ 7, 'xyzzy', 'address', $unsaved ];
+               yield 'bad content address' => [ 7, 5, 77, $unsaved ];
+       }
+
+       /**
+        * @dataProvider provideNewSaved_InvalidArgumentException
+        */
+       public function testNewSaved_InvalidArgumentException(
+               $revisionId,
+               $contentId,
+               $contentAddress,
+               SlotRecord $protoSlot
+       ) {
+               $this->setExpectedException( InvalidArgumentException::class );
+               SlotRecord::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot );
        }
 
 }