Merge "rdbms: avoid LoadBalancer::getConnection waste when using $groups"
[lhc/web/wiklou.git] / tests / phpunit / includes / resourceloader / MessageBlobStoreTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 /**
6 * @group ResourceLoader
7 * @covers MessageBlobStore
8 */
9 class MessageBlobStoreTest extends PHPUnit\Framework\TestCase {
10
11 use MediaWikiCoversValidator;
12 use PHPUnit4And6Compat;
13
14 protected function setUp() {
15 parent::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()
20 ] );
21
22 $this->clock = 1301655600.000;
23 $this->wanCache->setMockTime( $this->clock );
24 }
25
26 public function testBlobCreation() {
27 $module = $this->makeModule( [ 'mainpage' ] );
28 $rl = new EmptyResourceLoader();
29 $rl->register( $module->getName(), $module );
30
31 $blobStore = $this->makeBlobStore( null, $rl );
32 $blob = $blobStore->getBlob( $module, 'en' );
33
34 $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
35 }
36
37 public function testBlobCreation_empty() {
38 $module = $this->makeModule( [] );
39 $rl = new EmptyResourceLoader();
40 $rl->register( $module->getName(), $module );
41
42 $blobStore = $this->makeBlobStore( null, $rl );
43 $blob = $blobStore->getBlob( $module, 'en' );
44
45 $this->assertEquals( '{}', $blob, 'Generated blob' );
46 }
47
48 public function testBlobCreation_unknownMessage() {
49 $module = $this->makeModule( [ 'i-dont-exist', 'mainpage', 'i-dont-exist2' ] );
50 $rl = new EmptyResourceLoader();
51 $rl->register( $module->getName(), $module );
52 $blobStore = $this->makeBlobStore( null, $rl );
53
54 // Generating a blob should continue without errors,
55 // with keys of unknown messages excluded from the blob.
56 $blob = $blobStore->getBlob( $module, 'en' );
57 $this->assertEquals( '{"mainpage":"Main Page"}', $blob, 'Generated blob' );
58 }
59
60 public function testMessageCachingAndPurging() {
61 $module = $this->makeModule( [ 'example' ] );
62 $rl = new EmptyResourceLoader();
63 $rl->register( $module->getName(), $module );
64 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
65
66 // Advance this new WANObjectCache instance to a normal state,
67 // by doing one "get" and letting its hold off period expire.
68 // Without this, the first real "get" would lazy-initialise the
69 // checkKey and thus reject the first "set".
70 $blobStore->getBlob( $module, 'en' );
71 $this->clock += 20;
72
73 // Arrange version 1 of a message
74 $blobStore->expects( $this->once() )
75 ->method( 'fetchMessage' )
76 ->will( $this->returnValue( 'First version' ) );
77
78 // Assert
79 $blob = $blobStore->getBlob( $module, 'en' );
80 $this->assertEquals( '{"example":"First version"}', $blob, 'Blob for v1' );
81
82 // Arrange version 2
83 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
84 $blobStore->expects( $this->once() )
85 ->method( 'fetchMessage' )
86 ->will( $this->returnValue( 'Second version' ) );
87 $this->clock += 20;
88
89 // Assert
90 // We do not validate whether a cached message is up-to-date.
91 // Instead, changes to messages will send us a purge.
92 // When cache is not purged or expired, it must be used.
93 $blob = $blobStore->getBlob( $module, 'en' );
94 $this->assertEquals( '{"example":"First version"}', $blob, 'Reuse cached v1 blob' );
95
96 // Purge cache
97 $blobStore->updateMessage( 'example' );
98 $this->clock += 20;
99
100 // Assert
101 $blob = $blobStore->getBlob( $module, 'en' );
102 $this->assertEquals( '{"example":"Second version"}', $blob, 'Updated blob for v2' );
103 }
104
105 public function testPurgeEverything() {
106 $module = $this->makeModule( [ 'example' ] );
107 $rl = new EmptyResourceLoader();
108 $rl->register( $module->getName(), $module );
109 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
110 // Advance this new WANObjectCache instance to a normal state.
111 $blobStore->getBlob( $module, 'en' );
112 $this->clock += 20;
113
114 // Arrange version 1 and 2
115 $blobStore->expects( $this->exactly( 2 ) )
116 ->method( 'fetchMessage' )
117 ->will( $this->onConsecutiveCalls( 'First', 'Second' ) );
118
119 // Assert
120 $blob = $blobStore->getBlob( $module, 'en' );
121 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1' );
122
123 $this->clock += 20;
124
125 // Assert
126 $blob = $blobStore->getBlob( $module, 'en' );
127 $this->assertEquals( '{"example":"First"}', $blob, 'Blob for v1 again' );
128
129 // Purge everything
130 $blobStore->clear();
131 $this->clock += 20;
132
133 // Assert
134 $blob = $blobStore->getBlob( $module, 'en' );
135 $this->assertEquals( '{"example":"Second"}', $blob, 'Blob for v2' );
136 }
137
138 public function testValidateAgainstModuleRegistry() {
139 // Arrange version 1 of a module
140 $module = $this->makeModule( [ 'foo' ] );
141 $rl = new EmptyResourceLoader();
142 $rl->register( $module->getName(), $module );
143 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
144 $blobStore->expects( $this->once() )
145 ->method( 'fetchMessage' )
146 ->will( $this->returnValueMap( [
147 // message key, language code, message value
148 [ 'foo', 'en', 'Hello' ],
149 ] ) );
150
151 // Assert
152 $blob = $blobStore->getBlob( $module, 'en' );
153 $this->assertEquals( '{"foo":"Hello"}', $blob, 'Blob for v1' );
154
155 // Arrange version 2 of module
156 // While message values may be out of date, the set of messages returned
157 // must always match the set of message keys required by the module.
158 // We do not receive purges for this because no messages were changed.
159 $module = $this->makeModule( [ 'foo', 'bar' ] );
160 $rl = new EmptyResourceLoader();
161 $rl->register( $module->getName(), $module );
162 $blobStore = $this->makeBlobStore( [ 'fetchMessage' ], $rl );
163 $blobStore->expects( $this->exactly( 2 ) )
164 ->method( 'fetchMessage' )
165 ->will( $this->returnValueMap( [
166 // message key, language code, message value
167 [ 'foo', 'en', 'Hello' ],
168 [ 'bar', 'en', 'World' ],
169 ] ) );
170
171 // Assert
172 $blob = $blobStore->getBlob( $module, 'en' );
173 $this->assertEquals( '{"foo":"Hello","bar":"World"}', $blob, 'Blob for v2' );
174 }
175
176 public function testSetLoggedIsVoid() {
177 $blobStore = $this->makeBlobStore();
178 $this->assertSame( null, $blobStore->setLogger( new Psr\Log\NullLogger() ) );
179 }
180
181 private function makeBlobStore( $methods = null, $rl = null ) {
182 $blobStore = $this->getMockBuilder( MessageBlobStore::class )
183 ->setConstructorArgs( [ $rl ?? $this->createMock( ResourceLoader::class ) ] )
184 ->setMethods( $methods )
185 ->getMock();
186
187 $access = TestingAccessWrapper::newFromObject( $blobStore );
188 $access->wanCache = $this->wanCache;
189 return $blobStore;
190 }
191
192 private function makeModule( array $messages ) {
193 $module = new ResourceLoaderTestModule( [ 'messages' => $messages ] );
194 $module->setName( 'test.blobstore' );
195 return $module;
196 }
197 }