[ 'id' => 1, 'username' => 'alice', 'password_hash' => Password::hash('pw'), 'role' => 'operator', 'site_scope' => null, 'failed_attempts' => 0, 'locked_until' => 0, ]]); } private function req(string $u, string $p): Request { return new Request('POST', '/login', [], '', [], ['username' => $u, 'password' => $p]); } public function testLoginSuccessSetsSession(): void { $c = new AuthController($this->users(), new InMemoryAuditRepository()); $sess = new ArraySessionStore(); $res = $c->login($this->req('alice', 'pw'), $sess, now: 1000); $this->assertSame(200, $res->status()); $this->assertSame(1, $sess->get('user_id')); $this->assertSame('operator', $sess->get('role')); } public function testBadPasswordIs401(): void { $c = new AuthController($this->users(), new InMemoryAuditRepository()); $this->assertSame(401, $c->login($this->req('alice', 'nope'), new ArraySessionStore(), now: 1000)->status()); } public function testLockoutAfterMaxAttempts(): void { $repo = $this->users(); $c = new AuthController($repo, new InMemoryAuditRepository(), maxAttempts: 3, lockSeconds: 900); for ($i = 0; $i < 3; $i++) $c->login($this->req('alice', 'bad'), new ArraySessionStore(), now: 1000); // now locked, even correct password is 423 $this->assertSame(423, $c->login($this->req('alice', 'pw'), new ArraySessionStore(), now: 1000)->status()); } }