toBeString()->not->toBeEmpty(); }); test('column names are bound, not pasted into the IN list', function () use ($src) { expect($src)->not->toContain('implode("\',\'", $column)') ->and(substr_count($src, "array_fill(0, cacti_sizeof(\$column), '?')"))->toBe(2) ->and(substr_count($src, 'array_merge(array_values($column), [$filter])'))->toBe(2); }); test('the alias is confined to identifier characters', function () use ($src) { expect($src)->not->toContain('SELECT value AS \'" . $column . "\'') ->and($src)->toContain("\$alias = sanitize_sql_column(\$column, '');") ->and($src)->toContain("if (\$alias === '') {"); }); test('a name carrying the delimiter cannot escape the alias', function () { // the shape that used to close the alias and continue the statement expect(sanitize_sql_column("x` UNION SELECT password FROM user_auth -- ")) ->not->toContain('`') ->and(sanitize_sql_column("x' UNION SELECT 1 -- "))->not->toContain("'"); }); test('placeholder count always matches the values bound', function () { foreach ([1, 2, 5, 17] as $n) { $column = array_fill(0, $n, 'cactiApplDeviceIndex'); $placeholders = implode(',', array_fill(0, cacti_sizeof($column), '?')); $params = array_merge(array_values($column), ['filter']); expect(substr_count($placeholders, '?'))->toBe($n) ->and(cacti_sizeof($params))->toBe($n + 1); } });