3 use Wikimedia\TestingAccessWrapper;
10 'host' =>
'etcd-tcp.example.net',
14 ->setMethods( [
'fetchAllFromEtcd' ] )
20 $mock->expects( $this->once() )->method(
'fetchAllFromEtcd' )
36 $this->assertSame(
true, $config->has(
'known' ) );
47 $this->assertSame(
'value', $config->get(
'known' ) );
57 $this->assertSame(
false, $config->has(
'unknown' ) );
68 $config->get(
'unknown' );
76 ->setMethods( [
'get' ] )
78 $cache->expects( $this->once() )->method(
'get' )
80 'config' => [
'known' =>
'from-cache' ],
85 $this->assertSame(
'from-cache', $config->get(
'known' ) );
95 $config->expects( $this->once() )->method(
'fetchAllFromEtcd' )
97 [
'known' =>
'from-fetch' ],
102 $this->assertSame(
'from-fetch', $config->get(
'known' ) );
153 ->setMethods( [
'get',
'lock' ] )
156 $cache->expects( $this->once() )->method(
'get' )
157 ->willReturn(
false );
159 $cache->expects( $this->once() )->method(
'lock' )
160 ->willReturn(
true );
166 $mock->expects( $this->once() )->method(
'fetchAllFromEtcd' )
167 ->willReturn( [ [
'known' =>
'from-fetch' ],
null,
false ] );
169 $this->assertSame(
'from-fetch', $mock->get(
'known' ) );
178 ->setMethods( [
'get',
'lock' ] )
181 $cache->expects( $this->once() )->method(
'get' )
182 ->willReturn(
false );
184 $cache->expects( $this->once() )->method(
'lock' )
185 ->willReturn(
true );
191 $mock->expects( $this->once() )->method(
'fetchAllFromEtcd' )
192 ->willReturn( [
null,
'Fake error',
false ] );
204 ->setMethods( [
'get',
'lock' ] )
206 $cache->expects( $this->exactly( 2 ) )->method(
'get' )
207 ->will( $this->onConsecutiveCalls(
212 'config' => [
'known' =>
'from-cache' ],
217 $cache->expects( $this->once() )->method(
'lock' )
218 ->willReturn(
false );
224 $mock->expects( $this->never() )->method(
'fetchAllFromEtcd' );
226 $this->assertSame(
'from-cache', $mock->get(
'known' ) );
235 ->setMethods( [
'get',
'lock' ] )
237 $cache->expects( $this->once() )->method(
'get' )
240 'config' => [
'known' =>
'from-cache' ],
243 $cache->expects( $this->never() )->method(
'lock' );
249 $mock->expects( $this->never() )->method(
'fetchAllFromEtcd' );
251 $this->assertSame(
'from-cache', $mock->get(
'known' ) );
260 ->setMethods( [
'get',
'lock' ] )
262 $cache->expects( $this->once() )->method(
'get' )
265 'config' => [
'known' =>
'from-cache' ],
268 $cache->expects( $this->never() )->method(
'lock' );
274 $mock->expects( $this->never() )->method(
'fetchAllFromEtcd' );
276 $this->assertSame(
'from-cache', $mock->get(
'known' ),
'Cache hit' );
277 $this->assertSame(
'from-cache', $mock->get(
'known' ),
'Process cache hit' );
286 ->setMethods( [
'get',
'lock' ] )
288 $cache->expects( $this->once() )->method(
'get' )->willReturn(
291 'config' => [
'known' =>
'from-cache-expired' ],
296 $cache->expects( $this->once() )->method(
'lock' )
297 ->willReturn(
true );
303 $mock->expects( $this->once() )->method(
'fetchAllFromEtcd' )
304 ->willReturn( [ [
'known' =>
'from-fetch' ],
null,
false ] );
306 $this->assertSame(
'from-fetch', $mock->get(
'known' ) );
315 ->setMethods( [
'get',
'lock' ] )
317 $cache->expects( $this->once() )->method(
'get' )->willReturn(
320 'config' => [
'known' =>
'from-cache-expired' ],
325 $cache->expects( $this->once() )->method(
'lock' )
326 ->willReturn(
true );
332 $mock->expects( $this->once() )->method(
'fetchAllFromEtcd' )
333 ->willReturn( [
null,
'Fake failure',
true ] );
335 $this->assertSame(
'from-cache-expired', $mock->get(
'known' ) );
344 ->setMethods( [
'get',
'lock' ] )
346 $cache->expects( $this->once() )->method(
'get' )
349 'config' => [
'known' =>
'from-cache-expired' ],
353 $cache->expects( $this->once() )->method(
'lock' )
354 ->willReturn(
false );
360 $mock->expects( $this->never() )->method(
'fetchAllFromEtcd' );
362 $this->assertSame(
'from-cache-expired', $mock->get(
'known' ) );
367 '200 OK - Success' => [
372 'body' => json_encode( [
'node' => [
'nodes' => [
374 'key' =>
'/example/foo',
375 'value' => json_encode( [
'val' =>
true ] )
386 '200 OK - Empty dir' => [
391 'body' => json_encode( [
'node' => [
'nodes' => [
393 'key' =>
'/example/foo',
394 'value' => json_encode( [
'val' =>
true ] )
397 'key' =>
'/example/sub',
402 'key' =>
'/example/bar',
403 'value' => json_encode( [
'val' =>
false ] )
409 [
'foo' =>
true,
'bar' =>
false ],
414 '200 OK - Recursive' => [
419 'body' => json_encode( [
'node' => [
'nodes' => [
421 'key' =>
'/example/a',
426 'value' => json_encode( [
'val' =>
true ] ),
430 'value' => json_encode( [
'val' =>
false ] ),
438 [
'a/b' =>
true,
'a/c' =>
false ],
443 '200 OK - Missing nodes at second level' => [
448 'body' => json_encode( [
'node' => [
'nodes' => [
450 'key' =>
'/example/a',
458 "Unexpected JSON response in dir 'a'; missing 'nodes' list.",
462 '200 OK - Correctly encoded garbage response' => [
467 'body' => json_encode( [
'foo' =>
'bar' ] ),
472 "Unexpected JSON response: Missing or invalid node at top level.",
476 '200 OK - Bad value' => [
481 'body' => json_encode( [
'node' => [
'nodes' => [
483 'key' =>
'/example/foo',
484 'value' =>
';"broken{value'
491 "Failed to parse value for 'foo'.",
495 '200 OK - Empty node list' => [
500 'body' =>
'{"node":{"nodes":[]}}',
509 '200 OK - Invalid JSON' => [
513 'headers' => [
'content-length' => 0 ],
515 'error' =>
'(curl error: no status set)',
519 "Error unserializing JSON response.",
526 'reason' =>
'Not Found',
527 'headers' => [
'content-length' => 0 ],
533 'HTTP 404 (Not Found)',
537 '400 Bad Request - custom error' => [
540 'reason' =>
'Bad Request',
541 'headers' => [
'content-length' => 0 ],
543 'error' =>
'No good reason',
564 ->disableOriginalConstructor()
566 $http->expects( $this->once() )->method(
'run' )
567 ->willReturn( array_values( $httpResponse ) );
570 ->disableOriginalConstructor()
573 $conf = TestingAccessWrapper::newFromObject( $conf );
578 $conf->fetchAllFromEtcdServer(
'etcd-tcp.example.net' )