3 use Wikimedia\TestingAccessWrapper
;
6 * @group ResourceLoader
7 * @covers MessageBlobStore
9 class MessageBlobStoreTest
extends PHPUnit\Framework\TestCase
{
11 use MediaWikiCoversValidator
;
12 use PHPUnit4And6Compat
;
14 const NAME
= 'test.blobstore';
16 protected function setUp() {
18 // MediaWiki's test wrapper sets $wgMainWANCache to CACHE_NONE.
19 // Use HashBagOStuff here so that we can observe caching.
20 $this->wanCache
= new WANObjectCache( [
21 'cache' => new HashBagOStuff()
24 $this->clock
= 1301655600.000;
25 $this->wanCache
->setMockTime( $this->clock
);
28 public function testBlobCreation() {
29 $rl = new EmptyResourceLoader();
30 $rl->register( self
::NAME
, [
31 'factory' => function () {
32 return $this->makeModule( [ 'mainpage' ] );
36 $blobStore = $this->makeBlobStore( null, $rl );
37 $blob = $blobStore->getBlob( $rl->getModule( self
::NAME
), 'en' );
39 $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
42 public function testBlobCreation_empty() {
43 $module = $this->makeModule( [] );
44 $rl = new EmptyResourceLoader();
46 $blobStore = $this->makeBlobStore( null, $rl );
47 $blob = $blobStore->getBlob( $module, 'en' );
49 $this->assertEquals( '{}', $blob, 'Generated blob' );
52 public function testBlobCreation_unknownMessage() {
53 $module = $this->makeModule( [ 'i-dont-exist', 'mainpage', 'i-dont-exist2' ] );
54 $rl = new EmptyResourceLoader();
55 $blobStore = $this->makeBlobStore( null, $rl );
57 // Generating a blob should continue without errors,
58 // with keys of unknown messages excluded from the blob.
59 $blob = $blobStore->getBlob( $module, 'en' );
60 $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
63 public function testMessageCachingAndPurging() {
64 $rl = new EmptyResourceLoader();
65 // Register it so that MessageBlobStore::updateMessage can
66 // discover it from the registry as a module that uses this message.
67 $rl->register( self
::NAME
, [
68 'factory' => function () {
69 return $this->makeModule( [ 'example' ] );
72 $module = $rl->getModule( self
::NAME
);
73 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
75 // Advance this new WANObjectCache instance to a normal state,
76 // by doing one "get" and letting its hold off period expire.
77 // Without this, the first real "get" would lazy-initialise the
78 // checkKey and thus reject the first "set".
79 $blobStore->getBlob( $module, 'en' );
82 // Arrange version 1 of a message
83 $blobStore->expects( $this->once() )
84 ->method( 'fetchMessage' )
85 ->will( $this->returnValue( 'First version' ) );
88 $blob = $blobStore->getBlob( $module, 'en' );
89 $this->assertEquals( '{"example":"First version"}', $blob, 'Blob for v1' );
92 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
93 $blobStore->expects( $this->once() )
94 ->method( 'fetchMessage' )
95 ->will( $this->returnValue( 'Second version' ) );
99 // We do not validate whether a cached message is up-to-date.
100 // Instead, changes to messages will send us a purge.
101 // When cache is not purged or expired, it must be used.
102 $blob = $blobStore->getBlob( $module, 'en' );
103 $this->assertEquals( '{"example":"First version"}', $blob, 'Reuse cached v1 blob' );
106 $blobStore->updateMessage( 'example' );
110 $blob = $blobStore->getBlob( $module, 'en' );
111 $this->assertEquals( '{"example":"Second version"}', $blob, 'Updated blob for v2' );
114 public function testPurgeEverything() {
115 $module = $this->makeModule( [ 'example' ] );
116 $rl = new EmptyResourceLoader();
117 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
118 // Advance this new WANObjectCache instance to a normal state.
119 $blobStore->getBlob( $module, 'en' );
122 // Arrange version 1 and 2
123 $blobStore->expects( $this->exactly( 2 ) )
124 ->method( 'fetchMessage' )
125 ->will( $this->onConsecutiveCalls( 'First', 'Second' ) );
128 $blob = $blobStore->getBlob( $module, 'en' );
129 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1' );
134 $blob = $blobStore->getBlob( $module, 'en' );
135 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1 again' );
142 $blob = $blobStore->getBlob( $module, 'en' );
143 $this->assertEquals( '{"example":"Second"}', $blob, 'Blob for v2' );
146 public function testValidateAgainstModuleRegistry() {
147 // Arrange version 1 of a module
148 $module = $this->makeModule( [ 'foo' ] );
149 $rl = new EmptyResourceLoader();
150 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
151 $blobStore->expects( $this->once() )
152 ->method( 'fetchMessage' )
153 ->will( $this->returnValueMap( [
154 // message key, language code, message value
155 [ 'foo', 'en', 'Hello' ],
159 $blob = $blobStore->getBlob( $module, 'en' );
160 $this->assertEquals( '{"foo":"Hello"}', $blob, 'Blob for v1' );
162 // Arrange version 2 of module
163 // While message values may be out of date, the set of messages returned
164 // must always match the set of message keys required by the module.
165 // We do not receive purges for this because no messages were changed.
166 $module = $this->makeModule( [ 'foo', 'bar' ] );
167 $rl = new EmptyResourceLoader();
168 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
169 $blobStore->expects( $this->exactly( 2 ) )
170 ->method( 'fetchMessage' )
171 ->will( $this->returnValueMap( [
172 // message key, language code, message value
173 [ 'foo', 'en', 'Hello' ],
174 [ 'bar', 'en', 'World' ],
178 $blob = $blobStore->getBlob( $module, 'en' );
179 $this->assertEquals( '{"foo":"Hello","bar":"World"}', $blob, 'Blob for v2' );
182 public function testSetLoggedIsVoid() {
183 $blobStore = $this->makeBlobStore();
184 $this->assertSame( null, $blobStore->setLogger( new Psr\Log\
NullLogger() ) );
187 private function makeBlobStore( $methods = null, $rl = null ) {
188 $blobStore = $this->getMockBuilder( MessageBlobStore
::class )
189 ->setConstructorArgs( [ $rl ??
$this->createMock( ResourceLoader
::class ) ] )
190 ->setMethods( $methods )
193 $access = TestingAccessWrapper
::newFromObject( $blobStore );
194 $access->wanCache
= $this->wanCache
;
198 private function makeModule( array $messages ) {
199 $module = new ResourceLoaderTestModule( [ 'messages' => $messages ] );
200 $module->setName( self
::NAME
);