Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Database\StoredProcedure;
|
||||
|
||||
use App\Framework\Database\StoredProcedure\StoredProcedureDefinition;
|
||||
|
||||
test('creates a stored procedure definition with builder pattern', function () {
|
||||
$procedure = StoredProcedureDefinition::create('get_user_by_id')
|
||||
->withParameter('user_id', 'INT')
|
||||
->withBody('SELECT * FROM users WHERE id = user_id')
|
||||
->returnsType('TABLE');
|
||||
|
||||
expect($procedure->getName())->toBe('get_user_by_id');
|
||||
expect($procedure->getParameters())->toHaveCount(1);
|
||||
expect($procedure->getParameters()[0]['name'])->toBe('user_id');
|
||||
expect($procedure->getParameters()[0]['type'])->toBe('INT');
|
||||
expect($procedure->getBody())->toBe('SELECT * FROM users WHERE id = user_id');
|
||||
expect($procedure->getReturnType())->toBe('TABLE');
|
||||
});
|
||||
|
||||
test('generates MySQL stored procedure SQL', function () {
|
||||
$procedure = StoredProcedureDefinition::create('calculate_order_total')
|
||||
->withParameter('order_id', 'INT')
|
||||
->withParameter('include_tax', 'BOOLEAN', true)
|
||||
->withBody('
|
||||
DECLARE total DECIMAL(10,2);
|
||||
SELECT SUM(price * quantity) INTO total FROM order_items WHERE order_id = order_id;
|
||||
IF include_tax THEN
|
||||
SET total = total * 1.19;
|
||||
END IF;
|
||||
RETURN total;
|
||||
')
|
||||
->returnsType('DECIMAL(10,2)');
|
||||
|
||||
$sql = $procedure->toSql('mysql');
|
||||
|
||||
expect($sql)->toContain('CREATE PROCEDURE `calculate_order_total`');
|
||||
expect($sql)->toContain('IN `order_id` INT');
|
||||
expect($sql)->toContain('IN `include_tax` BOOLEAN');
|
||||
expect($sql)->toContain('DECLARE total DECIMAL(10,2)');
|
||||
expect($sql)->toContain('DELIMITER');
|
||||
});
|
||||
|
||||
test('generates PostgreSQL stored procedure SQL', function () {
|
||||
$procedure = StoredProcedureDefinition::create('get_active_users')
|
||||
->withParameter('min_login_count', 'INT')
|
||||
->withBody('
|
||||
RETURN QUERY
|
||||
SELECT * FROM users
|
||||
WHERE active = true AND login_count >= min_login_count
|
||||
ORDER BY last_login DESC;
|
||||
')
|
||||
->returnsType('SETOF users');
|
||||
|
||||
$sql = $procedure->toSql('pgsql');
|
||||
|
||||
expect($sql)->toContain('CREATE OR REPLACE FUNCTION get_active_users');
|
||||
expect($sql)->toContain('min_login_count INT');
|
||||
expect($sql)->toContain('RETURNS SETOF users');
|
||||
expect($sql)->toContain('LANGUAGE plpgsql');
|
||||
});
|
||||
|
||||
test('generates SQLite stored procedure SQL as user-defined function', function () {
|
||||
$procedure = StoredProcedureDefinition::create('calculate_age')
|
||||
->withParameter('birth_date', 'TEXT')
|
||||
->withBody('
|
||||
RETURN (julianday("now") - julianday(birth_date)) / 365.25;
|
||||
')
|
||||
->returnsType('REAL');
|
||||
|
||||
$sql = $procedure->toSql('sqlite');
|
||||
|
||||
expect($sql)->toContain('CREATE FUNCTION calculate_age');
|
||||
expect($sql)->toContain('(birth_date TEXT)');
|
||||
expect($sql)->toContain('RETURNS REAL');
|
||||
});
|
||||
|
||||
test('throws exception for unsupported database driver', function () {
|
||||
$procedure = StoredProcedureDefinition::create('test_procedure')
|
||||
->withBody('SELECT 1')
|
||||
->returnsType('INT');
|
||||
|
||||
expect(fn () => $procedure->toSql('unsupported_driver'))
|
||||
->toThrow(\InvalidArgumentException::class, 'Unsupported database driver: unsupported_driver');
|
||||
});
|
||||
|
||||
test('validates procedure name', function () {
|
||||
expect(fn () => StoredProcedureDefinition::create(''))
|
||||
->toThrow(\InvalidArgumentException::class, 'Procedure name cannot be empty');
|
||||
|
||||
expect(fn () => StoredProcedureDefinition::create('invalid-name'))
|
||||
->toThrow(\InvalidArgumentException::class, 'Invalid procedure name: invalid-name');
|
||||
});
|
||||
|
||||
test('validates parameter name', function () {
|
||||
$procedure = StoredProcedureDefinition::create('test_procedure');
|
||||
|
||||
expect(fn () => $procedure->withParameter('', 'INT'))
|
||||
->toThrow(\InvalidArgumentException::class, 'Parameter name cannot be empty');
|
||||
|
||||
expect(fn () => $procedure->withParameter('invalid-name', 'INT'))
|
||||
->toThrow(\InvalidArgumentException::class, 'Invalid parameter name: invalid-name');
|
||||
});
|
||||
|
||||
test('validates body is not empty before generating SQL', function () {
|
||||
$procedure = StoredProcedureDefinition::create('test_procedure');
|
||||
|
||||
expect(fn () => $procedure->toSql('mysql'))
|
||||
->toThrow(\InvalidArgumentException::class, 'Procedure body cannot be empty');
|
||||
});
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Framework\Database\StoredProcedure;
|
||||
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\Database\ResultInterface;
|
||||
use App\Framework\Database\StoredProcedure\StoredProcedureDefinition;
|
||||
use App\Framework\Database\StoredProcedure\StoredProcedureManager;
|
||||
use Mockery;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->connection = Mockery::mock(ConnectionInterface::class);
|
||||
$this->manager = new StoredProcedureManager($this->connection);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('creates a stored procedure', function () {
|
||||
$procedure = StoredProcedureDefinition::create('get_user_by_id')
|
||||
->withParameter('user_id', 'INT')
|
||||
->withBody('SELECT * FROM users WHERE id = user_id')
|
||||
->returnsType('TABLE');
|
||||
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('mysql');
|
||||
|
||||
$this->connection->shouldReceive('execute')
|
||||
->with(Mockery::pattern('/CREATE PROCEDURE/'), [])
|
||||
->once()
|
||||
->andReturn(1);
|
||||
|
||||
$result = $this->manager->createProcedure($procedure);
|
||||
|
||||
expect($result)->toBeTrue();
|
||||
});
|
||||
|
||||
test('drops a stored procedure', function () {
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('mysql');
|
||||
|
||||
$this->connection->shouldReceive('execute')
|
||||
->with('DROP PROCEDURE IF EXISTS `get_user_by_id`', [])
|
||||
->once()
|
||||
->andReturn(1);
|
||||
|
||||
$result = $this->manager->dropProcedure('get_user_by_id');
|
||||
|
||||
expect($result)->toBeTrue();
|
||||
});
|
||||
|
||||
test('checks if a stored procedure exists', function () {
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('mysql');
|
||||
|
||||
$this->connection->shouldReceive('queryScalar')
|
||||
->with(Mockery::pattern('/INFORMATION_SCHEMA.ROUTINES/'), ['get_user_by_id'])
|
||||
->once()
|
||||
->andReturn(1);
|
||||
|
||||
$result = $this->manager->procedureExists('get_user_by_id');
|
||||
|
||||
expect($result)->toBeTrue();
|
||||
});
|
||||
|
||||
test('executes a stored procedure with parameters', function () {
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('mysql');
|
||||
|
||||
$mockResult = Mockery::mock(ResultInterface::class);
|
||||
$mockResult->shouldReceive('fetchAll')->andReturn([
|
||||
['id' => 1, 'name' => 'John Doe', 'email' => 'john@example.com'],
|
||||
]);
|
||||
|
||||
$this->connection->shouldReceive('query')
|
||||
->with('CALL get_user_by_id(?)', [42])
|
||||
->once()
|
||||
->andReturn($mockResult);
|
||||
|
||||
$result = $this->manager->executeProcedure('get_user_by_id', [42]);
|
||||
|
||||
expect($result)->toBeInstanceOf(ResultInterface::class);
|
||||
expect($result->fetchAll())->toHaveCount(1);
|
||||
expect($result->fetchAll()[0]['name'])->toBe('John Doe');
|
||||
});
|
||||
|
||||
test('executes a stored function with parameters', function () {
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('mysql');
|
||||
|
||||
$this->connection->shouldReceive('queryScalar')
|
||||
->with('SELECT calculate_order_total(?, ?)', [123, true])
|
||||
->once()
|
||||
->andReturn(99.99);
|
||||
|
||||
$result = $this->manager->executeFunction('calculate_order_total', [123, true]);
|
||||
|
||||
expect($result)->toBe(99.99);
|
||||
});
|
||||
|
||||
test('lists all stored procedures', function () {
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('mysql');
|
||||
|
||||
$mockResult = Mockery::mock(ResultInterface::class);
|
||||
$mockResult->shouldReceive('fetchAll')->andReturn([
|
||||
['ROUTINE_NAME' => 'get_user_by_id', 'ROUTINE_TYPE' => 'PROCEDURE'],
|
||||
['ROUTINE_NAME' => 'calculate_order_total', 'ROUTINE_TYPE' => 'FUNCTION'],
|
||||
]);
|
||||
|
||||
$this->connection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/INFORMATION_SCHEMA.ROUTINES/'), [])
|
||||
->once()
|
||||
->andReturn($mockResult);
|
||||
|
||||
$procedures = $this->manager->listProcedures();
|
||||
|
||||
expect($procedures)->toHaveCount(2);
|
||||
expect($procedures[0]['name'])->toBe('get_user_by_id');
|
||||
expect($procedures[0]['type'])->toBe('PROCEDURE');
|
||||
expect($procedures[1]['name'])->toBe('calculate_order_total');
|
||||
expect($procedures[1]['type'])->toBe('FUNCTION');
|
||||
});
|
||||
|
||||
test('gets procedure definition', function () {
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('mysql');
|
||||
|
||||
$mockResult = Mockery::mock(ResultInterface::class);
|
||||
$mockResult->shouldReceive('fetchOne')->andReturn([
|
||||
'ROUTINE_NAME' => 'get_user_by_id',
|
||||
'ROUTINE_TYPE' => 'PROCEDURE',
|
||||
'ROUTINE_DEFINITION' => 'SELECT * FROM users WHERE id = user_id',
|
||||
'DTD_IDENTIFIER' => null,
|
||||
'PARAMETER_STYLE' => 'SQL',
|
||||
]);
|
||||
|
||||
$this->connection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/INFORMATION_SCHEMA.ROUTINES/'), ['get_user_by_id'])
|
||||
->once()
|
||||
->andReturn($mockResult);
|
||||
|
||||
$paramResult = Mockery::mock(ResultInterface::class);
|
||||
$paramResult->shouldReceive('fetchAll')->andReturn([
|
||||
['PARAMETER_NAME' => 'user_id', 'DATA_TYPE' => 'INT', 'PARAMETER_MODE' => 'IN'],
|
||||
]);
|
||||
|
||||
$this->connection->shouldReceive('query')
|
||||
->with(Mockery::pattern('/INFORMATION_SCHEMA.PARAMETERS/'), ['get_user_by_id'])
|
||||
->once()
|
||||
->andReturn($paramResult);
|
||||
|
||||
$definition = $this->manager->getProcedureDefinition('get_user_by_id');
|
||||
|
||||
expect($definition)->toBeInstanceOf(StoredProcedureDefinition::class);
|
||||
expect($definition->getName())->toBe('get_user_by_id');
|
||||
expect($definition->getBody())->toBe('SELECT * FROM users WHERE id = user_id');
|
||||
expect($definition->getParameters())->toHaveCount(1);
|
||||
expect($definition->getParameters()[0]['name'])->toBe('user_id');
|
||||
expect($definition->getParameters()[0]['type'])->toBe('INT');
|
||||
});
|
||||
|
||||
test('handles unsupported database driver', function () {
|
||||
$this->connection->shouldReceive('getPdo->getAttribute')
|
||||
->with(\PDO::ATTR_DRIVER_NAME)
|
||||
->andReturn('unsupported_driver');
|
||||
|
||||
expect(fn () => $this->manager->procedureExists('test_procedure'))
|
||||
->toThrow(\InvalidArgumentException::class, 'Unsupported database driver: unsupported_driver');
|
||||
});
|
||||
Reference in New Issue
Block a user