881 lines
36 KiB
PHP
881 lines
36 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace PhpMyAdmin\Tests\Plugins\Export;
|
|
|
|
use PhpMyAdmin\Column;
|
|
use PhpMyAdmin\Config;
|
|
use PhpMyAdmin\Config\Settings\Export as SettingsExport;
|
|
use PhpMyAdmin\ConfigStorage\Relation;
|
|
use PhpMyAdmin\ConfigStorage\RelationParameters;
|
|
use PhpMyAdmin\Dbal\ConnectionType;
|
|
use PhpMyAdmin\Dbal\DatabaseInterface;
|
|
use PhpMyAdmin\Export\Export;
|
|
use PhpMyAdmin\Export\OutputHandler;
|
|
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
|
|
use PhpMyAdmin\Identifiers\TableName;
|
|
use PhpMyAdmin\Identifiers\TriggerName;
|
|
use PhpMyAdmin\Plugins\Export\ExportOdt;
|
|
use PhpMyAdmin\Plugins\ExportPlugin;
|
|
use PhpMyAdmin\Plugins\ExportType;
|
|
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
|
|
use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
|
|
use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
|
|
use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
|
|
use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
|
|
use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
|
|
use PhpMyAdmin\Tests\AbstractTestCase;
|
|
use PhpMyAdmin\Tests\FieldHelper;
|
|
use PhpMyAdmin\Tests\Stubs\DummyResult;
|
|
use PhpMyAdmin\Transformations;
|
|
use PhpMyAdmin\Triggers\Event;
|
|
use PhpMyAdmin\Triggers\Timing;
|
|
use PhpMyAdmin\Triggers\Trigger;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use PHPUnit\Framework\Attributes\Medium;
|
|
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
|
|
use ReflectionMethod;
|
|
use ReflectionProperty;
|
|
|
|
use function __;
|
|
use function bin2hex;
|
|
|
|
use const MYSQLI_BLOB_FLAG;
|
|
use const MYSQLI_NUM_FLAG;
|
|
use const MYSQLI_TYPE_BLOB;
|
|
use const MYSQLI_TYPE_DECIMAL;
|
|
use const MYSQLI_TYPE_STRING;
|
|
|
|
#[CoversClass(ExportOdt::class)]
|
|
#[Medium]
|
|
#[RequiresPhpExtension('zip')]
|
|
final class ExportOdtTest extends AbstractTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
OutputHandler::$asFile = true;
|
|
ExportPlugin::$exportType = ExportType::Table;
|
|
ExportPlugin::$singleTable = false;
|
|
}
|
|
|
|
public function testSetProperties(): void
|
|
{
|
|
ExportPlugin::$exportType = ExportType::Raw;
|
|
ExportPlugin::$singleTable = false;
|
|
|
|
$relationParameters = RelationParameters::fromArray([
|
|
RelationParameters::DATABASE => 'db',
|
|
RelationParameters::RELATION => 'relation',
|
|
RelationParameters::COLUMN_INFO => 'column_info',
|
|
RelationParameters::REL_WORK => true,
|
|
RelationParameters::MIME_WORK => true,
|
|
]);
|
|
(new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters);
|
|
|
|
$exportOdt = $this->getExportOdt();
|
|
|
|
$method = new ReflectionMethod(ExportOdt::class, 'setProperties');
|
|
$properties = $method->invoke($exportOdt, null);
|
|
|
|
self::assertInstanceOf(ExportPluginProperties::class, $properties);
|
|
|
|
self::assertSame(
|
|
'OpenDocument Text',
|
|
$properties->getText(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'odt',
|
|
$properties->getExtension(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'application/vnd.oasis.opendocument.text',
|
|
$properties->getMimeType(),
|
|
);
|
|
|
|
self::assertTrue(
|
|
$properties->getForceFile(),
|
|
);
|
|
|
|
$options = $properties->getOptions();
|
|
|
|
self::assertInstanceOf(OptionsPropertyRootGroup::class, $options);
|
|
|
|
self::assertSame(
|
|
'Format Specific Options',
|
|
$options->getName(),
|
|
);
|
|
|
|
$generalOptionsArray = $options->getProperties();
|
|
|
|
$generalOptions = $generalOptionsArray->current();
|
|
$generalOptionsArray->next();
|
|
|
|
self::assertInstanceOf(OptionsPropertyMainGroup::class, $generalOptions);
|
|
|
|
self::assertSame(
|
|
'odt_general_opts',
|
|
$generalOptions->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Dump table',
|
|
$generalOptions->getText(),
|
|
);
|
|
|
|
$generalProperties = $generalOptions->getProperties();
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(RadioPropertyItem::class, $property);
|
|
|
|
self::assertSame(
|
|
'odt_structure_or_data',
|
|
$property->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
['structure' => __('structure'), 'data' => __('data'), 'structure_and_data' => __('structure and data')],
|
|
$property->getValues(),
|
|
);
|
|
|
|
$generalOptions = $generalOptionsArray->current();
|
|
$generalOptionsArray->next();
|
|
|
|
self::assertInstanceOf(OptionsPropertyMainGroup::class, $generalOptions);
|
|
|
|
self::assertSame(
|
|
'odt_structure',
|
|
$generalOptions->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Object creation options',
|
|
$generalOptions->getText(),
|
|
);
|
|
|
|
$generalProperties = $generalOptions->getProperties();
|
|
|
|
$property = $generalProperties->current();
|
|
$generalProperties->next();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
self::assertSame(
|
|
'odt_relation',
|
|
$property->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Display foreign key relationships',
|
|
$property->getText(),
|
|
);
|
|
|
|
$property = $generalProperties->current();
|
|
$generalProperties->next();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
self::assertSame(
|
|
'odt_comments',
|
|
$property->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Display comments',
|
|
$property->getText(),
|
|
);
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
self::assertSame(
|
|
'odt_mime',
|
|
$property->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Display media types',
|
|
$property->getText(),
|
|
);
|
|
|
|
// hide structure
|
|
$generalOptions = $generalOptionsArray->current();
|
|
|
|
self::assertInstanceOf(OptionsPropertyMainGroup::class, $generalOptions);
|
|
|
|
self::assertSame(
|
|
'odt_data',
|
|
$generalOptions->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Data dump options',
|
|
$generalOptions->getText(),
|
|
);
|
|
|
|
$generalProperties = $generalOptions->getProperties();
|
|
|
|
$property = $generalProperties->current();
|
|
$generalProperties->next();
|
|
|
|
self::assertInstanceOf(BoolPropertyItem::class, $property);
|
|
|
|
self::assertSame(
|
|
'odt_columns',
|
|
$property->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Put columns names in the first row',
|
|
$property->getText(),
|
|
);
|
|
|
|
$property = $generalProperties->current();
|
|
|
|
self::assertInstanceOf(TextPropertyItem::class, $property);
|
|
|
|
self::assertSame(
|
|
'odt_null',
|
|
$property->getName(),
|
|
);
|
|
|
|
self::assertSame(
|
|
'Replace NULL with:',
|
|
$property->getText(),
|
|
);
|
|
|
|
// case 2
|
|
ExportPlugin::$exportType = ExportType::Table;
|
|
ExportPlugin::$singleTable = false;
|
|
|
|
$method->invoke($exportOdt, null);
|
|
|
|
$generalOptionsArray = $options->getProperties();
|
|
|
|
self::assertCount(3, $generalOptionsArray);
|
|
}
|
|
|
|
public function testExportHeader(): void
|
|
{
|
|
$exportOdt = $this->getExportOdt();
|
|
$exportOdt->exportHeader();
|
|
|
|
self::assertStringContainsString('<office:document-content', $exportOdt->buffer);
|
|
self::assertStringContainsString('office:version', $exportOdt->buffer);
|
|
}
|
|
|
|
public function testExportFooter(): void
|
|
{
|
|
$exportOdt = $this->getExportOdt();
|
|
$exportOdt->buffer = 'header';
|
|
$exportOdt->exportFooter();
|
|
$output = $this->getActualOutputForAssertion();
|
|
self::assertMatchesRegularExpression('/^504b.*636f6e74656e742e786d6c/', bin2hex($output));
|
|
self::assertStringContainsString('header', $exportOdt->buffer);
|
|
self::assertStringContainsString('</office:text></office:body></office:document-content>', $exportOdt->buffer);
|
|
}
|
|
|
|
public function testExportDBHeader(): void
|
|
{
|
|
$exportOdt = $this->getExportOdt();
|
|
$exportOdt->buffer = 'header';
|
|
|
|
$exportOdt->exportDBHeader('d&b');
|
|
|
|
self::assertStringContainsString('header', $exportOdt->buffer);
|
|
|
|
self::assertStringContainsString('Database d&b</text:h>', $exportOdt->buffer);
|
|
}
|
|
|
|
public function testExportDBFooter(): void
|
|
{
|
|
$exportOdt = $this->getExportOdt();
|
|
$this->expectNotToPerformAssertions();
|
|
$exportOdt->exportDBFooter('testDB');
|
|
}
|
|
|
|
public function testExportDBCreate(): void
|
|
{
|
|
$exportOdt = $this->getExportOdt();
|
|
$this->expectNotToPerformAssertions();
|
|
$exportOdt->exportDBCreate('testDB');
|
|
}
|
|
|
|
public function testExportData(): void
|
|
{
|
|
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$fields = [
|
|
FieldHelper::fromArray(['type' => -1]),
|
|
FieldHelper::fromArray([
|
|
'type' => MYSQLI_TYPE_BLOB,
|
|
'flags' => MYSQLI_BLOB_FLAG,
|
|
'charsetnr' => 63,
|
|
]),
|
|
FieldHelper::fromArray(['type' => MYSQLI_TYPE_DECIMAL, 'flags' => MYSQLI_NUM_FLAG]),
|
|
FieldHelper::fromArray(['type' => MYSQLI_TYPE_STRING]),
|
|
];
|
|
$resultStub = self::createMock(DummyResult::class);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('getFieldsMeta')
|
|
->with($resultStub)
|
|
->willReturn($fields);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('query')
|
|
->with('SELECT', ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED)
|
|
->willReturn($resultStub);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('numFields')
|
|
->willReturn(4);
|
|
|
|
$resultStub->expects(self::exactly(2))
|
|
->method('fetchRow')
|
|
->willReturn([null, 'a<b', 'a>b', 'a&b'], []);
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['odt_null' => '&']);
|
|
|
|
$exportOdt = $this->getExportOdt($dbi);
|
|
$exportOdt->setExportOptions($request, new SettingsExport());
|
|
|
|
$exportOdt->exportData('db', 'ta<ble', 'SELECT');
|
|
|
|
self::assertSame(
|
|
'<text:h text:outline-level="2" text:style-name="Heading_2" ' .
|
|
'text:is-list-header="true">Dumping data for table ta<ble</text:h>' .
|
|
'<table:table table:name="ta<ble_structure"><table:table-column ' .
|
|
'table:number-columns-repeated="4"/><table:table-row>' .
|
|
'<table:table-cell office:value-type="string"><text:p>&</text:p>' .
|
|
'</table:table-cell><table:table-cell office:value-type="string">' .
|
|
'<text:p></text:p></table:table-cell><table:table-cell ' .
|
|
'office:value-type="float" office:value="a>b" ><text:p>a>b</text:p>' .
|
|
'</table:table-cell><table:table-cell office:value-type="string">' .
|
|
'<text:p>a&b</text:p></table:table-cell></table:table-row>' .
|
|
'</table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
}
|
|
|
|
public function testExportDataWithFieldNames(): void
|
|
{
|
|
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$fields = [
|
|
FieldHelper::fromArray([
|
|
'type' => MYSQLI_TYPE_STRING,
|
|
'name' => 'fna\"me',
|
|
'length' => 20,
|
|
]),
|
|
FieldHelper::fromArray([
|
|
'type' => MYSQLI_TYPE_STRING,
|
|
'name' => 'fnam/<e2',
|
|
'length' => 20,
|
|
]),
|
|
];
|
|
|
|
$resultStub = self::createMock(DummyResult::class);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('getFieldsMeta')
|
|
->with($resultStub)
|
|
->willReturn($fields);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('query')
|
|
->with('SELECT', ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED)
|
|
->willReturn($resultStub);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('numFields')
|
|
->willReturn(2);
|
|
|
|
$resultStub->expects(self::exactly(1))
|
|
->method('fetchRow')
|
|
->willReturn([]);
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['odt_columns' => 'On']);
|
|
|
|
$exportOdt = $this->getExportOdt($dbi);
|
|
$exportOdt->setExportOptions($request, new SettingsExport());
|
|
|
|
$exportOdt->exportData('db', 'table', 'SELECT');
|
|
|
|
self::assertSame(
|
|
'<text:h text:outline-level="2" text:style-name="Heading_2" text:' .
|
|
'is-list-header="true">Dumping data for table table</text:h><table:' .
|
|
'table table:name="table_structure"><table:table-column table:number-' .
|
|
'columns-repeated="2"/><table:table-row><table:table-cell office:' .
|
|
'value-type="string"><text:p>fna\"me</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>fnam/<e2' .
|
|
'</text:p></table:table-cell></table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
|
|
// with no row count
|
|
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$flags = [];
|
|
|
|
$resultStub = self::createMock(DummyResult::class);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('getFieldsMeta')
|
|
->with($resultStub)
|
|
->willReturn($flags);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('query')
|
|
->with('SELECT', ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED)
|
|
->willReturn($resultStub);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('numFields')
|
|
->willReturn(0);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('fetchRow')
|
|
->willReturn([]);
|
|
|
|
$exportOdt = $this->getExportOdt($dbi);
|
|
$exportOdt->setExportOptions($request, new SettingsExport());
|
|
$exportOdt->buffer = '';
|
|
|
|
$exportOdt->exportData('db', 'table', 'SELECT');
|
|
|
|
self::assertSame(
|
|
'<text:h text:outline-level="2" text:style-name="Heading_2" ' .
|
|
'text:is-list-header="true">Dumping data for table table</text:h>' .
|
|
'<table:table table:name="table_structure"><table:table-column ' .
|
|
'table:number-columns-repeated="0"/><table:table-row>' .
|
|
'</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
}
|
|
|
|
public function testGetTableDefStandIn(): void
|
|
{
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$exportOdt = $this->getExportOdt($this->createDatabaseInterface($dbiDummy));
|
|
|
|
$dbiDummy->addSelectDb('test_db');
|
|
self::assertSame(
|
|
$exportOdt->getTableDefStandIn('test_db', 'test_table'),
|
|
'',
|
|
);
|
|
$dbiDummy->assertAllSelectsConsumed();
|
|
|
|
self::assertSame(
|
|
'<table:table table:name="test_table_data">'
|
|
. '<table:table-column table:number-columns-repeated="4"/><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Column</text:p>'
|
|
. '</table:table-cell><table:table-cell office:value-type="string"><text:p>Type</text:p>'
|
|
. '</table:table-cell><table:table-cell office:value-type="string"><text:p>Null</text:p>'
|
|
. '</table:table-cell><table:table-cell office:value-type="string"><text:p>Default</text:p>'
|
|
. '</table:table-cell></table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>id</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>int(11)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>name</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>varchar(20)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetimefield</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetime</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
}
|
|
|
|
public function testGetTableDef(): void
|
|
{
|
|
$resultStub = self::createMock(DummyResult::class);
|
|
|
|
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$dbi->expects(self::exactly(2))
|
|
->method('fetchResult')
|
|
->willReturn(
|
|
[],
|
|
['fieldname' => ['values' => 'test-', 'transformation' => 'testfoo', 'mimetype' => 'test<']],
|
|
);
|
|
|
|
$column = new Column('fieldname', '', null, false, '', null, '', '', '');
|
|
$dbi->expects(self::once())
|
|
->method('getColumns')
|
|
->with('database', '')
|
|
->willReturn([$column]);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('tryQueryAsControlUser')
|
|
->willReturn($resultStub);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('numRows')
|
|
->willReturn(1);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('fetchAssoc')
|
|
->willReturn(['comment' => 'testComment']);
|
|
|
|
$exportOdt = $this->getExportOdt($dbi);
|
|
|
|
$relationParameters = RelationParameters::fromArray([
|
|
RelationParameters::REL_WORK => true,
|
|
RelationParameters::COMM_WORK => true,
|
|
RelationParameters::MIME_WORK => true,
|
|
RelationParameters::DATABASE => 'database',
|
|
RelationParameters::RELATION => 'rel',
|
|
RelationParameters::COLUMN_INFO => 'col',
|
|
]);
|
|
(new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters);
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['odt_relation' => 'On', 'odt_mime' => 'On', 'odt_comments' => 'On']);
|
|
|
|
$exportOdt->setExportOptions($request, new SettingsExport());
|
|
|
|
self::assertTrue($exportOdt->getTableDef('database', ''));
|
|
|
|
self::assertSame(
|
|
'<table:table table:name="_structure">' .
|
|
'<table:table-column table:number-columns-repeated="6"/><table:table-row>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Column</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Type</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Null</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Default</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Comments</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Media type</text:p></table:table-cell>' .
|
|
'</table:table-row><table:table-row>' .
|
|
'<table:table-cell office:value-type="string"><text:p>fieldname</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>&nbsp;</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p></text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p></text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Test<</text:p></table:table-cell>' .
|
|
'</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
|
|
// case 2
|
|
|
|
$resultStub = self::createMock(DummyResult::class);
|
|
|
|
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$dbi->expects(self::exactly(2))
|
|
->method('fetchResult')
|
|
->willReturn(
|
|
['fieldname' => ['foreign_table' => 'ftable', 'foreign_field' => 'ffield']],
|
|
['field' => ['values' => 'test-', 'transformation' => 'testfoo', 'mimetype' => 'test<']],
|
|
);
|
|
|
|
$column = new Column('fieldname', '', null, false, '', null, '', '', '');
|
|
$dbi->expects(self::once())
|
|
->method('getColumns')
|
|
->with('database', '')
|
|
->willReturn([$column]);
|
|
|
|
$dbi->expects(self::once())
|
|
->method('tryQueryAsControlUser')
|
|
->willReturn($resultStub);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('numRows')
|
|
->willReturn(1);
|
|
|
|
$resultStub->expects(self::once())
|
|
->method('fetchAssoc')
|
|
->willReturn(['comment' => 'testComment']);
|
|
|
|
$exportOdt = $this->getExportOdt($dbi);
|
|
$exportOdt->setExportOptions($request, new SettingsExport());
|
|
|
|
$exportOdt->buffer = '';
|
|
$relationParameters = RelationParameters::fromArray([
|
|
RelationParameters::REL_WORK => true,
|
|
RelationParameters::COMM_WORK => true,
|
|
RelationParameters::MIME_WORK => true,
|
|
RelationParameters::DATABASE => 'database',
|
|
RelationParameters::RELATION => 'rel',
|
|
RelationParameters::COLUMN_INFO => 'col',
|
|
]);
|
|
(new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters);
|
|
|
|
self::assertTrue($exportOdt->getTableDef('database', ''));
|
|
|
|
self::assertSame(
|
|
'<table:table table:name="_structure">' .
|
|
'<table:table-column table:number-columns-repeated="7"/><table:table-row>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Column</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Type</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Null</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Default</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Links to</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Comments</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>Media type</text:p></table:table-cell>' .
|
|
'</table:table-row><table:table-row>' .
|
|
'<table:table-cell office:value-type="string"><text:p>fieldname</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>&nbsp;</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p></text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p>ftable (ffield)</text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p></text:p></table:table-cell>' .
|
|
'<table:table-cell office:value-type="string"><text:p></text:p></table:table-cell>' .
|
|
'</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
}
|
|
|
|
public function testGetTriggers(): void
|
|
{
|
|
$triggers = [
|
|
new Trigger(
|
|
TriggerName::from('tna"me'),
|
|
Timing::After,
|
|
Event::Insert,
|
|
TableName::from('ta<ble'),
|
|
'def',
|
|
'test_user@localhost',
|
|
),
|
|
];
|
|
|
|
$exportOdt = $this->getExportOdt();
|
|
|
|
$method = new ReflectionMethod(ExportOdt::class, 'getTriggers');
|
|
$result = $method->invoke($exportOdt, 'ta<ble', $triggers);
|
|
|
|
self::assertSame($result, $exportOdt->buffer);
|
|
|
|
self::assertStringContainsString('<table:table table:name="ta<ble_triggers">', $result);
|
|
|
|
self::assertStringContainsString('<text:p>tna"me</text:p>', $result);
|
|
|
|
self::assertStringContainsString('<text:p>AFTER</text:p>', $result);
|
|
|
|
self::assertStringContainsString('<text:p>INSERT</text:p>', $result);
|
|
|
|
self::assertStringContainsString('<text:p>def</text:p>', $result);
|
|
}
|
|
|
|
public function testExportStructure(): void
|
|
{
|
|
Config::getInstance()->selectedServer['DisableIS'] = true;
|
|
|
|
$dbiDummy = $this->createDbiDummy();
|
|
$exportOdt = $this->getExportOdt($this->createDatabaseInterface($dbiDummy));
|
|
|
|
// case 1
|
|
$dbiDummy->addSelectDb('test_db');
|
|
$exportOdt->exportStructure('test_db', 'test_table', 'create_table');
|
|
$dbiDummy->assertAllSelectsConsumed();
|
|
|
|
self::assertSame(
|
|
'<text:h text:outline-level="2" text:style-name="Heading_2" text:is-list-header="true">'
|
|
. 'Table structure for table test_table</text:h><table:table table:name="test_table_structure">'
|
|
. '<table:table-column table:number-columns-repeated="4"/><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Column</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Type</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Null</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Default</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>id</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>int(11)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>name</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>varchar(20)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetimefield</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetime</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
|
|
// case 2
|
|
$exportOdt->buffer = '';
|
|
|
|
$exportOdt->exportStructure('test_db', 'test_table', 'triggers');
|
|
|
|
self::assertSame(
|
|
'<text:h text:outline-level="2" text:style-name="Heading_2" text:is-list-header="true">'
|
|
. 'Triggers test_table</text:h><table:table table:name="test_table_triggers">'
|
|
. '<table:table-column table:number-columns-repeated="4"/><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Name</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Time</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Event</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Definition</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>test_trigger</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>AFTER</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>INSERT</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>BEGIN END</text:p></table:table-cell>'
|
|
. '</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
|
|
// case 3
|
|
$exportOdt->buffer = '';
|
|
|
|
$dbiDummy->addSelectDb('test_db');
|
|
$exportOdt->exportStructure('test_db', 'test_table', 'create_view');
|
|
$dbiDummy->assertAllSelectsConsumed();
|
|
|
|
self::assertSame(
|
|
'<text:h text:outline-level="2" text:style-name="Heading_2" text:is-list-header="true">'
|
|
. 'Structure for view test_table</text:h><table:table table:name="test_table_structure">'
|
|
. '<table:table-column table:number-columns-repeated="4"/><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Column</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Type</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Null</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Default</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>id</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>int(11)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>name</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>varchar(20)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetimefield</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetime</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
|
|
// case 4
|
|
$dbiDummy->addSelectDb('test_db');
|
|
$exportOdt->buffer = '';
|
|
$exportOdt->exportStructure('test_db', 'test_table', 'stand_in');
|
|
$dbiDummy->assertAllSelectsConsumed();
|
|
|
|
self::assertSame(
|
|
'<text:h text:outline-level="2" text:style-name="Heading_2" text:is-list-header="true">'
|
|
. 'Stand-in structure for view test_table</text:h><table:table table:name="test_table_data">'
|
|
. '<table:table-column table:number-columns-repeated="4"/><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Column</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Type</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Null</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>Default</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>id</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>int(11)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>name</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>varchar(20)</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row><table:table-row>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetimefield</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>datetime</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>No</text:p></table:table-cell>'
|
|
. '<table:table-cell office:value-type="string"><text:p>NULL</text:p></table:table-cell>'
|
|
. '</table:table-row></table:table>',
|
|
$exportOdt->buffer,
|
|
);
|
|
}
|
|
|
|
public function testFormatOneColumnDefinition(): void
|
|
{
|
|
$method = new ReflectionMethod(ExportOdt::class, 'formatOneColumnDefinition');
|
|
|
|
$column = new Column('field', 'set(abc)enum123', null, true, 'PRI', null, '', '', '');
|
|
|
|
$colAlias = 'alias';
|
|
|
|
$exportOdt = $this->getExportOdt();
|
|
|
|
self::assertSame(
|
|
'<table:table-row><table:table-cell office:value-type="string">' .
|
|
'<text:p>alias</text:p></table:table-cell><table:table-cell off' .
|
|
'ice:value-type="string"><text:p>set(abc)</text:p></table:table' .
|
|
'-cell><table:table-cell office:value-type="string"><text:p>Yes' .
|
|
'</text:p></table:table-cell><table:table-cell office:value-typ' .
|
|
'e="string"><text:p>NULL</text:p></table:table-cell>',
|
|
$method->invoke($exportOdt, $column, $colAlias),
|
|
);
|
|
|
|
$column = new Column('fields', '', null, false, 'COMP', 'def', '', '', '');
|
|
|
|
self::assertSame(
|
|
'<table:table-row><table:table-cell office:value-type="string">' .
|
|
'<text:p>fields</text:p></table:table-cell><table:table-cell off' .
|
|
'ice:value-type="string"><text:p>&nbsp;</text:p></table:table' .
|
|
'-cell><table:table-cell office:value-type="string"><text:p>No' .
|
|
'</text:p></table:table-cell><table:table-cell office:value-type=' .
|
|
'"string"><text:p>def</text:p></table:table-cell>',
|
|
$method->invoke($exportOdt, $column, ''),
|
|
);
|
|
}
|
|
|
|
public function testExportTableCallsExportStructureMethod(): void
|
|
{
|
|
$dbi = $this->getMockBuilder(DatabaseInterface::class)
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
|
|
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
|
|
->withParsedBody(['odt_structure_or_data' => 'structure']);
|
|
|
|
$exportOdt = $this->getExportOdt($dbi);
|
|
$exportOdt->setExportOptions($request, new SettingsExport());
|
|
$export = new Export($dbi, new OutputHandler());
|
|
$export->exportTable(
|
|
'testdb',
|
|
'testtable',
|
|
$exportOdt,
|
|
null,
|
|
'0',
|
|
'0',
|
|
'',
|
|
[],
|
|
);
|
|
self::assertStringContainsString('Table structure for table testtable', $exportOdt->buffer);
|
|
}
|
|
|
|
private function getExportOdt(DatabaseInterface|null $dbi = null): ExportOdt
|
|
{
|
|
$dbi ??= $this->createDatabaseInterface();
|
|
$config = new Config();
|
|
$relation = new Relation($dbi, $config);
|
|
|
|
return new ExportOdt($relation, new OutputHandler(), new Transformations($dbi, $relation), $dbi, $config);
|
|
}
|
|
}
|