From d104709fed68e865e93a7c7d55ea2a057f1d7ce9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 28 Nov 2019 08:50:34 +0100 Subject: [PATCH 1/9] adapt tests to v2 connection interface --- src/AbstractConnectionTestCase.php | 87 +++++++----------------------- src/AbstractTestCase.php | 60 ++++++++++----------- 2 files changed, 49 insertions(+), 98 deletions(-) diff --git a/src/AbstractConnectionTestCase.php b/src/AbstractConnectionTestCase.php index fc26150..979c6fc 100644 --- a/src/AbstractConnectionTestCase.php +++ b/src/AbstractConnectionTestCase.php @@ -1,16 +1,10 @@ getSapConfig(); } - $connection = $this->newConnection($config); - static::assertFalse($connection->isConnected()); - $connection->connect(); - static::assertTrue($connection->isConnected()); - $connection->connect(); - static::assertTrue($connection->isConnected()); - $connection->close(); - static::assertFalse($connection->isConnected()); + $rfc_ping = $this->newConnection($config) + ->prepareFunction('RFC_PING'); + static::assertInstanceOf(IFunction::class, $rfc_ping); } /** * Mock the SAP RFC module for a failed connection attempt. */ - abstract protected function mockFailedConnect(); + abstract protected function mockConnectionFailed(); /** * Test a failed connection attempt using either the module or its mockup. * @expectedException \phpsap\exceptions\ConnectionFailedException */ - public function testFailedConnect() + public function testConnectionFailed() { if (!extension_loaded($this->getModuleName())) { //load functions mocking SAP RFC module functions or class methods - $this->mockFailedConnect(); + $this->mockConnectionFailed(); //load a bogus config $config = $this->getSampleSapConfig(); } else { - //load an invalid config - $config = $this->getSampleSapConfig(); - } - $connection = $this->newConnection($config); - $connection->connect(); - } - - /** - * Mock the SAP RFC module for a successful attempt to ping a connection. - */ - abstract protected function mockSuccessfulPing(); - - /** - * Test a successful attempt to ping a connection using either the module or its - * mockup. - */ - public function testSuccessfulPing() - { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockSuccessfulPing(); - //load a bogus config + //load a complete but invalid config $config = $this->getSampleSapConfig(); - } else { - //load a valid config - $config = $this->getSapConfig(); } - $connection = $this->newConnection($config); - $result = $connection->ping(); - static::assertTrue($result); + $rfc_ping = $this->newConnection($config) + ->prepareFunction('RFC_PING'); } /** - * Mock the SAP RFC module for a failed attempt to ping a connection. - */ - abstract protected function mockFailedPing(); - - /** - * Test a failed attempt to ping a connection using either the module or its - * mockup. + * Test a failed connection attempt using either the module or its mockup. + * @expectedException \phpsap\exceptions\IncompleteConfigException */ - public function testFailedPing() + public function testConfigIncomplete() { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockFailedPing(); - //load a bogus config - $config = $this->getSampleSapConfig(); - } else { - static::markTestSkipped('Cannot test a failing ping with SAP module loaded.'); - } - $connection = $this->newConnection($config); - $result = $connection->ping(); - static::assertFalse($result); + /** + * Connection with empty configuration will be considered incomplete. + */ + $rfc_ping = $this->newConnection() + ->prepareFunction('RFC_PING'); } } diff --git a/src/AbstractTestCase.php b/src/AbstractTestCase.php index 2dcf5f5..d5bb045 100644 --- a/src/AbstractTestCase.php +++ b/src/AbstractTestCase.php @@ -1,15 +1,11 @@ 'sap.example.com', - 'sysnr' => '001', - 'client' => '002', - 'user' => 'username', - 'passwd' => 'password' + 'ashost' => 'sap.example.com', + 'sysnr' => '001', + 'client' => '002', + 'user' => 'username', + 'passwd' => 'password' ]; /** * AbstractTestCase constructor. - * @param null $name - * @param array $data - * @param string $dataName + * @param string|null $name + * @param array $data + * @param string $dataName */ public function __construct($name = null, array $data = [], $dataName = '') { @@ -47,19 +43,22 @@ public function __construct($name = null, array $data = [], $dataName = '') /** * Get a sample SAP config. - * @return array + * @return ConfigTypeA */ protected function getSampleSapConfig() { - return static::$sampleSapConfig; + return new ConfigTypeA(static::$sampleSapConfig); } /** * Load an actual sap configuration. - * @return array + * @return ConfigTypeA */ protected function getSapConfig() { + /** + * Actual implementation has to return the path of a valid configuration file. + */ $configFile = $this->getSapConfigFile(); if (file_exists($configFile) !== true) { throw new \RuntimeException(sprintf( @@ -67,21 +66,20 @@ protected function getSapConfig() $configFile )); } - + /** + * Try to read the configuration file. + */ if (($configJson = file_get_contents($configFile)) === false) { throw new \RuntimeException(sprintf( 'Cannot read from config file %s!', $configFile )); } - - if (($configArr = json_decode($configJson, true)) === null) { - throw new \RuntimeException(sprintf( - 'Invalid JSON format in config file %s!', - $configFile - )); - } - return $configArr; + /** + * Let the Config* classes decide what to do with the given string. + * In case the string is not JSON, an exception will be thrown. + */ + return ConfigCommon::jsonDecode($configJson); } /** @@ -120,8 +118,8 @@ abstract public function getValidModuleFunctions(); /** * Create a new instance of a PHP/SAP connection class. - * @param array|string|null $config The PHP/SAP configuration. Default: null + * @param IConfiguration|null $config The PHP/SAP configuration. Default: null * @return \phpsap\interfaces\IConnection */ - abstract public function newConnection($config = null); + abstract public function newConnection(IConfiguration $config = null); } From 2bfbc7d3e3ae47636311f8bfa381bc61869be150 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 28 Nov 2019 14:16:19 +0100 Subject: [PATCH 2/9] extend tests to reach 100% coverage of potential code for v2 connection interface --- src/AbstractConnectionTestCase.php | 147 ++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-) diff --git a/src/AbstractConnectionTestCase.php b/src/AbstractConnectionTestCase.php index 979c6fc..295d20d 100644 --- a/src/AbstractConnectionTestCase.php +++ b/src/AbstractConnectionTestCase.php @@ -2,7 +2,13 @@ namespace phpsap\IntegrationTests; +use phpsap\classes\Config\ConfigCommon; use phpsap\classes\Config\ConfigTypeA; +use phpsap\classes\Config\ConfigTypeB; +use phpsap\exceptions\ConnectionFailedException; +use phpsap\interfaces\Config\IConfigTypeA; +use phpsap\interfaces\Config\IConfigTypeB; +use phpsap\interfaces\IConnection; use phpsap\interfaces\IFunction; /** @@ -16,6 +22,82 @@ */ abstract class AbstractConnectionTestCase extends AbstractTestCase { + /** + * Test SAP RFC connection type A configuration. + * @throws \InvalidArgumentException + * @throws \PHPUnit_Framework_Exception + */ + public function testConnectionConfigTypeA() + { + if (!extension_loaded($this->getModuleName())) { + //load functions mocking SAP RFC module functions or class methods + $this->mockConnectionFailed(); + } + $conn = $this->newConnection(new ConfigTypeA([ + 'ashost' => 'sap.example.com', + 'sysnr' => '001', + 'client' => '002', + 'user' => 'username', + 'passwd' => 'password' + ])); + static::assertInstanceOf(IConnection::class, $conn); + /** + * @var IConfigTypeA $cfg + */ + $cfg = $conn->getConfiguration(); + static::assertInstanceOf(IConfigTypeA::class, $cfg); + static::assertSame('sap.example.com', $cfg->getAshost()); + static::assertSame('001', $cfg->getSysnr()); + static::assertSame('002', $cfg->getClient()); + //Set a clearly non-existing hostname to cause a connection failure. + $conn->getConfiguration()->setAshost('prod.sap.example.com'); + static::assertSame('prod.sap.example.com', $cfg->getAshost()); + /** + * Try to establish a connection, which should fail because of example.com. + */ + $exception = null; + try { + $conn->prepareFunction('RFC_PING'); + } catch (ConnectionFailedException $exception) {} + static::assertInstanceOf(ConnectionFailedException::class, $exception); + } + + /** + * Test SAP RFC connection type B configuration. + * @throws \InvalidArgumentException + * @throws \PHPUnit_Framework_Exception + */ + public function testConnectionConfigTypeB() + { + if (!extension_loaded($this->getModuleName())) { + //load functions mocking SAP RFC module functions or class methods + $this->mockConnectionFailed(); + } + $conn = $this->newConnection(new ConfigTypeB([ + 'mshost' => 'msg.sap.example.com', + 'group' => 'grp01', + 'client' => '003', + 'user' => 'username', + 'passwd' => 'password' + ])); + static::assertInstanceOf(IConnection::class, $conn); + /** + * @var IConfigTypeB $cfg + */ + $cfg = $conn->getConfiguration(); + static::assertInstanceOf(IConfigTypeB::class, $cfg); + static::assertSame('msg.sap.example.com', $cfg->getMshost()); + static::assertSame('grp01', $cfg->getGroup()); + static::assertSame('003', $cfg->getClient()); + $conn->getConfiguration()->setMshost('grp01msg.sap.example.com'); + static::assertSame('grp01msg.sap.example.com', $cfg->getMshost()); + $exception = null; + try { + $conn->prepareFunction('RFC_PING'); + } catch (ConnectionFailedException $exception) {} + static::assertInstanceOf(ConnectionFailedException::class, $exception); + } + /** * Mock the SAP RFC module for a successful connection attempt. */ @@ -64,16 +146,77 @@ public function testConnectionFailed() ->prepareFunction('RFC_PING'); } + /** + * Data provider for incomplete configurations. + * @return array + * @throws \InvalidArgumentException + */ + public static function provideIncompleteConfig() + { + return [ + [new ConfigTypeA()], + [new ConfigTypeA([ + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_ASHOST => 'sap.example.com', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_SYSNR => '00', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_GWHOST => 'sapgw.example.com', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_GWSERV => 'sapsrv', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeB()], + [new ConfigTypeB([ + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeB([ + ConfigTypeB::JSON_R3NAME => 'name', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeB([ + ConfigTypeB::JSON_GROUP => 'group', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])] + ]; + } + /** * Test a failed connection attempt using either the module or its mockup. + * @dataProvider provideIncompleteConfig * @expectedException \phpsap\exceptions\IncompleteConfigException + * @expectedExceptionMessage Configuration is missing mandatory key */ - public function testConfigIncomplete() + public function testIncompleteConfig($config) { /** * Connection with empty configuration will be considered incomplete. */ - $rfc_ping = $this->newConnection() + $rfc_ping = $this->newConnection($config) ->prepareFunction('RFC_PING'); } } From 0f559d125141fe1c943713485e73c48182c9d668 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 16 Dec 2019 16:08:24 +0100 Subject: [PATCH 3/9] Adapt PHP5 version of integration tests to v2 API and v3 commons. --- src/AbstractConfigATestCase.php | 152 ------------ src/AbstractConfigBTestCase.php | 147 ------------ src/AbstractConnectionTestCase.php | 222 ------------------ src/AbstractFunctionTestCase.php | 218 ------------------ src/AbstractSapRfcTestCase.php | 358 +++++++++++++++++++++++++++++ src/AbstractTestCase.php | 85 +++++-- src/RFC_PING.json | 3 + 7 files changed, 428 insertions(+), 757 deletions(-) delete mode 100644 src/AbstractConfigATestCase.php delete mode 100644 src/AbstractConfigBTestCase.php delete mode 100644 src/AbstractConnectionTestCase.php delete mode 100644 src/AbstractFunctionTestCase.php create mode 100644 src/AbstractSapRfcTestCase.php create mode 100644 src/RFC_PING.json diff --git a/src/AbstractConfigATestCase.php b/src/AbstractConfigATestCase.php deleted file mode 100644 index c7f46e3..0000000 --- a/src/AbstractConfigATestCase.php +++ /dev/null @@ -1,152 +0,0 @@ -newConfigA(); - static::assertInstanceOf(IConfig::class, $config); - static::assertInstanceOf(IConfigA::class, $config); - static::assertInstanceOf(AbstractConfigA::class, $config); - } - - /** - * Test a valid config creation. - */ - public function testValidConfig() - { - $configArr = [ - 'ashost' => 'sap.example.com', - 'sysnr' => '000', - 'client' => '01', - 'user' => 'username', - 'passwd' => 'password', - 'gwhost' => 'gw.example.com', - 'gwserv' => 'abc', - 'lang' => 'EN', - 'trace' => IConfigA::TRACE_FULL - ]; - $configJson = json_encode($configArr); - $config = $this->newConfigA($configJson); - $this->assertValidModuleConfig( - $config->generateConfig(), - 'sap.example.com', - '000', - '01', - 'username', - 'password', - 'gw.example.com', - 'abc', - 'EN', - IConfigA::TRACE_FULL - ); - } - - /** - * Data provider for incomplete config. - * @return array - */ - public static function incompleteConfig() - { - return [ - [ - [ - 'ashost' => 'sap.example.com', - 'sysnr' => '000', - 'client' => '01', - 'user' => 'username' - ], - 'passwd' - ], - [ - [ - 'ashost' => 'sap.example.com', - 'sysnr' => '000', - 'user' => 'username', - 'passwd' => 'password', - 'gwhost' => 'gw.example.com', - 'gwserv' => 'abc', - 'lang' => 'EN', - 'trace' => IConfigA::TRACE_BRIEF - ], - 'client' - ] - ]; - } - - /** - * Test incomplete config exception. - * @param array $configArr - * @param string $missing - * @dataProvider incompleteConfig - */ - public function testIncompleteConfig($configArr, $missing) - { - $configJson = json_encode($configArr); - $config = $this->newConfigA($configJson); - $expectedMsg = sprintf('Missing mandatory key %s.', $missing); - $this->setExpectedException(IncompleteConfigException::class, $expectedMsg); - $config->generateConfig(); - } - - /** - * Return a new instance of a PHP/SAP config type A. - * @param array|string|null $config PHP/SAP config JSON/array. Default: null - * @return \phpsap\interfaces\IConfigA - */ - abstract public function newConfigA($config = null); - - /** - * Assert the actual module configuration variable. - * @param mixed $configSaprfc - * @param string $ashost - * @param string $sysnr - * @param string $client - * @param string $user - * @param string $passwd - * @param string $gwhost - * @param string $gwserv - * @param string $lang - * @param int $trace - */ - abstract public function assertValidModuleConfig( - $configSaprfc, - $ashost, - $sysnr, - $client, - $user, - $passwd, - $gwhost, - $gwserv, - $lang, - $trace - ); -} diff --git a/src/AbstractConfigBTestCase.php b/src/AbstractConfigBTestCase.php deleted file mode 100644 index d30e604..0000000 --- a/src/AbstractConfigBTestCase.php +++ /dev/null @@ -1,147 +0,0 @@ -newConfigB(); - static::assertInstanceOf(IConfig::class, $config); - static::assertInstanceOf(IConfigB::class, $config); - static::assertInstanceOf(AbstractConfigB::class, $config); - } - - /** - * Test a valid config creation. - */ - public function testValidConfig() - { - $configArr = [ - 'client' => '02', - 'user' => 'username', - 'passwd' => 'password', - 'mshost' => 'sap.example.com', - 'r3name' => 'system_id', - 'group' => 'logon_group', - 'lang' => 'EN', - 'trace' => IConfigB::TRACE_VERBOSE - ]; - $configJson = json_encode($configArr); - $config = $this->newConfigB($configJson); - $this->assertValidModuleConfig( - $config->generateConfig(), - '02', - 'username', - 'password', - 'sap.example.com', - 'system_id', - 'logon_group', - 'EN', - IConfigB::TRACE_VERBOSE - ); - } - - /** - * Data provider for incomplete config. - * @return array - */ - public static function incompleteConfig() - { - return [ - [ - [ - 'client' => '02', - 'user' => 'username', - 'passwd' => 'password', - 'r3name' => 'system_id', - 'group' => 'logon_group' - ], - 'mshost' - ], - [ - [ - 'user' => 'username', - 'passwd' => 'password', - 'mshost' => 'sap.example.com', - 'group' => 'logon_group', - 'lang' => 'EN', - 'trace' => IConfigB::TRACE_OFF - ], - 'client' - ] - ]; - } - - /** - * Test incomplete config exception. - * @param array $configArr - * @param string $missing - * @dataProvider incompleteConfig - */ - public function testIncompleteConfig($configArr, $missing) - { - $configJson = json_encode($configArr); - $config = $this->newConfigB($configJson); - $expectedMsg = sprintf('Missing mandatory key %s.', $missing); - $this->setExpectedException(IncompleteConfigException::class, $expectedMsg); - $config->generateConfig(); - } - - /** - * Return a new instance of a PHP/SAP config type B. - * @param array|string|null $config PHP/SAP config JSON/array. Default: null - * @return \phpsap\interfaces\IConfigB - */ - abstract public function newConfigB($config = null); - - /** - * Assert the actual module configuration variable. - * @param mixed $configSaprfc - * @param string $client - * @param string $user - * @param string $passwd - * @param string $mshost - * @param string $r3name - * @param string $group - * @param string $lang - * @param int $trace - */ - abstract public function assertValidModuleConfig( - $configSaprfc, - $client, - $user, - $passwd, - $mshost, - $r3name, - $group, - $lang, - $trace - ); -} diff --git a/src/AbstractConnectionTestCase.php b/src/AbstractConnectionTestCase.php deleted file mode 100644 index 295d20d..0000000 --- a/src/AbstractConnectionTestCase.php +++ /dev/null @@ -1,222 +0,0 @@ -getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockConnectionFailed(); - } - $conn = $this->newConnection(new ConfigTypeA([ - 'ashost' => 'sap.example.com', - 'sysnr' => '001', - 'client' => '002', - 'user' => 'username', - 'passwd' => 'password' - ])); - static::assertInstanceOf(IConnection::class, $conn); - /** - * @var IConfigTypeA $cfg - */ - $cfg = $conn->getConfiguration(); - static::assertInstanceOf(IConfigTypeA::class, $cfg); - static::assertSame('sap.example.com', $cfg->getAshost()); - static::assertSame('001', $cfg->getSysnr()); - static::assertSame('002', $cfg->getClient()); - //Set a clearly non-existing hostname to cause a connection failure. - $conn->getConfiguration()->setAshost('prod.sap.example.com'); - static::assertSame('prod.sap.example.com', $cfg->getAshost()); - /** - * Try to establish a connection, which should fail because of example.com. - */ - $exception = null; - try { - $conn->prepareFunction('RFC_PING'); - } catch (ConnectionFailedException $exception) {} - static::assertInstanceOf(ConnectionFailedException::class, $exception); - } - - /** - * Test SAP RFC connection type B configuration. - * @throws \InvalidArgumentException - * @throws \PHPUnit_Framework_Exception - */ - public function testConnectionConfigTypeB() - { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockConnectionFailed(); - } - $conn = $this->newConnection(new ConfigTypeB([ - 'mshost' => 'msg.sap.example.com', - 'group' => 'grp01', - 'client' => '003', - 'user' => 'username', - 'passwd' => 'password' - ])); - static::assertInstanceOf(IConnection::class, $conn); - /** - * @var IConfigTypeB $cfg - */ - $cfg = $conn->getConfiguration(); - static::assertInstanceOf(IConfigTypeB::class, $cfg); - static::assertSame('msg.sap.example.com', $cfg->getMshost()); - static::assertSame('grp01', $cfg->getGroup()); - static::assertSame('003', $cfg->getClient()); - $conn->getConfiguration()->setMshost('grp01msg.sap.example.com'); - static::assertSame('grp01msg.sap.example.com', $cfg->getMshost()); - $exception = null; - try { - $conn->prepareFunction('RFC_PING'); - } catch (ConnectionFailedException $exception) {} - static::assertInstanceOf(ConnectionFailedException::class, $exception); - } - - /** - * Mock the SAP RFC module for a successful connection attempt. - */ - abstract protected function mockSuccessfulConnect(); - - /** - * Test a successful connection attempt using either the module or its mockup. - */ - public function testSuccessfulConnect() - { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockSuccessfulConnect(); - //load a bogus config - $config = $this->getSampleSapConfig(); - } else { - //load a valid config - $config = $this->getSapConfig(); - } - $rfc_ping = $this->newConnection($config) - ->prepareFunction('RFC_PING'); - static::assertInstanceOf(IFunction::class, $rfc_ping); - } - - /** - * Mock the SAP RFC module for a failed connection attempt. - */ - abstract protected function mockConnectionFailed(); - - /** - * Test a failed connection attempt using either the module or its mockup. - * @expectedException \phpsap\exceptions\ConnectionFailedException - */ - public function testConnectionFailed() - { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockConnectionFailed(); - //load a bogus config - $config = $this->getSampleSapConfig(); - } else { - //load a complete but invalid config - $config = $this->getSampleSapConfig(); - } - $rfc_ping = $this->newConnection($config) - ->prepareFunction('RFC_PING'); - } - - /** - * Data provider for incomplete configurations. - * @return array - * @throws \InvalidArgumentException - */ - public static function provideIncompleteConfig() - { - return [ - [new ConfigTypeA()], - [new ConfigTypeA([ - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])], - [new ConfigTypeA([ - ConfigTypeA::JSON_ASHOST => 'sap.example.com', - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])], - [new ConfigTypeA([ - ConfigTypeA::JSON_SYSNR => '00', - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])], - [new ConfigTypeA([ - ConfigTypeA::JSON_GWHOST => 'sapgw.example.com', - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])], - [new ConfigTypeA([ - ConfigTypeA::JSON_GWSERV => 'sapsrv', - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])], - [new ConfigTypeB()], - [new ConfigTypeB([ - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])], - [new ConfigTypeB([ - ConfigTypeB::JSON_R3NAME => 'name', - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])], - [new ConfigTypeB([ - ConfigTypeB::JSON_GROUP => 'group', - ConfigTypeA::JSON_CLIENT => '001', - ConfigTypeA::JSON_USER => 'username', - ConfigTypeA::JSON_PASSWD => 'password' - ])] - ]; - } - - /** - * Test a failed connection attempt using either the module or its mockup. - * @dataProvider provideIncompleteConfig - * @expectedException \phpsap\exceptions\IncompleteConfigException - * @expectedExceptionMessage Configuration is missing mandatory key - */ - public function testIncompleteConfig($config) - { - /** - * Connection with empty configuration will be considered incomplete. - */ - $rfc_ping = $this->newConnection($config) - ->prepareFunction('RFC_PING'); - } -} diff --git a/src/AbstractFunctionTestCase.php b/src/AbstractFunctionTestCase.php deleted file mode 100644 index 149f70f..0000000 --- a/src/AbstractFunctionTestCase.php +++ /dev/null @@ -1,218 +0,0 @@ -getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockSuccessfulFunctionCall(); - //load a bogus config - $config = $this->getSampleSapConfig(); - } else { - //load a valid config - $config = $this->getSapConfig(); - } - $connection = $this->newConnection($config); - $function = $connection->prepareFunction('RFC_PING'); - $result = $function->invoke(); - static::assertSame([], $result); - } - - /** - * Mock the SAP RFC module for an unknown function call exception. - */ - abstract protected function mockUnknownFunctionException(); - - /** - * Test invoking an unknown function an receiving an exception. - * @expectedException \phpsap\exceptions\UnknownFunctionException - * @expectedExceptionMessageRegExp "^Unknown function RFC_PINGG: .*" - */ - public function testUnknownFunctionException() - { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockUnknownFunctionException(); - //load a bogus config - $config = $this->getSampleSapConfig(); - } else { - //load a valid config - $config = $this->getSapConfig(); - } - $connection = $this->newConnection($config); - $function = $connection->prepareFunction('RFC_PINGG'); - $function->invoke(); - } - - /** - * Mock the SAP RFC module for a successful SAP remote function call with - * parameters and results. - */ - abstract protected function mockRemoteFunctionCallWithParametersAndResults(); - - /** - * Test successful SAP remote function call with parameters and results. - */ - public function testRemoteFunctionCallWithParametersAndResults() - { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockRemoteFunctionCallWithParametersAndResults(); - //load a bogus config - $config = $this->getSampleSapConfig(); - } else { - //load a valid config - $config = $this->getSapConfig(); - } - //prepare a DateTime object for testing SAP date and time. - $testDateTime = new \DateTime('2019-10-30 10:20:30'); - //prepare function call parameter - $test_in = [ - 'RFCFLOAT' => 70.11, - 'RFCCHAR1' => 'A', - 'RFCINT2' => 4095, - 'RFCINT1' => 163, - 'RFCCHAR4' => 'QqMh', - 'RFCINT4' => 416639, - 'RFCHEX3' => '53', //=S - 'RFCCHAR2' => 'XC', - 'RFCTIME' => $testDateTime->format(SapDateTime::SAP_TIME), - 'RFCDATE' => $testDateTime->format(SapDateTime::SAP_DATE), - 'RFCDATA1' => 'qKWjmNfad32rfS9Z', - 'RFCDATA2' => 'xi82ph2zJ8BCVtlR' - ]; - //remote function call - $jsonFile = __DIR__ . DIRECTORY_SEPARATOR . 'RFC_WALK_THRU_TEST.json'; - $result = $this->newConnection($config) - ->prepareFunction('RFC_WALK_THRU_TEST') - ->setApi(new RemoteApi(file_get_contents($jsonFile))) - ->setParam('TEST_IN', $test_in) - ->setParam('DESTINATIONS', [ - ['RFCDEST' => 'AOP3'] - ]) - ->invoke(); - //assert basics - static::assertInternalType('array', $result); - static::assertArrayHasKey('TEST_OUT', $result); - //create a link for programmer's convenience ... - $test_out = &$result['TEST_OUT']; - /** - * Assert float value. - */ - static::assertArrayHasKey('RFCFLOAT', $test_out, 'Missing RFCFLOAT in TEST_OUT!'); - static::assertSame(70.11, $test_out['RFCFLOAT'], 'Test IN and OUT of RFCFLOAT don\'t match!'); - /** - * Assert integer values. - */ - static::assertArrayHasKey('RFCINT1', $test_out, 'Missing RFCINT1 in TEST_OUT!'); - static::assertSame(163, $test_out['RFCINT1'], 'Test IN and OUT of RFCINT1 don\'t match!'); - static::assertArrayHasKey('RFCINT2', $test_out, 'Missing RFCINT2 in TEST_OUT!'); - static::assertSame(4095, $test_out['RFCINT2'], 'Test IN and OUT of RFCINT2 don\'t match!'); - static::assertArrayHasKey('RFCINT4', $test_out, 'Missing RFCINT4 in TEST_OUT!'); - static::assertSame(416639, $test_out['RFCINT4'], 'Test IN and OUT of RFCINT4 don\'t match!'); - /** - * Assert DateTime objects. - */ - static::assertArrayHasKey('RFCTIME', $test_out, 'Missing RFCTIME in TEST_OUT!'); - static::assertInstanceOf(\DateTime::class, $test_out['RFCTIME'], 'Test OUT of RFCTIME is not DateTime!'); - static::assertSame($testDateTime->format('H:i:s'), $test_out['RFCTIME']->format('H:i:s')); - static::assertArrayHasKey('RFCDATE', $test_out, 'Missing RFCDATE in TEST_OUT!'); - static::assertInstanceOf(\DateTime::class, $test_out['RFCDATE'], 'Test OUT of RFCDATE is not DateTime!'); - static::assertSame($testDateTime->format('Y-m-d'), $test_out['RFCDATE']->format('Y-m-d')); - /** - * Assert hexadecimal value. - */ - static::assertArrayHasKey('RFCHEX3', $test_out, 'Missing RFCHEX3 in TEST_OUT!'); - static::assertSame('S', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); - /** - * Assert string values. - */ - static::assertArrayHasKey('RFCCHAR1', $test_out, 'Missing RFCCHAR1 in TEST_OUT!'); - static::assertSame('A', $test_out['RFCCHAR1'], 'Test IN and OUT of RFCCHAR1 don\'t match!'); - static::assertArrayHasKey('RFCCHAR2', $test_out, 'Missing RFCCHAR2 in TEST_OUT!'); - static::assertSame('XC', $test_out['RFCCHAR2'], 'Test IN and OUT of RFCCHAR2 don\'t match!'); - static::assertArrayHasKey('RFCCHAR4', $test_out, 'Missing RFCCHAR4 in TEST_OUT!'); - static::assertSame('QqMh', $test_out['RFCCHAR4'], 'Test IN and OUT of RFCCHAR4 don\'t match!'); - static::assertArrayHasKey('RFCDATA1', $test_out, 'Missing RFCDATA1 in TEST_OUT!'); - static::assertSame('qKWjmNfad32rfS9Z', $test_out['RFCDATA1'], 'Test IN and OUT of RFCDATA1 don\'t match!'); - static::assertArrayHasKey('RFCDATA2', $test_out, 'Missing RFCDATA2 in TEST_OUT!'); - static::assertSame('xi82ph2zJ8BCVtlR', $test_out['RFCDATA2'], 'Test IN and OUT of RFCDATA2 don\'t match!'); - /** - * Assert empty table parameter 'DESTINATIONS'. - */ - static::assertArrayHasKey('DESTINATIONS', $result); - static::assertEmpty($result['DESTINATIONS']); - /** - * Assert return table 'LOG' and that RFCDEST is the same as in the - * 'DESTINATIONS' table parameter. - */ - static::assertArrayHasKey('LOG', $result); - static::assertArrayHasKey(0, $result['LOG']); - static::assertInternalType('array', $result['LOG'][0]); - static::assertArrayHasKey('RFCDEST', $result['LOG'][0]); - static::assertSame('AOP3', $result['LOG'][0]['RFCDEST']); - } - - /** - * Mock the SAP RFC module for a failed SAP remote function call with parameters. - */ - abstract protected function mockFailedRemoteFunctionCallWithParameters(); - - /** - * Test a failed remote function call with parameters. - * @expectedException \phpsap\exceptions\FunctionCallException - * @expectedExceptionMessageRegExp "^Function call RFC_READ_TABLE failed: .*" - */ - public function testFailedRemoteFunctionCallWithParameters() - { - if (!extension_loaded($this->getModuleName())) { - //load functions mocking SAP RFC module functions or class methods - $this->mockFailedRemoteFunctionCallWithParameters(); - //load a bogus config - $config = $this->getSampleSapConfig(); - } else { - //load a valid config - $config = $this->getSapConfig(); - } - //try to invoke a function call using an invalid parameter value - $jsonFile = __DIR__ . DIRECTORY_SEPARATOR . 'RFC_READ_TABLE.json'; - $this->newConnection($config) - ->prepareFunction('RFC_READ_TABLE') - ->setApi(new RemoteApi(file_get_contents($jsonFile))) - ->setParam('QUERY_TABLE', '&') - ->invoke(); - } -} diff --git a/src/AbstractSapRfcTestCase.php b/src/AbstractSapRfcTestCase.php new file mode 100644 index 0000000..3413a37 --- /dev/null +++ b/src/AbstractSapRfcTestCase.php @@ -0,0 +1,358 @@ +mockConnectionFailed(); + } + $saprfc = static::newSapRfc('RFC_PING', null, new ConfigTypeA([ + 'ashost' => 'sap.example.com', + 'sysnr' => '001', + 'client' => '002', + 'user' => 'username', + 'passwd' => 'password' + ]), static::getApi('RFC_PING')); + static::assertInstanceOf(IFunction::class, $saprfc); + $cfg = $saprfc->getConfiguration(); + static::assertInstanceOf(IConfigTypeA::class, $cfg); + static::assertSame('sap.example.com', $cfg->getAshost()); + static::assertSame('001', $cfg->getSysnr()); + static::assertSame('002', $cfg->getClient()); + //Set a clearly non-existing hostname to cause a connection failure. + $saprfc->getConfiguration()->setAshost('prod.sap.example.com'); + static::assertSame('prod.sap.example.com', $cfg->getAshost()); + /** + * Try to establish a connection, which should fail because of example.com. + */ + $exception = null; + try { + $saprfc->invoke(); + } catch (IConnectionFailedException $exception) { + } + static::assertInstanceOf(IConnectionFailedException::class, $exception); + } + + /** + * Test SAP RFC connection type B configuration. + */ + public function testConnectionConfigTypeB() + { + if (!extension_loaded(static::getModuleName())) { + //load functions mocking SAP RFC module functions or class methods + $this->mockConnectionFailed(); + } + $saprfc = static::newSapRfc('RFC_PING', null, new ConfigTypeB([ + 'mshost' => 'msg.sap.example.com', + 'group' => 'grp01', + 'client' => '003', + 'user' => 'username', + 'passwd' => 'password' + ]), static::getApi('RFC_PING')); + static::assertInstanceOf(IConnection::class, $saprfc); + /** + * @var IConfigTypeB $cfg + */ + $cfg = $saprfc->getConfiguration(); + static::assertInstanceOf(IConfigTypeB::class, $cfg); + static::assertSame('msg.sap.example.com', $cfg->getMshost()); + static::assertSame('grp01', $cfg->getGroup()); + static::assertSame('003', $cfg->getClient()); + $saprfc->getConfiguration()->setMshost('grp01msg.sap.example.com'); + static::assertSame('grp01msg.sap.example.com', $cfg->getMshost()); + /** + * Try to establish a connection, which should fail because of example.com. + */ + $exception = null; + try { + $saprfc->invoke(); + } catch (IConnectionFailedException $exception) { + } + static::assertInstanceOf(IConnectionFailedException::class, $exception); + } + + /** + * Mock the SAP RFC module for a successful connection attempt. + */ + abstract protected function mockSuccessfulRfcPing(); + + /** + * Test a successful SAP remote function call to RFC_PING. + */ + public function testSuccessfulRfcPing() + { + if (!extension_loaded(static::getModuleName())) { + //load functions mocking SAP RFC module functions or class methods + $this->mockSuccessfulRfcPing(); + //load a bogus config + $config = static::getSampleSapConfig(); + } else { + //load a valid config + $config = static::getActualSapConfig(); + } + $rfcPing = static::newSapRfc('RFC_PING', null, $config); + static::assertInstanceOf(IFunction::class, $rfcPing); + $result = $rfcPing->invoke(); + static::assertSame([], $result); + } + + /** + * Data provider for incomplete configurations. + * @return array + */ + public static function provideIncompleteConfig() + { + return [ + [new ConfigTypeA()], + [new ConfigTypeA([ + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_ASHOST => 'sap.example.com', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_SYSNR => '00', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_GWHOST => 'sapgw.example.com', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeA([ + ConfigTypeA::JSON_GWSERV => 'sapsrv', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeB()], + [new ConfigTypeB([ + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeB([ + ConfigTypeB::JSON_R3NAME => 'name', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeB([ + ConfigTypeB::JSON_GROUP => 'group', + ConfigTypeA::JSON_CLIENT => '001', + ConfigTypeA::JSON_USER => 'username', + ConfigTypeA::JSON_PASSWD => 'password' + ])] + ]; + } + + /** + * Test a failed connection attempt using either the module or its mockup. + * @param \phpsap\interfaces\Config\IConfiguration $config + * @dataProvider provideIncompleteConfig + * @expectedException \phpsap\exceptions\IncompleteConfigException + */ + public function testIncompleteConfig($config) + { + /** + * Connection with empty configuration will be considered incomplete. + */ + static::newSapRfc('RFC_PING', null, $config, static::getApi('RFC_PING')) + ->invoke(); + } + + /** + * Mock the SAP RFC module for an unknown function call exception. + */ + abstract protected function mockUnknownFunctionException(); + + /** + * Test invoking an unknown function an receiving an exception. + * @expectedException \phpsap\exceptions\UnknownFunctionException + * @expectedExceptionMessageRegExp "^Unknown function RFC_PINGG: .*" + */ + public function testUnknownFunctionException() + { + if (!extension_loaded(static::getModuleName())) { + //load functions mocking SAP RFC module functions or class methods + $this->mockUnknownFunctionException(); + //load a bogus config + $config = static::getSampleSapConfig(); + } else { + //load a valid config + $config = static::getActualSapConfig(); + } + static::newSapRfc('RFC_PINGG', null, $config)->invoke(); + } + + /** + * Mock the SAP RFC module for a successful SAP remote function call with + * parameters and results. + */ + abstract protected function mockRemoteFunctionCallWithParametersAndResults(); + + /** + * Test successful SAP remote function call with parameters and results. + */ + public function testRemoteFunctionCallWithParametersAndResults() + { + if (!extension_loaded(static::getModuleName())) { + //load functions mocking SAP RFC module functions or class methods + $this->mockRemoteFunctionCallWithParametersAndResults(); + //load a bogus config + $config = static::getSampleSapConfig(); + } else { + //load a valid config + $config = static::getActualSapConfig(); + } + //prepare a DateTime object for testing SAP date and time. + $testDateTime = new \DateTime('2019-10-30 10:20:30'); + //prepare function call parameter + $test_in = [ + 'RFCFLOAT' => 70.11, + 'RFCCHAR1' => 'A', + 'RFCINT2' => 4095, + 'RFCINT1' => 163, + 'RFCCHAR4' => 'QqMh', + 'RFCINT4' => 416639, + 'RFCHEX3' => '53', //=S + 'RFCCHAR2' => 'XC', + 'RFCTIME' => $testDateTime->format(SapDateTime::SAP_TIME), + 'RFCDATE' => $testDateTime->format(SapDateTime::SAP_DATE), + 'RFCDATA1' => 'qKWjmNfad32rfS9Z', + 'RFCDATA2' => 'xi82ph2zJ8BCVtlR' + ]; + //remote function call + $result = static::newSapRfc('RFC_WALK_THRU_TEST') + ->setApi(static::getApi('RFC_WALK_THRU_TEST')) + ->setParam('TEST_IN', $test_in) + ->setParam('DESTINATIONS', [ + ['RFCDEST' => 'AOP3'] + ]) + ->invoke(); + //assert basics + static::assertInternalType('array', $result); + static::assertArrayHasKey('TEST_OUT', $result); + //create a link for programmer's convenience ... + $test_out = &$result['TEST_OUT']; + /** + * Assert float value. + */ + static::assertArrayHasKey('RFCFLOAT', $test_out, 'Missing RFCFLOAT in TEST_OUT!'); + static::assertSame(70.11, $test_out['RFCFLOAT'], 'Test IN and OUT of RFCFLOAT don\'t match!'); + /** + * Assert integer values. + */ + static::assertArrayHasKey('RFCINT1', $test_out, 'Missing RFCINT1 in TEST_OUT!'); + static::assertSame(163, $test_out['RFCINT1'], 'Test IN and OUT of RFCINT1 don\'t match!'); + static::assertArrayHasKey('RFCINT2', $test_out, 'Missing RFCINT2 in TEST_OUT!'); + static::assertSame(4095, $test_out['RFCINT2'], 'Test IN and OUT of RFCINT2 don\'t match!'); + static::assertArrayHasKey('RFCINT4', $test_out, 'Missing RFCINT4 in TEST_OUT!'); + static::assertSame(416639, $test_out['RFCINT4'], 'Test IN and OUT of RFCINT4 don\'t match!'); + /** + * Assert DateTime objects. + */ + static::assertArrayHasKey('RFCTIME', $test_out, 'Missing RFCTIME in TEST_OUT!'); + static::assertInstanceOf(\DateTime::class, $test_out['RFCTIME'], 'Test OUT of RFCTIME is not DateTime!'); + static::assertSame($testDateTime->format('H:i:s'), $test_out['RFCTIME']->format('H:i:s')); + static::assertArrayHasKey('RFCDATE', $test_out, 'Missing RFCDATE in TEST_OUT!'); + static::assertInstanceOf(\DateTime::class, $test_out['RFCDATE'], 'Test OUT of RFCDATE is not DateTime!'); + static::assertSame($testDateTime->format('Y-m-d'), $test_out['RFCDATE']->format('Y-m-d')); + /** + * Assert hexadecimal value. + */ + static::assertArrayHasKey('RFCHEX3', $test_out, 'Missing RFCHEX3 in TEST_OUT!'); + static::assertSame('S', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); + /** + * Assert string values. + */ + static::assertArrayHasKey('RFCCHAR1', $test_out, 'Missing RFCCHAR1 in TEST_OUT!'); + static::assertSame('A', $test_out['RFCCHAR1'], 'Test IN and OUT of RFCCHAR1 don\'t match!'); + static::assertArrayHasKey('RFCCHAR2', $test_out, 'Missing RFCCHAR2 in TEST_OUT!'); + static::assertSame('XC', $test_out['RFCCHAR2'], 'Test IN and OUT of RFCCHAR2 don\'t match!'); + static::assertArrayHasKey('RFCCHAR4', $test_out, 'Missing RFCCHAR4 in TEST_OUT!'); + static::assertSame('QqMh', $test_out['RFCCHAR4'], 'Test IN and OUT of RFCCHAR4 don\'t match!'); + static::assertArrayHasKey('RFCDATA1', $test_out, 'Missing RFCDATA1 in TEST_OUT!'); + static::assertSame('qKWjmNfad32rfS9Z', $test_out['RFCDATA1'], 'Test IN and OUT of RFCDATA1 don\'t match!'); + static::assertArrayHasKey('RFCDATA2', $test_out, 'Missing RFCDATA2 in TEST_OUT!'); + static::assertSame('xi82ph2zJ8BCVtlR', $test_out['RFCDATA2'], 'Test IN and OUT of RFCDATA2 don\'t match!'); + /** + * Assert empty table parameter 'DESTINATIONS'. + */ + static::assertArrayHasKey('DESTINATIONS', $result); + static::assertEmpty($result['DESTINATIONS']); + /** + * Assert return table 'LOG' and that RFCDEST is the same as in the + * 'DESTINATIONS' table parameter. + */ + static::assertArrayHasKey('LOG', $result); + static::assertArrayHasKey(0, $result['LOG']); + static::assertInternalType('array', $result['LOG'][0]); + static::assertArrayHasKey('RFCDEST', $result['LOG'][0]); + static::assertSame('AOP3', $result['LOG'][0]['RFCDEST']); + } + + /** + * Mock the SAP RFC module for a failed SAP remote function call with parameters. + */ + abstract protected function mockFailedRemoteFunctionCallWithParameters(); + + /** + * Test a failed remote function call with parameters. + * @expectedException \phpsap\exceptions\FunctionCallException + * @expectedExceptionMessageRegExp "^Function call RFC_READ_TABLE failed: .*" + */ + public function testFailedRemoteFunctionCallWithParameters() + { + if (!extension_loaded(static::getModuleName())) { + //load functions mocking SAP RFC module functions or class methods + $this->mockFailedRemoteFunctionCallWithParameters(); + //load a bogus config + $config = static::getSampleSapConfig(); + } else { + //load a valid config + $config = static::getActualSapConfig(); + } + static::newSapRfc('RFC_READ_TABLE') + ->setApi(static::getApi('RFC_READ_TABLE')) + ->setParam('QUERY_TABLE', '&') + ->invoke(); + } +} diff --git a/src/AbstractTestCase.php b/src/AbstractTestCase.php index d5bb045..6961489 100644 --- a/src/AbstractTestCase.php +++ b/src/AbstractTestCase.php @@ -2,14 +2,18 @@ namespace phpsap\IntegrationTests; +use phpsap\classes\Api\RemoteApi; use phpsap\classes\Config\ConfigTypeA; use phpsap\classes\Config\ConfigCommon; +use phpsap\classes\Config\ConfigTypeB; +use phpsap\interfaces\Api\IApi; use phpsap\interfaces\Config\IConfiguration; +use phpsap\interfaces\IFunction; /** * Class \phpsap\IntegrationTests\AbstractTestCase * - * Helper class defining methods the connection and function tests will need. + * Helper class defining methods the implementation specific tests will need. * * @package phpsap\IntegrationTests * @author Gregor J. @@ -37,29 +41,29 @@ abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase public function __construct($name = null, array $data = [], $dataName = '') { parent::__construct($name, $data, $dataName); - SapRfcModuleMocks::requireFile($this->getModuleTemplateFile()); - SapRfcModuleMocks::validModuleFunctions($this->getValidModuleFunctions()); + SapRfcModuleMocks::requireFile(static::getModuleTemplateFile()); + SapRfcModuleMocks::validModuleFunctions(static::getValidModuleFunctions()); } /** * Get a sample SAP config. * @return ConfigTypeA */ - protected function getSampleSapConfig() + public static function getSampleSapConfig() { return new ConfigTypeA(static::$sampleSapConfig); } /** * Load an actual sap configuration. - * @return ConfigTypeA + * @return ConfigTypeA|ConfigTypeB */ - protected function getSapConfig() + public static function getActualSapConfig() { /** * Actual implementation has to return the path of a valid configuration file. */ - $configFile = $this->getSapConfigFile(); + $configFile = static::getSapConfigFile(); if (file_exists($configFile) !== true) { throw new \RuntimeException(sprintf( 'Cannot find config file %s!', @@ -82,6 +86,52 @@ protected function getSapConfig() return ConfigCommon::jsonDecode($configJson); } + /** + * Load an actual SAP remote function call API description from file. + * @param string $remoteFunction + * @return \phpsap\classes\Api\RemoteApi + */ + public static function getApi($remoteFunction) + { + $file = __DIR__ . DIRECTORY_SEPARATOR; + $file .= sprintf('%s.json', $remoteFunction); + if (file_exists($file) !== true) { + throw new \RuntimeException(sprintf( + 'Cannot find SAP remote function API file %s!', + $file + )); + } + /** + * Try to read the configuration file. + */ + if (($json = file_get_contents($file)) === false) { + throw new \RuntimeException(sprintf( + 'Cannot read from SAP remote function API file %s!', + $file + )); + } + return RemoteApi::jsonDecode($json); + } + + /** + * Initialize the remote function call with at least a name. + * In order to add SAP remote function call parameters, an API needs to be + * defined. In case no SAP remote function call API has been defined, it will be + * queried on the fly by connecting to the SAP remote system. In order to + * connect to the SAP remote system, you need a connection configuration. + * @param string $name SAP remote function name. + * @param array|null $params SAP remote function call parameters. Default: null + * @param \phpsap\interfaces\Config\IConfiguration|null $config Connection configuration. Default: null + * @param \phpsap\interfaces\Api\IApi|null $api SAP remote function call API. Default: null + * @return IFunction + * @throws \phpsap\interfaces\exceptions\IInvalidArgumentException + */ + public static function newSapRfc($name, array $params = null, IConfiguration $config = null, IApi $api = null) + { + $class = static::getClassName(); + return new $class($name, $params, $config, $api); + } + /** * Mock a SAP RFC module specific function or method. * @param string $name @@ -92,34 +142,33 @@ public static function mock($name, $logic) SapRfcModuleMocks::singleton()->mock($name, $logic); } + /** + * Return the name of the class, used for testing. + * @return string + */ + abstract public static function getClassName(); + /** * Get the name of the PHP module. * @return string */ - abstract public function getModuleName(); + abstract public static function getModuleName(); /** * Get the path to the PHP/SAP configuration file. * @return string */ - abstract public function getSapConfigFile(); + abstract public static function getSapConfigFile(); /** * Get the path to the filename containing the SAP RFC module mockups. * @return string */ - abstract public function getModuleTemplateFile(); + abstract public static function getModuleTemplateFile(); /** * Get an array of valid SAP RFC module function or class method names. * @return array */ - abstract public function getValidModuleFunctions(); - - /** - * Create a new instance of a PHP/SAP connection class. - * @param IConfiguration|null $config The PHP/SAP configuration. Default: null - * @return \phpsap\interfaces\IConnection - */ - abstract public function newConnection(IConfiguration $config = null); + abstract public static function getValidModuleFunctions(); } diff --git a/src/RFC_PING.json b/src/RFC_PING.json new file mode 100644 index 0000000..c44dc44 --- /dev/null +++ b/src/RFC_PING.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file From e89d6dfb0a085f55855789b84954301663c23bf8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 16 Dec 2019 17:28:35 +0100 Subject: [PATCH 4/9] replace invalid class and add missing config settings --- src/AbstractSapRfcTestCase.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AbstractSapRfcTestCase.php b/src/AbstractSapRfcTestCase.php index 3413a37..6bf8774 100644 --- a/src/AbstractSapRfcTestCase.php +++ b/src/AbstractSapRfcTestCase.php @@ -78,7 +78,7 @@ public function testConnectionConfigTypeB() 'user' => 'username', 'passwd' => 'password' ]), static::getApi('RFC_PING')); - static::assertInstanceOf(IConnection::class, $saprfc); + static::assertInstanceOf(IFunction::class, $saprfc); /** * @var IConfigTypeB $cfg */ @@ -262,6 +262,7 @@ public function testRemoteFunctionCallWithParametersAndResults() //remote function call $result = static::newSapRfc('RFC_WALK_THRU_TEST') ->setApi(static::getApi('RFC_WALK_THRU_TEST')) + ->setConfiguration($config) ->setParam('TEST_IN', $test_in) ->setParam('DESTINATIONS', [ ['RFCDEST' => 'AOP3'] @@ -352,6 +353,7 @@ public function testFailedRemoteFunctionCallWithParameters() } static::newSapRfc('RFC_READ_TABLE') ->setApi(static::getApi('RFC_READ_TABLE')) + ->setConfiguration($config) ->setParam('QUERY_TABLE', '&') ->invoke(); } From ab5afd0ed7d1d6465a4a974dd0a466ca3bc822c6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 16 Dec 2019 17:29:15 +0100 Subject: [PATCH 5/9] add null to invalid config data provider --- src/AbstractSapRfcTestCase.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AbstractSapRfcTestCase.php b/src/AbstractSapRfcTestCase.php index 6bf8774..da2d3a3 100644 --- a/src/AbstractSapRfcTestCase.php +++ b/src/AbstractSapRfcTestCase.php @@ -132,6 +132,7 @@ public function testSuccessfulRfcPing() public static function provideIncompleteConfig() { return [ + [null], [new ConfigTypeA()], [new ConfigTypeA([ ConfigTypeA::JSON_CLIENT => '001', From 332353717d3d644988c9faaaa5ca528d10dd8fc1 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 17 Dec 2019 14:45:38 +0100 Subject: [PATCH 6/9] added new invalid config and removed superfluous addApi() calls --- src/AbstractSapRfcTestCase.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/AbstractSapRfcTestCase.php b/src/AbstractSapRfcTestCase.php index da2d3a3..151a806 100644 --- a/src/AbstractSapRfcTestCase.php +++ b/src/AbstractSapRfcTestCase.php @@ -180,6 +180,11 @@ public static function provideIncompleteConfig() ConfigTypeA::JSON_CLIENT => '001', ConfigTypeA::JSON_USER => 'username', ConfigTypeA::JSON_PASSWD => 'password' + ])], + [new ConfigTypeB([ + ConfigTypeB::JSON_MSHOST => 'mshost.example.com', + ConfigTypeB::JSON_LANG => 'DE', + ConfigTypeB::JSON_TRACE => ConfigTypeB::TRACE_FULL ])] ]; } @@ -262,7 +267,6 @@ public function testRemoteFunctionCallWithParametersAndResults() ]; //remote function call $result = static::newSapRfc('RFC_WALK_THRU_TEST') - ->setApi(static::getApi('RFC_WALK_THRU_TEST')) ->setConfiguration($config) ->setParam('TEST_IN', $test_in) ->setParam('DESTINATIONS', [ @@ -353,7 +357,6 @@ public function testFailedRemoteFunctionCallWithParameters() $config = static::getActualSapConfig(); } static::newSapRfc('RFC_READ_TABLE') - ->setApi(static::getApi('RFC_READ_TABLE')) ->setConfiguration($config) ->setParam('QUERY_TABLE', '&') ->invoke(); From 71264004981588e3fcd80ed9030ffc0a67085f09 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 30 Jan 2020 12:59:44 +0100 Subject: [PATCH 7/9] underlying SAP RFC modules have different capabilities when returning the function API. e.g. Harding and Kralik are missing the members and their datatype of struct and table values. --- src/AbstractSapRfcTestCase.php | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/AbstractSapRfcTestCase.php b/src/AbstractSapRfcTestCase.php index 151a806..0c4e366 100644 --- a/src/AbstractSapRfcTestCase.php +++ b/src/AbstractSapRfcTestCase.php @@ -296,16 +296,28 @@ public function testRemoteFunctionCallWithParametersAndResults() * Assert DateTime objects. */ static::assertArrayHasKey('RFCTIME', $test_out, 'Missing RFCTIME in TEST_OUT!'); - static::assertInstanceOf(\DateTime::class, $test_out['RFCTIME'], 'Test OUT of RFCTIME is not DateTime!'); - static::assertSame($testDateTime->format('H:i:s'), $test_out['RFCTIME']->format('H:i:s')); static::assertArrayHasKey('RFCDATE', $test_out, 'Missing RFCDATE in TEST_OUT!'); - static::assertInstanceOf(\DateTime::class, $test_out['RFCDATE'], 'Test OUT of RFCDATE is not DateTime!'); - static::assertSame($testDateTime->format('Y-m-d'), $test_out['RFCDATE']->format('Y-m-d')); + static::assertArrayHasKey('RFCHEX3', $test_out, 'Missing RFCHEX3 in TEST_OUT!'); /** - * Assert hexadecimal value. + * Assertions based on the capabilities of the underlying module. */ - static::assertArrayHasKey('RFCHEX3', $test_out, 'Missing RFCHEX3 in TEST_OUT!'); - static::assertSame('S', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); + if (is_string($test_out['RFCTIME'])) { + static::assertSame('102030', $test_out['RFCTIME']); + static::assertSame('20191030', $test_out['RFCDATE']); + /** + * Assert hexadecimal value. + */ + static::assertSame('53', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); + } else { + static::assertInstanceOf(\DateTime::class, $test_out['RFCTIME'], 'Test OUT of RFCTIME is not DateTime!'); + static::assertSame($testDateTime->format('H:i:s'), $test_out['RFCTIME']->format('H:i:s')); + static::assertInstanceOf(\DateTime::class, $test_out['RFCDATE'], 'Test OUT of RFCDATE is not DateTime!'); + static::assertSame($testDateTime->format('Y-m-d'), $test_out['RFCDATE']->format('Y-m-d')); + /** + * Assert hexadecimal value. + */ + static::assertSame('S', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); + } /** * Assert string values. */ From 8a0046c6caf077a1bfb10d16e1b0beb7d9dee8c3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 31 Jan 2020 13:55:19 +0100 Subject: [PATCH 8/9] replace try-catch with expectedException --- src/AbstractSapRfcTestCase.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/AbstractSapRfcTestCase.php b/src/AbstractSapRfcTestCase.php index 0c4e366..0f7eb4a 100644 --- a/src/AbstractSapRfcTestCase.php +++ b/src/AbstractSapRfcTestCase.php @@ -28,6 +28,7 @@ abstract protected function mockConnectionFailed(); /** * Test SAP RFC connection type A configuration. + * @expectedException \phpsap\interfaces\exceptions\IConnectionFailedException */ public function testConnectionConfigTypeA() { @@ -54,16 +55,12 @@ public function testConnectionConfigTypeA() /** * Try to establish a connection, which should fail because of example.com. */ - $exception = null; - try { - $saprfc->invoke(); - } catch (IConnectionFailedException $exception) { - } - static::assertInstanceOf(IConnectionFailedException::class, $exception); + $saprfc->invoke(); } /** * Test SAP RFC connection type B configuration. + * @expectedException \phpsap\interfaces\exceptions\IConnectionFailedException */ public function testConnectionConfigTypeB() { @@ -92,12 +89,7 @@ public function testConnectionConfigTypeB() /** * Try to establish a connection, which should fail because of example.com. */ - $exception = null; - try { - $saprfc->invoke(); - } catch (IConnectionFailedException $exception) { - } - static::assertInstanceOf(IConnectionFailedException::class, $exception); + $saprfc->invoke(); } /** From c9874de263ae8e131f6cabaa5322ac2f0bea47bf Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 31 Jan 2020 15:32:19 +0100 Subject: [PATCH 9/9] use extracted complete API of RFC_WALK_THRU_TEST in order to test all possible data types --- src/AbstractSapRfcTestCase.php | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/AbstractSapRfcTestCase.php b/src/AbstractSapRfcTestCase.php index 0f7eb4a..931a81a 100644 --- a/src/AbstractSapRfcTestCase.php +++ b/src/AbstractSapRfcTestCase.php @@ -260,6 +260,7 @@ public function testRemoteFunctionCallWithParametersAndResults() //remote function call $result = static::newSapRfc('RFC_WALK_THRU_TEST') ->setConfiguration($config) + ->setApi(static::getApi('RFC_WALK_THRU_TEST')) ->setParam('TEST_IN', $test_in) ->setParam('DESTINATIONS', [ ['RFCDEST' => 'AOP3'] @@ -289,27 +290,15 @@ public function testRemoteFunctionCallWithParametersAndResults() */ static::assertArrayHasKey('RFCTIME', $test_out, 'Missing RFCTIME in TEST_OUT!'); static::assertArrayHasKey('RFCDATE', $test_out, 'Missing RFCDATE in TEST_OUT!'); - static::assertArrayHasKey('RFCHEX3', $test_out, 'Missing RFCHEX3 in TEST_OUT!'); + static::assertInstanceOf(\DateTime::class, $test_out['RFCTIME'], 'Test OUT of RFCTIME is not DateTime!'); + static::assertSame($testDateTime->format('H:i:s'), $test_out['RFCTIME']->format('H:i:s')); + static::assertInstanceOf(\DateTime::class, $test_out['RFCDATE'], 'Test OUT of RFCDATE is not DateTime!'); + static::assertSame($testDateTime->format('Y-m-d'), $test_out['RFCDATE']->format('Y-m-d')); /** - * Assertions based on the capabilities of the underlying module. + * Assert hexadecimal value. */ - if (is_string($test_out['RFCTIME'])) { - static::assertSame('102030', $test_out['RFCTIME']); - static::assertSame('20191030', $test_out['RFCDATE']); - /** - * Assert hexadecimal value. - */ - static::assertSame('53', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); - } else { - static::assertInstanceOf(\DateTime::class, $test_out['RFCTIME'], 'Test OUT of RFCTIME is not DateTime!'); - static::assertSame($testDateTime->format('H:i:s'), $test_out['RFCTIME']->format('H:i:s')); - static::assertInstanceOf(\DateTime::class, $test_out['RFCDATE'], 'Test OUT of RFCDATE is not DateTime!'); - static::assertSame($testDateTime->format('Y-m-d'), $test_out['RFCDATE']->format('Y-m-d')); - /** - * Assert hexadecimal value. - */ - static::assertSame('S', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); - } + static::assertArrayHasKey('RFCHEX3', $test_out, 'Missing RFCHEX3 in TEST_OUT!'); + static::assertSame('S', $test_out['RFCHEX3'], 'Test IN and OUT of RFCHEX3 don\'t match!'); /** * Assert string values. */