/**
  * @dataProvider provideAccountLink
  * @param StatusValue $preTest
  * @param array $primaryResponses
  * @param array $managerResponses
  */
 public function testAccountLink(StatusValue $preTest, array $primaryResponses, array $managerResponses)
 {
     $user = \User::newFromName('UTSysop');
     $this->initializeManager();
     // Set up lots of mocks...
     $req = $this->getMockForAbstractClass(AuthenticationRequest::class);
     $req->primary = $primaryResponses;
     $mocks = [];
     foreach (['pre', 'primary'] as $key) {
         $class = ucfirst($key) . 'AuthenticationProvider';
         $mocks[$key] = $this->getMockForAbstractClass("MediaWiki\\Auth\\{$class}", [], "Mock{$class}");
         $mocks[$key]->expects($this->any())->method('getUniqueId')->will($this->returnValue($key));
         for ($i = 2; $i <= 3; $i++) {
             $mocks[$key . $i] = $this->getMockForAbstractClass("MediaWiki\\Auth\\{$class}", [], "Mock{$class}");
             $mocks[$key . $i]->expects($this->any())->method('getUniqueId')->will($this->returnValue($key . $i));
         }
     }
     $mocks['pre']->expects($this->any())->method('testForAccountLink')->will($this->returnCallback(function ($u) use($user, $preTest) {
         $this->assertSame($user->getId(), $u->getId());
         $this->assertSame($user->getName(), $u->getName());
         return $preTest;
     }));
     $mocks['pre2']->expects($this->atMost(1))->method('testForAccountLink')->will($this->returnValue(StatusValue::newGood()));
     $mocks['primary']->expects($this->any())->method('accountCreationType')->will($this->returnValue(PrimaryAuthenticationProvider::TYPE_LINK));
     $ct = count($req->primary);
     $callback = $this->returnCallback(function ($u, $reqs) use($user, $req) {
         $this->assertSame($user->getId(), $u->getId());
         $this->assertSame($user->getName(), $u->getName());
         $foundReq = false;
         foreach ($reqs as $r) {
             $this->assertSame($user->getName(), $r->username);
             $foundReq = $foundReq || get_class($r) === get_class($req);
         }
         $this->assertTrue($foundReq, '$reqs contains $req');
         return array_shift($req->primary);
     });
     $mocks['primary']->expects($this->exactly(min(1, $ct)))->method('beginPrimaryAccountLink')->will($callback);
     $mocks['primary']->expects($this->exactly(max(0, $ct - 1)))->method('continuePrimaryAccountLink')->will($callback);
     $abstain = AuthenticationResponse::newAbstain();
     $mocks['primary2']->expects($this->any())->method('accountCreationType')->will($this->returnValue(PrimaryAuthenticationProvider::TYPE_LINK));
     $mocks['primary2']->expects($this->atMost(1))->method('beginPrimaryAccountLink')->will($this->returnValue($abstain));
     $mocks['primary2']->expects($this->never())->method('continuePrimaryAccountLink');
     $mocks['primary3']->expects($this->any())->method('accountCreationType')->will($this->returnValue(PrimaryAuthenticationProvider::TYPE_CREATE));
     $mocks['primary3']->expects($this->never())->method('beginPrimaryAccountLink');
     $mocks['primary3']->expects($this->never())->method('continuePrimaryAccountLink');
     $this->preauthMocks = [$mocks['pre'], $mocks['pre2']];
     $this->primaryauthMocks = [$mocks['primary3'], $mocks['primary2'], $mocks['primary']];
     $this->logger = new \TestLogger(true, function ($message, $level) {
         return $level === LogLevel::DEBUG ? null : $message;
     });
     $this->initializeManager(true);
     $constraint = \PHPUnit_Framework_Assert::logicalOr($this->equalTo(AuthenticationResponse::PASS), $this->equalTo(AuthenticationResponse::FAIL));
     $providers = array_merge($this->preauthMocks, $this->primaryauthMocks);
     foreach ($providers as $p) {
         $p->postCalled = false;
         $p->expects($this->atMost(1))->method('postAccountLink')->willReturnCallback(function ($user, $response) use($constraint, $p) {
             $this->assertInstanceOf('User', $user);
             $this->assertSame('UTSysop', $user->getName());
             $this->assertInstanceOf(AuthenticationResponse::class, $response);
             $this->assertThat($response->status, $constraint);
             $p->postCalled = $response->status;
         });
     }
     $first = true;
     $created = false;
     $expectLog = [];
     foreach ($managerResponses as $i => $response) {
         if ($response instanceof AuthenticationResponse && $response->status === AuthenticationResponse::PASS) {
             $expectLog[] = [LogLevel::INFO, 'Account linked to {user} by primary'];
         }
         $ex = null;
         try {
             if ($first) {
                 $ret = $this->manager->beginAccountLink($user, [$req], 'http://localhost/');
             } else {
                 $ret = $this->manager->continueAccountLink([$req]);
             }
             if ($response instanceof \Exception) {
                 $this->fail('Expected exception not thrown', "Response {$i}");
             }
         } catch (\Exception $ex) {
             if (!$response instanceof \Exception) {
                 throw $ex;
             }
             $this->assertEquals($response->getMessage(), $ex->getMessage(), "Response {$i}, exception");
             $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountLinkState'), "Response {$i}, exception, session state");
             return;
         }
         $this->assertSame('http://localhost/', $req->returnToUrl);
         $ret->message = $this->message($ret->message);
         $this->assertEquals($response, $ret, "Response {$i}, response");
         if ($response->status === AuthenticationResponse::PASS || $response->status === AuthenticationResponse::FAIL) {
             $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountLinkState'), "Response {$i}, session state");
             foreach ($providers as $p) {
                 $this->assertSame($response->status, $p->postCalled, "Response {$i}, post-auth callback called");
             }
         } else {
             $this->assertNotNull($this->request->getSession()->getSecret('AuthManager::accountLinkState'), "Response {$i}, session state");
             $this->assertEquals($ret->neededRequests, $this->manager->getAuthenticationRequests(AuthManager::ACTION_LINK_CONTINUE), "Response {$i}, continuation check");
             foreach ($providers as $p) {
                 $this->assertFalse($p->postCalled, "Response {$i}, post-auth callback not called");
             }
         }
         $first = false;
     }
     $this->assertSame($expectLog, $this->logger->getBuffer());
 }