Skip to content

Instantly share code, notes, and snippets.

@Vinai
Last active December 25, 2015 05:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Vinai/6922140 to your computer and use it in GitHub Desktop.
Save Vinai/6922140 to your computer and use it in GitHub Desktop.
PHPUnit bootstrap example for OrderManagement
<?php
$path = __DIR__ . '/../';
$mage = 'app/Mage.php';
$i = 0;
while (! file_exists($path . $mage) && ! file_exists($path . 'htdocs/' . $mage) && $i++ < 10) {
$path .= '../';
}
chdir(file_exists($path . $mage) ? $path : $path . 'htdocs');
require_once $mage;
// require_once 'vendor/autoload.php';
Mage::setIsDeveloperMode(true);
Mage::app('admin');
<?php
class Training_OrderIntegrationClient_Model_Client
{
protected $_socket;
/**
* Workaround to make class testable
*
* @var bool $_doReadOnSend
*/
protected $_doReadOnSend = true;
protected function _initSocket()
{
if (! $this->_socket) {
$ip = Mage::getStoreConfig('training/orderintegration_client/server_ip');
$port = Mage::getStoreConfig('training/orderintegration_client/server_port');
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (! $socket ||! socket_connect($socket, $ip, $port)) {
Mage::throwException("Unable to connect to OrderIntegration server $ip:$port");
}
$this->_socket = $socket;
}
}
protected function _send($msg)
{
$this->_initSocket();
if (mb_substr($msg, -1) !== "\n") {
$msg .= "\n";
}
$result = false !== socket_write($this->_socket, $msg, mb_strlen($msg));
if ($this->_doReadOnSend) {
$result = trim($this->_read());
}
return $result;
}
protected function _read()
{
$this->_initSocket();
$response = socket_read($this->_socket, 4096);
return $response;
}
/**
* Workaround to make class testable
*
* @param $flag
*/
public function setDoReadOnSend($flag)
{
$this->_doReadOnSend = (bool) $flag;
}
public function close()
{
if ($this->_socket) {
socket_close($this->_socket);
}
}
public function push(Mage_Sales_Model_Order $order)
{
if (! $order->getId()) {
Mage::throwException("Unable to push unsaved order model!\n");
}
$result = $this->_send("pull {$order->getId()}");
return '1' === $result;
}
public function canInvoice(Mage_Sales_Model_Order $order)
{
if (! $order->getId()) {
Mage::throwException("Unable to check capability for unsaved order model!\n");
}
$result = $this->_send("availability {$order->getId()} invoice");
return '1' === $result;
}
public function invoice(Mage_Sales_Model_Order $order)
{
if (! $order->getId()) {
Mage::throwException("Unable to check capability for unsaved order model!\n");
}
$this->_send("sync off");
$result = $this->_send("invoice {$order->getId()}");
$this->_send("sync on");
return '1' === $result;
}
public function canShip(Mage_Sales_Model_Order $order)
{
if (! $order->getId()) {
Mage::throwException("Unable to check capability for unsaved order model!\n");
}
$result = $this->_send("availability {$order->getId()} ship");
return '1' === $result;
}
public function ship(Mage_Sales_Model_Order $order)
{
if (! $order->getId()) {
Mage::throwException("Unable to check capability for unsaved order model!\n");
}
$this->_send("sync off");
$result = $this->_send("ship {$order->getId()}");
$this->_send("sync on");
return '1' === $result;
}
public function canRefund(Mage_Sales_Model_Order $order)
{
if (! $order->getId()) {
Mage::throwException("Unable to check capability for unsaved order model!\n");
}
$result = $this->_send("availability {$order->getId()} refund");
return '1' === $result;
}
public function refund(Mage_Sales_Model_Order $order)
{
if (! $order->getId()) {
Mage::throwException("Unable to check capability for unsaved order model!\n");
}
$this->_send("sync off");
$result = $this->_send("refund {$order->getId()}");
$this->_send("sync on");
return '1' === $result;
}
}
<?php
class ClientTest extends PHPUnit_Framework_TestCase
{
protected $sock;
protected $client;
protected $order;
protected function setUp()
{
$ip = '127.0.0.1';
$port = '0';
$this->initSocket($ip, $port);
$this->setServerConfig($ip, $port);
$this->mockOrder();
}
protected function initSocket(&$ip, &$port)
{
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sock, $ip, $port);
socket_listen($sock, 3);
$this->sock = $sock;
// Update the port number to what was assigned for 0
socket_getsockname($sock, $ip, $port);
}
protected function setServerConfig($ip, $port)
{
Mage::app()->getStore()
->setConfig('training/orderintegration_client/server_ip', $ip)
->setConfig('training/orderintegration_client/server_port', $port);
}
protected function mockOrder()
{
$order = $this->getMock('Mage_Sales_Model_Order');
$order->expects($this->exactly(2))
->method('getId')
->will($this->returnValue(1));
$this->order = $order;
}
protected function getClient()
{
$client = Mage::getModel('training_orderIntegrationClient/client');
$client->setDoReadOnSend(false);
return $client;
}
protected function readSentData()
{
if (! isset($this->client)) {
$this->client = socket_accept($this->sock);
}
$buffer = socket_read($this->client, 2048, PHP_BINARY_READ);
return $buffer;
}
public function testPush()
{
$this->getClient()->push($this->order);
usleep(50000); // wait 0.05 sec
$result = $this->readSentData();
$this->assertEquals("pull 1\n", $result);
}
public function testCanInvoice()
{
$this->getClient()->canInvoice($this->order);
usleep(50000); // wait 0.05 sec
$result = $this->readSentData();
$this->assertEquals("availability 1 invoice\n", $result);
}
public function testInvoice()
{
$this->getClient()->invoice($this->order);
usleep(50000); // wait 0.05 sec
$result = $this->readSentData();
$this->assertEquals("sync off\ninvoice 1\nsync on\n", $result);
}
public function testCanShip()
{
$this->getClient()->canShip($this->order);
usleep(50000); // wait 0.05 sec
$result = $this->readSentData();
$this->assertEquals("availability 1 ship\n", $result);
}
public function testShip()
{
$this->getClient()->ship($this->order);
usleep(50000); // wait 0.05 sec
$result = $this->readSentData();
$this->assertEquals("sync off\nship 1\nsync on\n", $result);
}
public function testCanRefund()
{
$this->getClient()->canRefund($this->order);
usleep(50000); // wait 0.05 sec
$result = $this->readSentData();
$this->assertEquals("availability 1 refund\n", $result);
}
public function testRefund()
{
$this->getClient()->refund($this->order);
usleep(50000); // wait 0.05 sec
$result = $this->readSentData();
$this->assertEquals("sync off\nrefund 1\nsync on\n", $result);
}
}
<?xml version="1.0"?>
<config>
<global>
<models>
<training_orderIntegrationClient>
<class>Training_OrderIntegrationClient_Model</class>
</training_orderIntegrationClient>
</models>
<events>
<sales_model_service_quote_submit_success>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesModelServiceQuoteSubmitSuccess</method>
</training_orderIntegrationClient>
</observers>
</sales_model_service_quote_submit_success>
<sales_order_save_commit_after>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesOrderSaveCommitAfter</method>
</training_orderIntegrationClient>
</observers>
</sales_order_save_commit_after>
<sales_order_invoice_save_before>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesOrderInvoiceSaveBefore</method>
</training_orderIntegrationClient>
</observers>
</sales_order_invoice_save_before>
<sales_order_invoice_save_commit_after>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesOrderInvoiceSaveCommitAfter</method>
</training_orderIntegrationClient>
</observers>
</sales_order_invoice_save_commit_after>
<sales_order_shipment_save_before>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesOrderShipmentSaveBefore</method>
</training_orderIntegrationClient>
</observers>
</sales_order_shipment_save_before>
<sales_order_shipment_save_commit_after>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesOrderShipmentSaveCommitAfter</method>
</training_orderIntegrationClient>
</observers>
</sales_order_shipment_save_commit_after>
<sales_order_creditmemo_save_before>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesOrderCreditmemoSaveBefore</method>
</training_orderIntegrationClient>
</observers>
</sales_order_creditmemo_save_before>
<sales_order_creditmemo_save_commit_after>
<observers>
<training_orderIntegrationClient>
<class>training_orderIntegrationClient/observer</class>
<method>salesOrderCreditmemoSaveCommitAfter</method>
</training_orderIntegrationClient>
</observers>
</sales_order_creditmemo_save_commit_after>
</events>
</global>
<default>
<training>
<orderintegration_client>
<server_ip>127.0.0.1</server_ip>
<server_port>8888</server_port>
</orderintegration_client>
</training>
</default>
</config>
<?php
class Training_OrderIntegrationClient_Model_Observer
{
const NEW_FLAG = 'orderintegration_object_new_flag';
/**
* @return Training_OrderIntegrationClient_Model_Client
*/
protected function _getClient()
{
return Mage::getSingleton('training_orderIntegrationClient/client');
}
public function salesModelServiceQuoteSubmitSuccess($args)
{
$order = $args->getOrder();
$this->_getClient()->push($order);
}
public function salesOrderSaveCommitAfter($args)
{
$order = $args->getOrder();
$this->_getClient()->push($order);
}
public function salesOrderInvoiceSaveBefore($args)
{
$invoice = $args->getInvoice();
if ($invoice->isObjectNew()) {
$invoice->setData(self::NEW_FLAG, true);
$order = $invoice->getOrder();
if (! $this->_getClient()->canInvoice($order)) {
Mage::throwException("Synchronization Error: invoice capability not available for order {$order->getId()}");
}
}
}
public function salesOrderInvoiceSaveCommitAfter($args)
{
$invoice = $args->getInvoice();
$order = $invoice->getOrder();
if ($invoice->getData(self::NEW_FLAG)) {
$invoice->unsetData(self::NEW_FLAG);
if (! $this->_getClient()->invoice($order)) {
Mage::throwException("Synchronization Error: unable to synchronize invoice for order {$order->getId()}");
}
} else {
$this->_getClient()->push($order);
}
}
public function salesOrderShipmentSaveBefore($args)
{
$shipment = $args->getShipment();
if ($shipment->isObjectNew()) {
$shipment->setData(self::NEW_FLAG, true);
$order = $shipment->getOrder();
if (! $this->_getClient()->canShip($order)) {
Mage::throwException("Synchronization Error: ship capability not available for order {$order->getId()}");
}
}
}
public function salesOrderShipmentSaveCommitAfter($args)
{
$shipment = $args->getShipment();
$order = $shipment->getOrder();
if ($shipment->getData(self::NEW_FLAG)) {
$shipment->unsetData(self::NEW_FLAG);
if (! $this->_getClient()->ship($order)) {
Mage::throwException("Synchronization Error: unable to synchronize shipment for order {$order->getId()}");
}
} else {
$this->_getClient()->push($order);
}
}
public function salesOrderCreditmemoSaveBefore($args)
{
$creditmemo = $args->getCreditmemo();
if ($creditmemo->isObjectNew()) {
$creditmemo->setData(self::NEW_FLAG, true);
$order = $creditmemo->getOrder();
if (! $this->_getClient()->canRefund($order)) {
Mage::throwException("Synchronization Error: refund capability not available for order {$order->getId()}");
}
}
}
public function salesOrderCreditmemoSaveCommitAfter($args)
{
$creditmemo = $args->getCreditmemo();
$order = $creditmemo->getOrder();
if ($creditmemo->getData(self::NEW_FLAG)) {
$creditmemo->unsetData(self::NEW_FLAG);
if (! $this->_getClient()->refund($order)) {
Mage::throwException("Synchronization Error: unable to synchronize refund for order {$order->getId()}");
}
} else {
$this->_getClient()->push($order);
}
}
}
<?php
class ObserverTest extends PHPUnit_Framework_TestCase
{
/** @var PHPUnit_Framework_MockObject_MockObject */
protected $mockClient;
protected function setUp()
{
$this->mockApiClient();
$this->mockCustomerSession();
$this->setCurrentCurrency();
}
/**
* @return Mage_Sales_Model_Order
*/
public function testOrderPlacement()
{
$this->mockClient
->expects($this->atLeastOnce())
->method('push')
->will($this->returnValue(true));
$quote = $this->getQuoteFixture();
$service = Mage::getModel('sales/service_quote', $quote);
$order = $service->submitOrder();
return $order;
}
/**
* @param Mage_Sales_Model_Order $order
* @test
* @depends testOrderPlacement
*/
public function testOrderUpdated(Mage_Sales_Model_Order $order)
{
$this->mockClient
->expects($this->atLeastOnce())
->method('push')
->will($this->returnValue(true));
$order->setStatus('test')->save();
}
/**
* @param Mage_Sales_Model_Order $order
* @returns Mage_Sales_Model_Order
* @test
* @depends testOrderPlacement
*/
public function testOrderInvoiced(Mage_Sales_Model_Order $order)
{
$this->mockClient
->expects($this->once())
->method('canInvoice')
->will($this->returnValue(true));
$this->mockClient
->expects($this->once())
->method('invoice')
->will($this->returnValue(true));
$order->prepareInvoice()
->register()
->save();
$order->save();
return $order;
}
/**
* @param Mage_Sales_Model_Order $order
* @test
* @depends testOrderPlacement
*/
public function testOrderShipped(Mage_Sales_Model_Order $order)
{
$this->mockClient
->expects($this->once())
->method('canShip')
->will($this->returnValue(true));
$this->mockClient
->expects($this->once())
->method('ship')
->will($this->returnValue(true));
$order->prepareShipment()
->register()
->save();
}
/**
* @param Mage_Sales_Model_Order $order
* @test
* @depends testOrderInvoiced
*/
public function testOrderRefunded(Mage_Sales_Model_Order $order)
{
$this->mockClient
->expects($this->once())
->method('canRefund')
->will($this->returnValue(true));
$this->mockClient
->expects($this->once())
->method('refund')
->will($this->returnValue(true));
$service = Mage::getModel('sales/service_order', $order);
$service->prepareCreditmemo()
->refund()
->save();
}
/* ---------------------------- */
protected function setSingleton($object, $type)
{
$key = '_singleton/' . $type;
Mage::unregister($key);
Mage::register($key, $object);
}
protected function mockApiClient()
{
$mockClient = $this->getMockBuilder('Training_OrderIntegrationClient_Model_Client')
->setMethods(
array('push', 'canInvoice', 'invoice', 'canShip', 'ship', 'canRefund', 'refund')
)
->getMock();
$this->setSingleton($mockClient, 'training_orderIntegrationClient/client');
$this->mockClient = $mockClient;
}
protected function mockCustomerSession()
{
$mockCustomerSession = $this->getMockBuilder('Mage_Customer_Model_Session')
->setMethods(array('getCustomerGroupId'))
->disableOriginalConstructor(true)
->getMock();
$mockCustomerSession->expects($this->any())
->method('getCustomerGroupId')
->will($this->returnValue(Mage_Customer_Model_Group::NOT_LOGGED_IN_ID));
$this->setSingleton($mockCustomerSession, 'customer/session');
}
/**
* Create a guest quote with one item
*
* @return Mage_Sales_Model_Quote
*/
protected function getQuoteFixture()
{
$product = $this->getSimpleProduct();
$quote = Mage::getModel('sales/quote');
$quote->addProduct($product);
$quote->setCustomerIsGuest(true);
$quote->setCustomerEmail('test@example.com');
$this->prepareQuoteAddress($quote->getShippingAddress());
$this->prepareQuoteAddress($quote->getBillingAddress());
$quote->getShippingAddress()
->setCollectShippingRates(true)
->setShippingMethod('flatrate_flatrate');
$quote->getPayment()->setMethod('checkmo');
$quote->collectTotals();
return $quote;
}
/**
* Return a random active, in-stock, simple product
*
* @return Mage_Catalog_Model_Product
*/
protected function getSimpleProduct()
{
$collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addPriceData(Mage_Customer_Model_Group::NOT_LOGGED_IN_ID)
->addAttributeToFilter('type_id', 'simple')
->addAttributeToFilter('required_options', 0)
->addAttributeToFilter('status', 1)
->addWebsiteFilter(Mage::app()->getDefaultStoreView()->getWebsite()->getId())
->setPage(1, 1);
Mage::getModel('cataloginventory/stock')->addInStockFilterToCollection($collection);
/** @var Mage_Catalog_Model_Product $product */
$product = $collection->getFirstItem();
return $product;
}
protected function prepareQuoteAddress(Mage_Sales_Model_Quote_Address $address)
{
$address->addData(array(
'country_id' => 'US',
'firstname' => 'Test',
'lastname' => 'Test',
'street' => 'Test',
'city' => 'Test',
'postcode' => '123456',
'region' => 'CA',
'telephone' => '123456',
));
}
protected function setCurrentCurrency()
{
// Avoid core session being initialized
$store = Mage::app()->getStore();
$currency = Mage::getModel('directory/currency')->load(
$store->getDefaultCurrencyCode()
);
$store->setCurrentCurrency($currency);
}
}
<?xml version="1.0"?>
<phpunit colors="true" bootstrap="bootstrap.php" strict="false">
<testsuite name="Module Tests">
<directory suffix="Test.php">integration/</directory>
</testsuite>
</phpunit>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment