assertInstanceOf( NullLogger::class, $tw->logger, 'Default logger is NullLogger' ); $loggerClass = TestLogger::class; $logger = $this->createMock( $loggerClass ); $tw->setLogger( $logger ); $this->assertInstanceOf( $loggerClass, $tw->logger, 'Logger can be changed' ); } /** * @covers AbuseFilterVariableHolder::newFromArray */ public function testNewFromArray() { $vars = [ 'foo' => 12, 'bar' => [ 'x', 'y' ], 'baz' => false ]; $actual = AbuseFilterVariableHolder::newFromArray( $vars ); $expected = new AbuseFilterVariableHolder(); foreach ( $vars as $var => $value ) { $expected->setVar( $var, $value ); } $this->assertEquals( $expected, $actual ); } /** * @covers AbuseFilterVariableHolder::setVar */ public function testVarsAreLowercased() { $vars = new AbuseFilterVariableHolder(); $this->assertCount( 0, $vars->getVars(), 'precondition' ); $vars->setVar( 'FOO', 42 ); $this->assertCount( 1, $vars->getVars(), 'variable should be set' ); $this->assertArrayHasKey( 'foo', $vars->getVars(), 'var should be lowercase' ); } /** * @param string $name * @param mixed $val * @param mixed $expected * * @dataProvider provideSetVar * * @covers AbuseFilterVariableHolder::setVar */ public function testSetVar( $name, $val, $expected ) { $vars = new AbuseFilterVariableHolder(); $vars->setVar( $name, $val ); $this->assertEquals( $expected, $vars->getVars()[$name] ); } public function provideSetVar() { yield 'native' => [ 'foo', 12, new AFPData( AFPData::DINT, 12 ) ]; $afpdata = new AFPData( AFPData::DSTRING, 'foobar' ); yield 'AFPData' => [ 'foo', $afpdata, $afpdata ]; $afcompvar = new AFComputedVariable( 'foo', [] ); yield 'AFComputedVariable' => [ 'foo', $afcompvar, $afcompvar ]; } /** * @covers AbuseFilterVariableHolder::getVars */ public function testGetVars() { $vars = new AbuseFilterVariableHolder(); $this->assertSame( [], $vars->getVars(), 'precondition' ); $vars->setVar( 'foo', [ true ] ); $vars->setVar( 'bar', 'bar' ); $exp = [ 'foo' => new AFPData( AFPData::DARRAY, [ new AFPData( AFPData::DBOOL, true ) ] ), 'bar' => new AFPData( AFPData::DSTRING, 'bar' ) ]; $this->assertEquals( $exp, $vars->getVars() ); } /** * @param AbuseFilterVariableHolder $vars * @param string $name * @param int $flags * @param AFPData $expected * @covers AbuseFilterVariableHolder::getVar * * @dataProvider provideGetVar */ public function testGetVar( AbuseFilterVariableHolder $vars, $name, $flags, AFPData $expected ) { $this->assertEquals( $expected, $vars->getVar( $name, $flags ) ); } /** * @return Generator|array */ public function provideGetVar() { $vars = new AbuseFilterVariableHolder(); $afcv = $this->getMockBuilder( AFComputedVariable::class ) ->setMethods( [ 'compute' ] ) ->disableOriginalConstructor() ->getMock(); $name = 'foo'; $expected = new AFPData( AFPData::DSTRING, 'foobarbaz' ); $afcv->method( 'compute' )->willReturn( $expected ); $vars->setVar( $name, $afcv ); yield 'set, AFComputedVariable' => [ $vars, $name, 0, $expected ]; $name = 'afpd'; $afpd = new AFPData( AFPData::DINT, 42 ); $vars->setVar( $name, $afpd ); yield 'set, AFPData' => [ $vars, $name, 0, $afpd ]; $name = 'not-set'; $expected = new AFPData( AFPData::DUNDEFINED ); yield 'unset, not strict' => [ $vars, $name, AbuseFilterVariableHolder::GET_LAX, $expected ]; // For now, it's the same. yield 'unset, strict' => [ $vars, $name, AbuseFilterVariableHolder::GET_STRICT, $expected ]; $vars->mVarsVersion = 1; $expected = new AFPData( AFPData::DSTRING, 'foo' ); $vars->setVar( 'article_text', $expected ); yield 'deprecated, with new name' => [ $vars, 'page_title', 0, $expected ]; } /** * @covers AbuseFilterVariableHolder::getVar */ public function testGetVarInvalidType() { $vars = new AbuseFilterVariableHolder(); $tw = TestingAccessWrapper::newFromObject( $vars ); $name = 'foo'; $tw->mVars = [ $name => 'INVALID TYPE' ]; $this->expectException( UnexpectedValueException::class ); $tw->getVar( $name ); } /** * @param array $expected * @param AbuseFilterVariableHolder ...$holders * @dataProvider provideHoldersForAddition * * @covers AbuseFilterVariableHolder::addHolders */ public function testAddHolders( $expected, AbuseFilterVariableHolder ...$holders ) { $actual = new AbuseFilterVariableHolder(); $actual->addHolders( ...$holders ); $this->assertEquals( $expected, $actual->getVars() ); } /** * @param array $expected * @param AbuseFilterVariableHolder ...$holders * @dataProvider provideHoldersForAddition * * @covers AbuseFilterVariableHolder::merge */ public function testMerge( $expected, AbuseFilterVariableHolder ...$holders ) { $this->assertEquals( $expected, AbuseFilterVariableHolder::merge( ...$holders )->getVars() ); } public function provideHoldersForAddition() { $v1 = AbuseFilterVariableHolder::newFromArray( [ 'a' => 1, 'b' => 2 ] ); $v2 = AbuseFilterVariableHolder::newFromArray( [ 'b' => 3, 'c' => 4 ] ); $v3 = AbuseFilterVariableHolder::newFromArray( [ 'c' => 5, 'd' => 6 ] ); $expected = [ 'a' => new AFPData( AFPData::DINT, 1 ), 'b' => new AFPData( AFPData::DINT, 3 ), 'c' => new AFPData( AFPData::DINT, 5 ), 'd' => new AFPData( AFPData::DINT, 6 ) ]; return [ [ $expected, $v1, $v2, $v3 ] ]; } /** * @covers AbuseFilterVariableHolder::varIsSet */ public function testVarIsSet() { $vars = AbuseFilterVariableHolder::newFromArray( [ 'foo' => null ] ); $this->assertTrue( $vars->varIsSet( 'foo' ), 'Set variable should be set' ); $this->assertFalse( $vars->varIsSet( 'foobarbaz' ), 'Unset variable should be unset' ); } /** * @covers AbuseFilterVariableHolder::setLazyLoadVar * @covers AbuseFilterVariableHolder::getLazyLoader */ public function testLazyLoader() { $var = 'foobar'; $method = 'compute-foo'; $params = [ 'baz', 1 ]; $exp = new AFComputedVariable( $method, $params ); $vars = new AbuseFilterVariableHolder(); $vars->setLazyLoadVar( $var, $method, $params ); $this->assertEquals( $exp, $vars->getVars()[$var] ); } /** * @covers AbuseFilterVariableHolder::exportAllVars */ public function testExportAllVars() { $pairs = [ 'foo' => 42, 'bar' => [ 'bar', 'baz' ], 'var' => false, 'boo' => null ]; $vars = AbuseFilterVariableHolder::newFromArray( $pairs ); $this->assertSame( $pairs, $vars->exportAllVars( true ), 'native types' ); $stringified = [ 'foo' => '42', 'bar' => "bar\nbaz\n", 'var' => '', 'boo' => '' ]; $this->assertSame( $stringified, $vars->exportAllVars( false ), 'stringified' ); } /** * @covers AbuseFilterVariableHolder::exportNonLazyVars */ public function testExportNonLazyVars() { $afcv = $this->createMock( AFComputedVariable::class ); $pairs = [ 'lazy1' => $afcv, 'lazy2' => $afcv, 'native1' => 42, 'native2' => 'foo', 'native3' => null, 'afpd' => new AFPData( AFPData::DSTRING, 'hey' ), ]; $vars = AbuseFilterVariableHolder::newFromArray( $pairs ); $nonLazy = [ 'native1' => '42', 'native2' => 'foo', 'native3' => '', 'afpd' => 'hey' ]; $this->assertSame( $nonLazy, $vars->exportNonLazyVars() ); } /** * @param AbuseFilterVariableHolder $vars * @param array|bool $compute * @param bool $includeUser * @param array $expected * @dataProvider provideDumpAllVars * * @covers AbuseFilterVariableHolder::dumpAllVars */ public function testDumpAllVars( $vars, $compute, $includeUser, $expected ) { $this->assertEquals( $expected, $vars->dumpAllVars( $compute, $includeUser ) ); } /** * @return Generator|array */ public function provideDumpAllVars() { $afcv = $this->getMockBuilder( AFComputedVariable::class ) ->disableOriginalConstructor() ->setMethods( [ 'compute' ] ); $preftitle = $afcv->getMock(); $titleVal = 'title'; $preftitle->method( 'compute' )->willReturn( new AFPData( AFPData::DSTRING, $titleVal ) ); $lines = $afcv->getMock(); $linesVal = 'lines'; $lines->method( 'compute' )->willReturn( new AFPData( AFPData::DSTRING, $linesVal ) ); $pairs = [ 'page_title' => 'foo', 'page_prefixedtitle' => $preftitle, 'added_lines' => $lines, 'user_name' => 'bar', 'custom-var' => 'foo' ]; $vars = AbuseFilterVariableHolder::newFromArray( $pairs ); $nonLazy = array_fill_keys( [ 'page_title', 'user_name', 'custom-var' ], 1 ); $nonLazyExpect = array_intersect_key( $pairs, $nonLazy ); yield 'lazy-loaded vars are excluded if not computed' => [ clone $vars, [], true, $nonLazyExpect ]; $nonUserExpect = array_diff_key( $nonLazyExpect, [ 'custom-var' => 1 ] ); yield 'user-set vars are excluded' => [ clone $vars, [], false, $nonUserExpect ]; $allExpect = $pairs; $allExpect['page_prefixedtitle'] = $titleVal; $allExpect['added_lines'] = $linesVal; yield 'all vars computed' => [ clone $vars, true, true, $allExpect ]; $titleOnlyComputed = array_merge( $nonLazyExpect, [ 'page_prefixedtitle' => $titleVal ] ); yield 'Only a specific var computed' => [ clone $vars, [ 'page_prefixedtitle' ], true, $titleOnlyComputed ]; } /** * @covers AbuseFilterVariableHolder::computeDBVars */ public function testComputeDBVars() { $afcv = $this->getMockBuilder( AFComputedVariable::class ) ->disableOriginalConstructor() ->setMethods( [ 'compute' ] ); $nonDBMet = [ 'unknown', 'certainly-not-db' ]; $dbMet = [ 'page-age', 'user-rights', 'load-recent-authors' ]; $methods = array_merge( $nonDBMet, $dbMet ); $objs = []; foreach ( $methods as $method ) { $cur = $afcv->getMock(); $cur->method( 'compute' )->willReturn( $method ); $cur->mMethod = $method; $objs[ $method ] = $cur; } $vars = AbuseFilterVariableHolder::newFromArray( $objs ); $vars->computeDBVars(); $expAFCV = array_intersect_key( $vars->getVars(), array_fill_keys( $nonDBMet, 1 ) ); $this->assertContainsOnlyInstancesOf( AFComputedVariable::class, $expAFCV, "non-DB methods shouldn't have been computed" ); $expComputed = array_intersect_key( $vars->getVars(), array_fill_keys( $dbMet, 1 ) ); $this->assertContainsOnlyInstancesOf( AFPData::class, $expComputed, 'DB methods should have been computed' ); } }