3 use Wikimedia\TestingAccessWrapper
;
6 * @group ResourceLoader
7 * @covers MessageBlobStore
9 class MessageBlobStoreTest
extends PHPUnit\Framework\TestCase
{
11 use MediaWikiCoversValidator
;
12 use PHPUnit4And6Compat
;
14 protected function setUp() {
16 // MediaWiki's test wrapper sets $wgMainWANCache to CACHE_NONE.
17 // Use HashBagOStuff here so that we can observe caching.
18 $this->wanCache
= new WANObjectCache( [
19 'cache' => new HashBagOStuff()
22 $this->clock
= 1301655600.000;
23 $this->wanCache
->setMockTime( $this->clock
);
26 public function testBlobCreation() {
27 $module = $this->makeModule( [ 'mainpage' ] );
28 $rl = new ResourceLoader();
29 $rl->register( $module->getName(), $module );
31 $blobStore = $this->makeBlobStore( null, $rl );
32 $blob = $blobStore->getBlob( $module, 'en' );
34 $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
37 public function testBlobCreation_unknownMessage() {
38 $module = $this->makeModule( [ 'i-dont-exist' ] );
39 $rl = new ResourceLoader();
40 $rl->register( $module->getName(), $module );
41 $blobStore = $this->makeBlobStore( null, $rl );
43 // Generating a blob should succeed without errors,
44 // even if a message is unknown.
45 $blob = $blobStore->getBlob( $module, 'en' );
46 $this->assertEquals( '{"i-dont-exist":"\u29fci-dont-exist\u29fd"}', $blob, 'Generated blob' );
49 public function testMessageCachingAndPurging() {
50 $module = $this->makeModule( [ 'example' ] );
51 $rl = new ResourceLoader();
52 $rl->register( $module->getName(), $module );
53 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
55 // Advance this new WANObjectCache instance to a normal state,
56 // by doing one "get" and letting its hold off period expire.
57 // Without this, the first real "get" would lazy-initialise the
58 // checkKey and thus reject the first "set".
59 $blobStore->getBlob( $module, 'en' );
62 // Arrange version 1 of a message
63 $blobStore->expects( $this->once() )
64 ->method( 'fetchMessage' )
65 ->will( $this->returnValue( 'First version' ) );
68 $blob = $blobStore->getBlob( $module, 'en' );
69 $this->assertEquals( '{"example":"First version"}', $blob, 'Blob for v1' );
72 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
73 $blobStore->expects( $this->once() )
74 ->method( 'fetchMessage' )
75 ->will( $this->returnValue( 'Second version' ) );
79 // We do not validate whether a cached message is up-to-date.
80 // Instead, changes to messages will send us a purge.
81 // When cache is not purged or expired, it must be used.
82 $blob = $blobStore->getBlob( $module, 'en' );
83 $this->assertEquals( '{"example":"First version"}', $blob, 'Reuse cached v1 blob' );
86 $blobStore->updateMessage( 'example' );
90 $blob = $blobStore->getBlob( $module, 'en' );
91 $this->assertEquals( '{"example":"Second version"}', $blob, 'Updated blob for v2' );
94 public function testPurgeEverything() {
95 $module = $this->makeModule( [ 'example' ] );
96 $rl = new ResourceLoader();
97 $rl->register( $module->getName(), $module );
98 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
99 // Advance this new WANObjectCache instance to a normal state.
100 $blobStore->getBlob( $module, 'en' );
103 // Arrange version 1 and 2
104 $blobStore->expects( $this->exactly( 2 ) )
105 ->method( 'fetchMessage' )
106 ->will( $this->onConsecutiveCalls( 'First', 'Second' ) );
109 $blob = $blobStore->getBlob( $module, 'en' );
110 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1' );
115 $blob = $blobStore->getBlob( $module, 'en' );
116 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1 again' );
123 $blob = $blobStore->getBlob( $module, 'en' );
124 $this->assertEquals( '{"example":"Second"}', $blob, 'Blob for v2' );
127 public function testValidateAgainstModuleRegistry() {
128 // Arrange version 1 of a module
129 $module = $this->makeModule( [ 'foo' ] );
130 $rl = new ResourceLoader();
131 $rl->register( $module->getName(), $module );
132 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
133 $blobStore->expects( $this->once() )
134 ->method( 'fetchMessage' )
135 ->will( $this->returnValueMap( [
136 // message key, language code, message value
137 [ 'foo', 'en', 'Hello' ],
141 $blob = $blobStore->getBlob( $module, 'en' );
142 $this->assertEquals( '{"foo":"Hello"}', $blob, 'Blob for v1' );
144 // Arrange version 2 of module
145 // While message values may be out of date, the set of messages returned
146 // must always match the set of message keys required by the module.
147 // We do not receive purges for this because no messages were changed.
148 $module = $this->makeModule( [ 'foo', 'bar' ] );
149 $rl = new ResourceLoader();
150 $rl->register( $module->getName(), $module );
151 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
152 $blobStore->expects( $this->exactly( 2 ) )
153 ->method( 'fetchMessage' )
154 ->will( $this->returnValueMap( [
155 // message key, language code, message value
156 [ 'foo', 'en', 'Hello' ],
157 [ 'bar', 'en', 'World' ],
161 $blob = $blobStore->getBlob( $module, 'en' );
162 $this->assertEquals( '{"foo":"Hello","bar":"World"}', $blob, 'Blob for v2' );
165 public function testSetLoggedIsVoid() {
166 $blobStore = $this->makeBlobStore();
167 $this->assertSame( null, $blobStore->setLogger( new Psr\Log\
NullLogger() ) );
170 private function makeBlobStore( $methods = null, $rl = null ) {
171 $blobStore = $this->getMockBuilder( MessageBlobStore
::class )
172 ->setConstructorArgs( [ $rl ??
$this->createMock( ResourceLoader
::class ) ] )
173 ->setMethods( $methods )
176 $access = TestingAccessWrapper
::newFromObject( $blobStore );
177 $access->wanCache
= $this->wanCache
;
181 private function makeModule( array $messages ) {
182 $module = new ResourceLoaderTestModule( [ 'messages' => $messages ] );
183 $module->setName( 'test.blobstore' );