| <?php
use ParagonIE\Ionizer\Filter\{
    ArrayFilter,
    BoolFilter,
    BoolArrayFilter,
    FloatFilter,
    FloatArrayFilter,
    IntFilter,
    IntArrayFilter,
    StringFilter,
    StrictArrayFilter,
    StringArrayFilter
};
use ParagonIE\Ionizer\GeneralFilterContainer;
use ParagonIE\Ionizer\InvalidDataException;
use PHPUnit\Framework\TestCase;
/**
 * Class ArrayFilterTest
 */
class ArrayFilterTest extends TestCase
{
    /**
     * @before
     */
    public function before()
    {
        if (!\class_exists('GenericFilterContainer')) {
            require_once __DIR__ . '/Errata/GenericFilterContainer.php';
        }
    }
    /**
     * This tests the normal use of the array filter, as well as the alternative separators
     *
     * @throws Error
     * @throws InvalidDataException
     */
    public function testArrayFilter()
    {
        $filter = (new GeneralFilterContainer())
            ->addFilter('test', new ArrayFilter())
            ->addFilter('test.apple', new BoolFilter())
            ->addFilter('test.boy', new FloatFilter())
            ->addFilter('test.cat', new IntFilter())
            ->addFilter('test.dog', new StringFilter());
        if (!($filter instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $before = [
            'test' => [
                'apple' => 1,
                'boy' => '1.345',
                'cat' => '25519',
                'dog' => 3.14159265
            ]
        ];
        $this->assertEquals(
            [
                'test' => [
                    'apple' => true,
                    'boy' => 1.345,
                    'cat' => 25519,
                    'dog' => '3.14159265'
                ]
            ],
            $filter($before)
        );
        $wrong = (new GenericFilterContainer())
            ->addFilter('test', new ArrayFilter())
            ->addFilter('test.apple', new BoolFilter())
            ->addFilter('test.boy', new FloatFilter())
            ->addFilter('test.cat', new IntFilter())
            ->addFilter('test.dog', new StringFilter());
        if (!($wrong instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $this->assertEquals(
            [
                'test' => [
                    'apple' => true,
                    'boy' => 1.345,
                    'cat' => 25519,
                    'dog' => '3.14159265'
                ],
                'test.apple' => false,
                'test.boy' => 0.0,
                'test.cat' => 0,
                'test.dog' => ''
            ],
            $wrong($before)
        );
        $corrected = (new GenericFilterContainer())
            ->addFilter('test', new ArrayFilter())
            ->addFilter('test::apple', new BoolFilter())
            ->addFilter('test::boy', new FloatFilter())
            ->addFilter('test::cat', new IntFilter())
            ->addFilter('test::dog', new StringFilter());
        if (!($corrected instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $this->assertEquals(
            [
                'test' => [
                    'apple' => true,
                    'boy' => 1.345,
                    'cat' => 25519,
                    'dog' => '3.14159265'
                ]
            ],
            $corrected($before)
        );
    }
    /**
     * @covers BoolArrayFilter
     * @throws Error
     * @throws InvalidDataException
     */
    public function testBoolArrayFilter()
    {
        $filter = (new GeneralFilterContainer())
            ->addFilter('test', new BoolArrayFilter());
        if (!($filter instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $before = [
            'test' => [
                '',
                null,
                0,
                1,
                'true',
                'false'
            ]
        ];
        $after = $filter($before);
        $this->assertSame(
            [
                'test' => [
                    false,
                    false,
                    false,
                    true,
                    true,
                    true
                ]
            ],
            $after
        );
        try {
            $typeError = [
                'test' => [[]]
            ];
            $filter($typeError);
            $this->fail('Expected a TypeError');
        } catch (\TypeError $ex) {
        }
    }
    /**
     * @covers FloatArrayFilter
     * @throws Error
     * @throws InvalidDataException
     */
    public function testFloatArrayFilter()
    {
        $filter = (new GeneralFilterContainer())
            ->addFilter('test', new FloatArrayFilter());
        if (!($filter instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $before = [
            'test' => [
                null,
                '',
                0,
                33
            ]
        ];
        $after = $filter($before);
        $this->assertSame(
            [
                'test' => [
                    0.0,
                    0.0,
                    0.0,
                    33.0
                ]
            ],
            $after
        );
        try {
            $typeError = [
                'test' => [2, 3, []],
                'test2' => [[1.5]]
            ];
            $filter($typeError);
            $this->fail('Expected a TypeError');
        } catch (\TypeError $ex) {
        }
    }
    /**
     * @covers IntArrayFilter
     * @throws Error
     * @throws InvalidDataException
     */
    public function testIntArrayFilter()
    {
        $filter = (new GeneralFilterContainer())
            ->addFilter('test', new IntArrayFilter())
            ->addFilter('test2', new IntArrayFilter());
        if (!($filter instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $before = [
            'test' => [
                null,
                '',
                0,
                33
            ],
            'test2' => ['1']
        ];
        $after = $filter($before);
        $this->assertSame(
            [
                'test' => [
                    0,
                    0,
                    0,
                    33
                ],
                'test2' => [
                    1
                ]
            ],
            $after
        );
        try {
            $typeError = [
                'test' => ['1', []],
                'test2' => [[1]]
            ];
            $filter($typeError);
            $this->fail('Expected a TypeError');
        } catch (\TypeError $ex) {
        }
    }
    /**
     * @throws Error
     * @throws Exception
     * @throws InvalidDataException
     */
    public function testStrictArrayFilter()
    {
        try {
            (new GeneralFilterContainer())
                ->addFilter('test', new StrictArrayFilter('float', 'string'));
        } catch (\RuntimeException $ex) {
            $this->assertSame(
                'Cannot accept key types other than "int" or "string".',
                $ex->getMessage()
            );
        }
        $filter = (new GeneralFilterContainer())
            ->addFilter('test', new StrictArrayFilter('int', 'string'));
        if (!($filter instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $filter([
            'test' => [
                'abc',
                'def'
            ]
        ]);
        try {
            $filter([
                'test' => [
                    1,
                    'abc',
                    'def'
                ]
            ]);
            $this->fail('Uncaught value mismatch');
        } catch (\TypeError $ex) {
            $this->assertSame(
                'Expected an array<int, string>. At least one element of <int, int> was found (test[0] == 1).',
                $ex->getMessage()
            );
        }
        $filter([
            'test' => [
                1 => 'abc',
                2 => 'def'
            ]
        ]);
        try {
            $filter([
                'test' => [
                    1 => 'abc',
                    '1a' => 'def'
                ]
            ]);
            $this->fail('Uncaught value mismatch');
        } catch (\TypeError $ex) {
            $this->assertSame(
                'Expected an array<int, string>. At least one element of <string, string> was found (test["1a"] == "def").',
                $ex->getMessage()
            );
        }
        $filter->addFilter('second', new StrictArrayFilter('string', \stdClass::class));
        $filter([
            'test' => ['abc', 'def'],
            'second' => [
                'test' => (object)['test']
            ]
        ]);
        $filter([
            'test' => ['abc', 'def'],
            'second' => [
                '1234a' => (object)['test']
            ]
        ]);
        try {
            $filter([
                'test' => ['abc', 'def'],
                'second' => [
                    'test' => (object)['test'],
                    123 => (object)['test2'],
                ]
            ]);
            $this->fail('Invalid key accepted');
        } catch (\TypeError $ex) {
            $this->assertSame(
                'Expected an array<string, stdClass>. At least one element of <int, stdClass> was found (second[123] == {"0":"test2"}).',
                $ex->getMessage()
            );
        }
        try {
            $filter([
                'test' => ['abc', 'def'],
                'second' => [
                    'test' => (object)['test'],
                    '123' => null,
                ]
            ]);
            $this->fail('Null accepted where it was not wanted');
        } catch (\TypeError $ex) {
            $this->assertSame(
                'Expected an array<string, stdClass>. At least one element of <int, null> was found (second[123] == null).',
                $ex->getMessage()
            );
        }
        $cf = (new GeneralFilterContainer());
        $cf->addFilter('test', new StrictArrayFilter('string', 'callable'));
        $cf([
            'test' => [
                'a' => function() { return 'foo'; },
                'b' => '\\strlen',
                'c' => [StringFilter::class, 'nonEmpty']
            ],
        ]);
        $fuzz = \bin2hex(\random_bytes(33));
        try {
            $cf([
                'test' => [
                    'a' => function() { return 'foo'; },
                    'b' => $fuzz
                ],
            ]);
            $this->fail('Invalid function name was declared');
        } catch (\TypeError $ex) {
            $this->assertSame(
                'Expected an array<string, callable>. At least one element of <string, string> was found (test["b"] == "' . $fuzz . '").',
                $ex->getMessage()
            );
        }
    }
    /**
     * @covers StringArrayFilter
     * @throws Error
     * @throws InvalidDataException
     */
    public function testStringArrayFilter()
    {
        $filter = (new GeneralFilterContainer())
            ->addFilter('test', new StringArrayFilter());
        if (!($filter instanceof GeneralFilterContainer)) {
            $this->fail('Type error');
        }
        $before = [
            'test' => [
                null,
                '',
                0,
                33
            ]
        ];
        $after = $filter($before);
        $this->assertSame(
            [
                'test' => [
                    '',
                    '',
                    '0',
                    '33'
                ]
            ],
            $after
        );
        try {
            $typeError = [
                'test' => ['a', []]
            ];
            $filter($typeError);
            $this->fail('Expected a TypeError');
        } catch (\TypeError $ex) {
        }
    }
}
 |