1<?php
2
3use MediaWiki\MediaWikiServices;
4
5/**
6 * @covers MediaWiki\Interwiki\ClassicInterwikiLookup
7 * @group Database
8 */
9class ClassicInterwikiLookupTest extends MediaWikiIntegrationTestCase {
10
11	private function populateDB( $iwrows ) {
12		$dbw = wfGetDB( DB_MASTER );
13		$dbw->delete( 'interwiki', '*', __METHOD__ );
14		$dbw->insert( 'interwiki', array_values( $iwrows ), __METHOD__ );
15		$this->tablesUsed[] = 'interwiki';
16	}
17
18	public function testDatabaseStorage() {
19		// NOTE: database setup is expensive, so we only do
20		//  it once and run all the tests in one go.
21		$dewiki = [
22			'iw_prefix' => 'de',
23			'iw_url' => 'http://de.wikipedia.org/wiki/',
24			'iw_api' => 'http://de.wikipedia.org/w/api.php',
25			'iw_wikiid' => 'dewiki',
26			'iw_local' => 1,
27			'iw_trans' => 0
28		];
29
30		$zzwiki = [
31			'iw_prefix' => 'zz',
32			'iw_url' => 'http://zzwiki.org/wiki/',
33			'iw_api' => 'http://zzwiki.org/w/api.php',
34			'iw_wikiid' => 'zzwiki',
35			'iw_local' => 0,
36			'iw_trans' => 0
37		];
38
39		$this->populateDB( [ $dewiki, $zzwiki ] );
40		$lookup = new \MediaWiki\Interwiki\ClassicInterwikiLookup(
41			MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'en' ),
42			WANObjectCache::newEmpty(),
43			MediaWikiServices::getInstance()->getHookContainer(),
44			60 * 60,
45			false,
46			3,
47			'en'
48		);
49
50		$this->assertEquals(
51			[ $dewiki, $zzwiki ],
52			$lookup->getAllPrefixes(),
53			'getAllPrefixes()'
54		);
55		$this->assertEquals(
56			[ $dewiki ],
57			$lookup->getAllPrefixes( true ),
58			'getAllPrefixes()'
59		);
60		$this->assertEquals(
61			[ $zzwiki ],
62			$lookup->getAllPrefixes( false ),
63			'getAllPrefixes()'
64		);
65
66		$this->assertTrue( $lookup->isValidInterwiki( 'de' ), 'known prefix is valid' );
67		$this->assertFalse( $lookup->isValidInterwiki( 'xyz' ), 'unknown prefix is valid' );
68
69		$this->assertNull( $lookup->fetch( null ), 'no prefix' );
70		$this->assertFalse( $lookup->fetch( 'xyz' ), 'unknown prefix' );
71
72		$interwiki = $lookup->fetch( 'de' );
73		$this->assertInstanceOf( Interwiki::class, $interwiki );
74		$this->assertSame( $interwiki, $lookup->fetch( 'de' ), 'in-process caching' );
75
76		$this->assertSame( 'http://de.wikipedia.org/wiki/', $interwiki->getURL(), 'getURL' );
77		$this->assertSame( 'http://de.wikipedia.org/w/api.php', $interwiki->getAPI(), 'getAPI' );
78		$this->assertSame( 'dewiki', $interwiki->getWikiID(), 'getWikiID' );
79		$this->assertSame( true, $interwiki->isLocal(), 'isLocal' );
80		$this->assertSame( false, $interwiki->isTranscludable(), 'isTranscludable' );
81
82		$lookup->invalidateCache( 'de' );
83		$this->assertNotSame( $interwiki, $lookup->fetch( 'de' ), 'invalidate cache' );
84	}
85
86	/**
87	 * @param string $thisSite
88	 * @param string[] $local
89	 * @param string[] $global
90	 *
91	 * @return string[]
92	 */
93	private function populateHash( $thisSite, $local, $global ) {
94		$hash = [];
95		$hash[ '__sites:' . wfWikiID() ] = $thisSite;
96
97		$globals = [];
98		$locals = [];
99
100		foreach ( $local as $row ) {
101			$prefix = $row['iw_prefix'];
102			$data = $row['iw_local'] . ' ' . $row['iw_url'];
103			$locals[] = $prefix;
104			$hash[ "_{$thisSite}:{$prefix}" ] = $data;
105		}
106
107		foreach ( $global as $row ) {
108			$prefix = $row['iw_prefix'];
109			$data = $row['iw_local'] . ' ' . $row['iw_url'];
110			$globals[] = $prefix;
111			$hash[ "__global:{$prefix}" ] = $data;
112		}
113
114		$hash[ '__list:__global' ] = implode( ' ', $globals );
115		$hash[ '__list:_' . $thisSite ] = implode( ' ', $locals );
116
117		return $hash;
118	}
119
120	private function populateCDB( $thisSite, $local, $global ) {
121		$cdbFile = $this->getNewTempFile();
122		$cdb = \Cdb\Writer::open( $cdbFile );
123
124		$hash = $this->populateHash( $thisSite, $local, $global );
125
126		foreach ( $hash as $key => $value ) {
127			$cdb->set( $key, $value );
128		}
129
130		$cdb->close();
131		return $cdbFile;
132	}
133
134	public function testCDBStorage() {
135		// NOTE: CDB setup is expensive, so we only do
136		//  it once and run all the tests in one go.
137
138		$zzwiki = [
139			'iw_prefix' => 'zz',
140			'iw_url' => 'http://zzwiki.org/wiki/',
141			'iw_local' => 0
142		];
143
144		$dewiki = [
145			'iw_prefix' => 'de',
146			'iw_url' => 'http://de.wikipedia.org/wiki/',
147			'iw_local' => 1
148		];
149
150		$cdbFile = $this->populateCDB(
151			'en',
152			[ $dewiki ],
153			[ $zzwiki ]
154		);
155		$lookup = new \MediaWiki\Interwiki\ClassicInterwikiLookup(
156			MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'en' ),
157			WANObjectCache::newEmpty(),
158			MediaWikiServices::getInstance()->getHookContainer(),
159			60 * 60,
160			$cdbFile,
161			3,
162			'en'
163		);
164
165		$this->assertEquals(
166			[ $zzwiki, $dewiki ],
167			$lookup->getAllPrefixes(),
168			'getAllPrefixes()'
169		);
170
171		$this->assertTrue( $lookup->isValidInterwiki( 'de' ), 'known prefix is valid' );
172		$this->assertTrue( $lookup->isValidInterwiki( 'zz' ), 'known prefix is valid' );
173
174		$interwiki = $lookup->fetch( 'de' );
175		$this->assertInstanceOf( Interwiki::class, $interwiki );
176
177		$this->assertSame( 'http://de.wikipedia.org/wiki/', $interwiki->getURL(), 'getURL' );
178		$this->assertSame( true, $interwiki->isLocal(), 'isLocal' );
179
180		$interwiki = $lookup->fetch( 'zz' );
181		$this->assertInstanceOf( Interwiki::class, $interwiki );
182
183		$this->assertSame( 'http://zzwiki.org/wiki/', $interwiki->getURL(), 'getURL' );
184		$this->assertSame( false, $interwiki->isLocal(), 'isLocal' );
185
186		// cleanup temp file
187		unlink( $cdbFile );
188	}
189
190	public function testArrayStorage() {
191		$zzwiki = [
192			'iw_prefix' => 'zz',
193			'iw_url' => 'http://zzwiki.org/wiki/',
194			'iw_local' => 0
195		];
196		$dewiki = [
197			'iw_prefix' => 'de',
198			'iw_url' => 'http://de.wikipedia.org/wiki/',
199			'iw_local' => 1
200		];
201
202		$hash = $this->populateHash(
203			'en',
204			[ $dewiki ],
205			[ $zzwiki ]
206		);
207		$lookup = new \MediaWiki\Interwiki\ClassicInterwikiLookup(
208			MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'en' ),
209			WANObjectCache::newEmpty(),
210			MediaWikiServices::getInstance()->getHookContainer(),
211			60 * 60,
212			$hash,
213			3,
214			'en'
215		);
216
217		$this->assertEquals(
218			[ $zzwiki, $dewiki ],
219			$lookup->getAllPrefixes(),
220			'getAllPrefixes()'
221		);
222
223		$this->assertTrue( $lookup->isValidInterwiki( 'de' ), 'known prefix is valid' );
224		$this->assertTrue( $lookup->isValidInterwiki( 'zz' ), 'known prefix is valid' );
225
226		$interwiki = $lookup->fetch( 'de' );
227		$this->assertInstanceOf( Interwiki::class, $interwiki );
228
229		$this->assertSame( 'http://de.wikipedia.org/wiki/', $interwiki->getURL(), 'getURL' );
230		$this->assertSame( true, $interwiki->isLocal(), 'isLocal' );
231
232		$interwiki = $lookup->fetch( 'zz' );
233		$this->assertInstanceOf( Interwiki::class, $interwiki );
234
235		$this->assertSame( 'http://zzwiki.org/wiki/', $interwiki->getURL(), 'getURL' );
236		$this->assertSame( false, $interwiki->isLocal(), 'isLocal' );
237	}
238
239	public function testGetAllPrefixes() {
240		$zz = [
241			'iw_prefix' => 'zz',
242			'iw_url' => 'https://azz.example.org/',
243			'iw_local' => 1
244		];
245		$de = [
246			'iw_prefix' => 'de',
247			'iw_url' => 'https://de.example.org/',
248			'iw_local' => 1
249		];
250		$azz = [
251			'iw_prefix' => 'azz',
252			'iw_url' => 'https://azz.example.org/',
253			'iw_local' => 1
254		];
255
256		$hash = $this->populateHash(
257			'en',
258			[],
259			[ $zz, $de, $azz ]
260		);
261		$lookup = new \MediaWiki\Interwiki\ClassicInterwikiLookup(
262			MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'en' ),
263			WANObjectCache::newEmpty(),
264			MediaWikiServices::getInstance()->getHookContainer(),
265			60 * 60,
266			$hash,
267			3,
268			'en'
269		);
270
271		$this->assertEquals(
272			[ $zz, $de, $azz ],
273			$lookup->getAllPrefixes(),
274			'getAllPrefixes() - preserves order'
275		);
276	}
277
278}
279