1<?php
2
3namespace MediaWiki\Tests\User\CentralId;
4
5use CentralIdLookup;
6use InvalidArgumentException;
7use LogicException;
8use MediaWiki\Permissions\Authority;
9use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
10use MediaWiki\User\UserIdentity;
11use MediaWiki\User\UserIdentityLookup;
12use MediaWiki\User\UserIdentityValue;
13use MediaWikiUnitTestCase;
14use User;
15use Wikimedia\TestingAccessWrapper;
16
17/**
18 * @covers CentralIdLookup
19 */
20class CentralIdLookupTest extends MediaWikiUnitTestCase {
21	use MockAuthorityTrait;
22
23	public function testGetProviderId() {
24		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
25		$mock->init( 'this is a test', $this->createNoOpMock( UserIdentityLookup::class ) );
26		$this->assertSame( 'this is a test', $mock->getProviderId() );
27	}
28
29	public function testRepeatingInitThrows() {
30		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
31		$mock->init( 'foo', $this->createNoOpMock( UserIdentityLookup::class ) );
32		$this->expectException( LogicException::class );
33		$mock->init( 'bar', $this->createNoOpMock( UserIdentityLookup::class ) );
34	}
35
36	public function testCheckAudience() {
37		$mock = TestingAccessWrapper::newFromObject(
38			$this->getMockForAbstractClass( CentralIdLookup::class )
39		);
40
41		$authority = $this->mockAnonUltimateAuthority();
42		$this->assertSame( $authority, $mock->checkAudience( $authority ) );
43
44		$authority = $mock->checkAudience( CentralIdLookup::AUDIENCE_PUBLIC );
45		$this->assertInstanceOf( Authority::class, $authority );
46		// Should be an empty User object, don't trigger loading that requires integration
47		// if a user is created from "defaults" then its an anonymous default user
48		$this->assertInstanceOf( User::class, $authority );
49		$this->assertSame( 'defaults', $authority->mFrom );
50
51		$this->assertNull( $mock->checkAudience( CentralIdLookup::AUDIENCE_RAW ) );
52
53		try {
54			$mock->checkAudience( 100 );
55			$this->fail( 'Expected exception not thrown' );
56		} catch ( InvalidArgumentException $ex ) {
57			$this->assertSame( 'Invalid audience', $ex->getMessage() );
58		}
59	}
60
61	public function testNameFromCentralId() {
62		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
63		$mock->expects( $this->once() )->method( 'lookupCentralIds' )
64			->with(
65				[ 15 => null ],
66				CentralIdLookup::AUDIENCE_RAW,
67				CentralIdLookup::READ_LATEST
68			)
69			->willReturn( [ 15 => 'FooBar' ] );
70
71		$this->assertSame(
72			'FooBar',
73			$mock->nameFromCentralId( 15, CentralIdLookup::AUDIENCE_RAW, CentralIdLookup::READ_LATEST )
74		);
75	}
76
77	/**
78	 * @dataProvider provideLocalUserFromCentralId
79	 * @param string $name
80	 * @param bool $succeeds
81	 */
82	public function testLocalUserFromCentralId( $name, $succeeds ) {
83		// UserIdentityLookup expects to be asked for a UserIdentity with the
84		// name $name, and will return one only if its for UTSysop, no other
85		// users are registered
86		$lookupResult = ( $name === 'UTSysop' ? ( new UserIdentityValue( 5, $name ) ) : null );
87		$userIdentityLookup = $this->createMock( UserIdentityLookup::class );
88		$userIdentityLookup->method( 'getUserIdentityByName' )
89			->with( $name )
90			->willReturn( $lookupResult );
91
92		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
93		$mock->method( 'isAttached' )
94			->willReturn( true );
95		$mock->expects( $this->once() )->method( 'lookupCentralIds' )
96			->with(
97				[ 42 => null ],
98				CentralIdLookup::AUDIENCE_RAW,
99				CentralIdLookup::READ_LATEST
100			)
101			->willReturn( [ 42 => $name ] );
102
103		$mock->init( 'test', $userIdentityLookup );
104		$user = $mock->localUserFromCentralId(
105			42, CentralIdLookup::AUDIENCE_RAW, CentralIdLookup::READ_LATEST
106		);
107		if ( $succeeds ) {
108			$this->assertInstanceOf( UserIdentity::class, $user );
109			$this->assertSame( $name, $user->getName() );
110		} else {
111			$this->assertNull( $user );
112		}
113
114		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
115		$mock->method( 'isAttached' )
116			->willReturn( false );
117		$mock->expects( $this->once() )->method( 'lookupCentralIds' )
118			->with(
119				[ 42 => null ],
120				CentralIdLookup::AUDIENCE_RAW,
121				CentralIdLookup::READ_LATEST
122			)
123			->willReturn( [ 42 => $name ] );
124		$mock->init( 'test', $userIdentityLookup );
125
126		$this->assertNull(
127			$mock->localUserFromCentralId( 42, CentralIdLookup::AUDIENCE_RAW, CentralIdLookup::READ_LATEST )
128		);
129	}
130
131	public static function provideLocalUserFromCentralId() {
132		return [
133			[ 'UTSysop', true ],
134			[ 'UTDoesNotExist', false ],
135			[ null, false ],
136			[ '', false ],
137			[ '<X>', false ],
138		];
139	}
140
141	public function testCentralIdFromName() {
142		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
143		$mock->expects( $this->once() )->method( 'lookupUserNames' )
144			->with(
145				[ 'FooBar' => 0 ],
146				CentralIdLookup::AUDIENCE_RAW,
147				CentralIdLookup::READ_LATEST
148			)
149			->willReturn( [ 'FooBar' => 23 ] );
150
151		$this->assertSame(
152			23,
153			$mock->centralIdFromName( 'FooBar', CentralIdLookup::AUDIENCE_RAW, CentralIdLookup::READ_LATEST )
154		);
155	}
156
157	public function testCentralIdFromLocalUser() {
158		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
159		$mock->method( 'isAttached' )
160			->willReturn( true );
161		$mock->expects( $this->once() )->method( 'lookupUserNames' )
162			->with(
163				[ 'FooBar' => 0 ],
164				CentralIdLookup::AUDIENCE_RAW,
165				CentralIdLookup::READ_LATEST
166			)
167			->willReturn( [ 'FooBar' => 23 ] );
168
169		$this->assertSame(
170			23,
171			$mock->centralIdFromLocalUser(
172				new UserIdentityValue( 10, 'FooBar' ),
173				CentralIdLookup::AUDIENCE_RAW,
174				CentralIdLookup::READ_LATEST
175			)
176		);
177
178		$mock = $this->getMockForAbstractClass( CentralIdLookup::class );
179		$mock->method( 'isAttached' )
180			->willReturn( false );
181		$mock->expects( $this->never() )->method( 'lookupUserNames' );
182
183		$this->assertSame(
184			0,
185			$mock->centralIdFromLocalUser(
186				new UserIdentityValue( 10, 'FooBar' ),
187				CentralIdLookup::AUDIENCE_RAW,
188				CentralIdLookup::READ_LATEST
189			)
190		);
191	}
192
193}
194