Completed
Push — master ( 81113d...22e795 )
by Aimeos
02:42
created

StandardTest   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 432
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 7
dl 0
loc 432
rs 10
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 8 1
A tearDown() 0 5 1
B testDelete() 0 26 1
A testDeleteControllerException() 0 10 1
A testDeleteMShopException() 0 10 1
A testDeleteException() 0 11 1
A testGet() 0 19 1
B testGetById() 0 28 1
A testGetNoAccess() 0 19 1
B testGetIncluded() 0 28 1
B testGetIncludedNone() 0 27 1
A testGetControllerException() 0 11 1
A testGetMShopException() 0 11 1
A testGetException() 0 11 1
B testPatch() 0 32 1
A testPatchControllerException() 0 14 1
A testPatchMShopException() 0 14 1
A testPatchException() 0 14 1
B testPost() 0 24 1
A testPostControllerException() 0 14 1
A testPostMShopException() 0 14 1
A testPostException() 0 8 1
A getObject() 0 16 1
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017
6
 */
7
8
9
namespace Aimeos\Client\JsonApi\Customer;
10
11
12
class StandardTest extends \PHPUnit_Framework_TestCase
13
{
14
	private $context;
15
	private $object;
16
	private $view;
17
18
19
	protected function setUp()
20
	{
21
		$this->context = \TestHelperJapi::getContext();
22
		$templatePaths = \TestHelperJapi::getTemplatePaths();
23
		$this->view = $this->context->getView();
24
25
		$this->object = new \Aimeos\Client\JsonApi\Customer\Standard( $this->context, $this->view, $templatePaths, 'customer' );
26
	}
27
28
29
	protected function tearDown()
30
	{
31
		\Aimeos\Controller\Frontend\Customer\Factory::injectController( '\Aimeos\Controller\Frontend\Customer\Standard', null );
32
		unset( $this->context, $this->object, $this->view );
33
	}
34
35
36
	public function testDelete()
37
	{
38
		$manager = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' );
39
		$item = $manager->createItem()->setCode( 'unittest-japi' );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Item\Iface as the method setCode() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Item\Standard, Aimeos\MShop\Catalog\Item\Standard, Aimeos\MShop\Common\Item\Type\Standard, Aimeos\MShop\Coupon\Item\Code\Standard, Aimeos\MShop\Customer\Item\Base, Aimeos\MShop\Customer\Item\Group\Standard, Aimeos\MShop\Customer\Item\Standard, Aimeos\MShop\Locale\Item\Currency\Standard, Aimeos\MShop\Locale\Item\Language\Standard, Aimeos\MShop\Locale\Item\Site\Standard, Aimeos\MShop\Order\Item\Base\Coupon\Standard, Aimeos\MShop\Order\Item\...duct\Attribute\Standard, Aimeos\MShop\Order\Item\...vice\Attribute\Standard, Aimeos\MShop\Order\Item\Base\Service\Base, Aimeos\MShop\Order\Item\Base\Service\Standard, Aimeos\MShop\Product\Item\Standard, Aimeos\MShop\Service\Item\Standard, Aimeos\MShop\Supplier\Item\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
40
		$manager->saveItem( $item );
41
42
		$this->context->setUserId( $item->getId() );
43
44
		$params = array( 'id' => $item->getId() );
45
		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $this->view, $params );
46
		$this->view->addHelper( 'param', $helper );
47
48
49
		$response = $this->object->delete( $this->view->request(), $this->view->response() );
50
		$result = json_decode( (string) $response->getBody(), true );
51
52
		$this->assertEquals( 200, $response->getStatusCode() );
53
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
54
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
55
56
		$this->assertEquals( 0, $result['meta']['total'] );
57
		$this->assertArrayNotHasKey( 'errors', $result );
58
59
		$this->setExpectedException( '\Aimeos\MShop\Exception' );
60
		$manager->findItem( 'unittest-japi' );
61
	}
62
63
64
	public function testDeleteControllerException()
65
	{
66
		$object = $this->getObject( 'deleteItem', $this->throwException( new \Aimeos\Controller\Frontend\Customer\Exception() ) );
67
68
		$response = $object->delete( $this->view->request(), $this->view->response() );
69
		$result = json_decode( (string) $response->getBody(), true );
70
71
		$this->assertEquals( 403, $response->getStatusCode() );
72
		$this->assertArrayHasKey( 'errors', $result );
73
	}
74
75
76
	public function testDeleteMShopException()
77
	{
78
		$object = $this->getObject( 'deleteItem', $this->throwException( new \Aimeos\MShop\Exception() ) );
79
80
		$response = $object->delete( $this->view->request(), $this->view->response() );
81
		$result = json_decode( (string) $response->getBody(), true );
82
83
		$this->assertEquals( 404, $response->getStatusCode() );
84
		$this->assertArrayHasKey( 'errors', $result );
85
	}
86
87
88
	public function testDeleteException()
89
	{
90
		$object = $this->getObject( 'deleteItem', $this->throwException( new \Exception() ) );
91
92
		$response = $object->delete( $this->view->request(), $this->view->response() );
93
		$result = json_decode( (string) $response->getBody(), true );
94
95
96
		$this->assertEquals( 500, $response->getStatusCode() );
97
		$this->assertArrayHasKey( 'errors', $result );
98
	}
99
100
101
	public function testGet()
102
	{
103
		$user = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' )->findItem( 'UTC001' );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Manager\Iface as the method findItem() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Manager\Lists\Type\Standard, Aimeos\MShop\Attribute\Manager\Standard, Aimeos\MShop\Attribute\Manager\Type\Standard, Aimeos\MShop\Catalog\Manager\Decorator\Base, Aimeos\MShop\Catalog\Manager\Decorator\Sitecheck, Aimeos\MShop\Catalog\Manager\Lists\Type\Standard, Aimeos\MShop\Catalog\Manager\Standard, Aimeos\MShop\Common\Manager\Decorator\Base, Aimeos\MShop\Common\Manager\Decorator\Changelog, Aimeos\MShop\Common\Manager\Decorator\Sitecheck, Aimeos\MShop\Common\Manager\Type\Base, Aimeos\MShop\Coupon\Manager\Code\Standard, Aimeos\MShop\Customer\Manager\Base, Aimeos\MShop\Customer\Manager\Group\Standard, Aimeos\MShop\Customer\Manager\Lists\Type\Standard, Aimeos\MShop\Customer\Manager\Standard, Aimeos\MShop\Locale\Manager\Site\Standard, Aimeos\MShop\Media\Manager\Lists\Type\Standard, Aimeos\MShop\Media\Manager\Type\Standard, Aimeos\MShop\Plugin\Manager\Type\Standard, Aimeos\MShop\Price\Manager\Lists\Type\Standard, Aimeos\MShop\Price\Manager\Type\Standard, Aimeos\MShop\Product\Manager\Lists\Type\Standard, Aimeos\MShop\Product\Man...\Property\Type\Standard, Aimeos\MShop\Product\Manager\Standard, Aimeos\MShop\Product\Manager\Type\Standard, Aimeos\MShop\Service\Manager\Lists\Type\Standard, Aimeos\MShop\Service\Manager\Standard, Aimeos\MShop\Service\Manager\Type\Standard, Aimeos\MShop\Stock\Manager\Standard, Aimeos\MShop\Stock\Manager\Type\Standard, Aimeos\MShop\Supplier\Manager\Lists\Type\Standard, Aimeos\MShop\Supplier\Manager\Standard, Aimeos\MShop\Tag\Manager\Type\Standard, Aimeos\MShop\Text\Manager\Lists\Type\Standard, Aimeos\MShop\Text\Manager\Type\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
104
		$this->context->setUserId( $user->getId() );
105
106
		$response = $this->object->get( $this->view->request(), $this->view->response() );
107
		$result = json_decode( (string) $response->getBody(), true );
108
109
		$this->assertEquals( 200, $response->getStatusCode() );
110
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
111
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
112
113
		$this->assertEquals( 1, $result['meta']['total'] );
114
		$this->assertEquals( 'customer', $result['data']['type'] );
115
		$this->assertGreaterThan( 13, count( $result['data']['attributes'] ) );
116
		$this->assertEquals( 0, count( $result['included'] ) );
117
118
		$this->assertArrayNotHasKey( 'errors', $result );
119
	}
120
121
122
	public function testGetById()
123
	{
124
		$user = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' )->findItem( 'UTC001' );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Manager\Iface as the method findItem() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Manager\Lists\Type\Standard, Aimeos\MShop\Attribute\Manager\Standard, Aimeos\MShop\Attribute\Manager\Type\Standard, Aimeos\MShop\Catalog\Manager\Decorator\Base, Aimeos\MShop\Catalog\Manager\Decorator\Sitecheck, Aimeos\MShop\Catalog\Manager\Lists\Type\Standard, Aimeos\MShop\Catalog\Manager\Standard, Aimeos\MShop\Common\Manager\Decorator\Base, Aimeos\MShop\Common\Manager\Decorator\Changelog, Aimeos\MShop\Common\Manager\Decorator\Sitecheck, Aimeos\MShop\Common\Manager\Type\Base, Aimeos\MShop\Coupon\Manager\Code\Standard, Aimeos\MShop\Customer\Manager\Base, Aimeos\MShop\Customer\Manager\Group\Standard, Aimeos\MShop\Customer\Manager\Lists\Type\Standard, Aimeos\MShop\Customer\Manager\Standard, Aimeos\MShop\Locale\Manager\Site\Standard, Aimeos\MShop\Media\Manager\Lists\Type\Standard, Aimeos\MShop\Media\Manager\Type\Standard, Aimeos\MShop\Plugin\Manager\Type\Standard, Aimeos\MShop\Price\Manager\Lists\Type\Standard, Aimeos\MShop\Price\Manager\Type\Standard, Aimeos\MShop\Product\Manager\Lists\Type\Standard, Aimeos\MShop\Product\Man...\Property\Type\Standard, Aimeos\MShop\Product\Manager\Standard, Aimeos\MShop\Product\Manager\Type\Standard, Aimeos\MShop\Service\Manager\Lists\Type\Standard, Aimeos\MShop\Service\Manager\Standard, Aimeos\MShop\Service\Manager\Type\Standard, Aimeos\MShop\Stock\Manager\Standard, Aimeos\MShop\Stock\Manager\Type\Standard, Aimeos\MShop\Supplier\Manager\Lists\Type\Standard, Aimeos\MShop\Supplier\Manager\Standard, Aimeos\MShop\Tag\Manager\Type\Standard, Aimeos\MShop\Text\Manager\Lists\Type\Standard, Aimeos\MShop\Text\Manager\Type\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
125
		$this->context->setUserId( $user->getId() );
126
127
		$params = array(
128
			'id' => $user->getId(),
129
			'fields' => array( 'customer' => 'customer.id,customer.code' ),
130
		);
131
		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $this->view, $params );
132
		$this->view->addHelper( 'param', $helper );
133
134
135
		$response = $this->object->get( $this->view->request(), $this->view->response() );
136
		$result = json_decode( (string) $response->getBody(), true );
137
138
		$this->assertEquals( 200, $response->getStatusCode() );
139
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
140
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
141
142
		$this->assertEquals( 1, $result['meta']['total'] );
143
		$this->assertEquals( 'customer', $result['data']['type'] );
144
		$this->assertNotNull( $result['data']['id'] );
145
		$this->assertGreaterThan( 1, count( $result['data']['attributes'] ) );
146
		$this->assertEquals( 'UTC001', $result['data']['attributes']['customer.code'] );
147
148
		$this->assertArrayNotHasKey( 'errors', $result );
149
	}
150
151
152
	public function testGetNoAccess()
153
	{
154
		$user = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' )->findItem( 'UTC001' );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Manager\Iface as the method findItem() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Manager\Lists\Type\Standard, Aimeos\MShop\Attribute\Manager\Standard, Aimeos\MShop\Attribute\Manager\Type\Standard, Aimeos\MShop\Catalog\Manager\Decorator\Base, Aimeos\MShop\Catalog\Manager\Decorator\Sitecheck, Aimeos\MShop\Catalog\Manager\Lists\Type\Standard, Aimeos\MShop\Catalog\Manager\Standard, Aimeos\MShop\Common\Manager\Decorator\Base, Aimeos\MShop\Common\Manager\Decorator\Changelog, Aimeos\MShop\Common\Manager\Decorator\Sitecheck, Aimeos\MShop\Common\Manager\Type\Base, Aimeos\MShop\Coupon\Manager\Code\Standard, Aimeos\MShop\Customer\Manager\Base, Aimeos\MShop\Customer\Manager\Group\Standard, Aimeos\MShop\Customer\Manager\Lists\Type\Standard, Aimeos\MShop\Customer\Manager\Standard, Aimeos\MShop\Locale\Manager\Site\Standard, Aimeos\MShop\Media\Manager\Lists\Type\Standard, Aimeos\MShop\Media\Manager\Type\Standard, Aimeos\MShop\Plugin\Manager\Type\Standard, Aimeos\MShop\Price\Manager\Lists\Type\Standard, Aimeos\MShop\Price\Manager\Type\Standard, Aimeos\MShop\Product\Manager\Lists\Type\Standard, Aimeos\MShop\Product\Man...\Property\Type\Standard, Aimeos\MShop\Product\Manager\Standard, Aimeos\MShop\Product\Manager\Type\Standard, Aimeos\MShop\Service\Manager\Lists\Type\Standard, Aimeos\MShop\Service\Manager\Standard, Aimeos\MShop\Service\Manager\Type\Standard, Aimeos\MShop\Stock\Manager\Standard, Aimeos\MShop\Stock\Manager\Type\Standard, Aimeos\MShop\Supplier\Manager\Lists\Type\Standard, Aimeos\MShop\Supplier\Manager\Standard, Aimeos\MShop\Tag\Manager\Type\Standard, Aimeos\MShop\Text\Manager\Lists\Type\Standard, Aimeos\MShop\Text\Manager\Type\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
155
		$this->context->setUserId( null );
156
157
		$params = array( 'id' => $user->getId() );
158
		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $this->view, $params );
159
		$this->view->addHelper( 'param', $helper );
160
161
		$response = $this->object->get( $this->view->request(), $this->view->response() );
162
		$result = json_decode( (string) $response->getBody(), true );
163
164
		$this->assertEquals( 403, $response->getStatusCode() );
165
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
166
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
167
168
		$this->assertEquals( 0, $result['meta']['total'] );
169
		$this->assertArrayHasKey( 'errors', $result );
170
	}
171
172
173
	public function testGetIncluded()
174
	{
175
		$user = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' )->findItem( 'UTC001' );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Manager\Iface as the method findItem() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Manager\Lists\Type\Standard, Aimeos\MShop\Attribute\Manager\Standard, Aimeos\MShop\Attribute\Manager\Type\Standard, Aimeos\MShop\Catalog\Manager\Decorator\Base, Aimeos\MShop\Catalog\Manager\Decorator\Sitecheck, Aimeos\MShop\Catalog\Manager\Lists\Type\Standard, Aimeos\MShop\Catalog\Manager\Standard, Aimeos\MShop\Common\Manager\Decorator\Base, Aimeos\MShop\Common\Manager\Decorator\Changelog, Aimeos\MShop\Common\Manager\Decorator\Sitecheck, Aimeos\MShop\Common\Manager\Type\Base, Aimeos\MShop\Coupon\Manager\Code\Standard, Aimeos\MShop\Customer\Manager\Base, Aimeos\MShop\Customer\Manager\Group\Standard, Aimeos\MShop\Customer\Manager\Lists\Type\Standard, Aimeos\MShop\Customer\Manager\Standard, Aimeos\MShop\Locale\Manager\Site\Standard, Aimeos\MShop\Media\Manager\Lists\Type\Standard, Aimeos\MShop\Media\Manager\Type\Standard, Aimeos\MShop\Plugin\Manager\Type\Standard, Aimeos\MShop\Price\Manager\Lists\Type\Standard, Aimeos\MShop\Price\Manager\Type\Standard, Aimeos\MShop\Product\Manager\Lists\Type\Standard, Aimeos\MShop\Product\Man...\Property\Type\Standard, Aimeos\MShop\Product\Manager\Standard, Aimeos\MShop\Product\Manager\Type\Standard, Aimeos\MShop\Service\Manager\Lists\Type\Standard, Aimeos\MShop\Service\Manager\Standard, Aimeos\MShop\Service\Manager\Type\Standard, Aimeos\MShop\Stock\Manager\Standard, Aimeos\MShop\Stock\Manager\Type\Standard, Aimeos\MShop\Supplier\Manager\Lists\Type\Standard, Aimeos\MShop\Supplier\Manager\Standard, Aimeos\MShop\Tag\Manager\Type\Standard, Aimeos\MShop\Text\Manager\Lists\Type\Standard, Aimeos\MShop\Text\Manager\Type\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
176
		$this->context->setUserId( $user->getId() );
177
178
		$params = array(
179
			'id' => $user->getId(),
180
			'include' => 'customer/address',
181
		);
182
		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $this->view, $params );
183
		$this->view->addHelper( 'param', $helper );
184
185
186
		$response = $this->object->get( $this->view->request(), $this->view->response() );
187
		$result = json_decode( (string) $response->getBody(), true );
188
189
		$this->assertEquals( 200, $response->getStatusCode() );
190
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
191
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
192
193
		$this->assertEquals( 1, $result['meta']['total'] );
194
		$this->assertEquals( 'customer', $result['data']['type'] );
195
		$this->assertArrayHasKey( 'customer/address', $result['data']['attributes'] );
196
		$this->assertEquals( 1, count( $result['data']['attributes']['customer/address'] ) );
197
		$this->assertEquals( 0, count( $result['included'] ) );
198
199
		$this->assertArrayNotHasKey( 'errors', $result );
200
	}
201
202
203
	public function testGetIncludedNone()
204
	{
205
		$user = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' )->findItem( 'UTC001' );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Manager\Iface as the method findItem() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Manager\Lists\Type\Standard, Aimeos\MShop\Attribute\Manager\Standard, Aimeos\MShop\Attribute\Manager\Type\Standard, Aimeos\MShop\Catalog\Manager\Decorator\Base, Aimeos\MShop\Catalog\Manager\Decorator\Sitecheck, Aimeos\MShop\Catalog\Manager\Lists\Type\Standard, Aimeos\MShop\Catalog\Manager\Standard, Aimeos\MShop\Common\Manager\Decorator\Base, Aimeos\MShop\Common\Manager\Decorator\Changelog, Aimeos\MShop\Common\Manager\Decorator\Sitecheck, Aimeos\MShop\Common\Manager\Type\Base, Aimeos\MShop\Coupon\Manager\Code\Standard, Aimeos\MShop\Customer\Manager\Base, Aimeos\MShop\Customer\Manager\Group\Standard, Aimeos\MShop\Customer\Manager\Lists\Type\Standard, Aimeos\MShop\Customer\Manager\Standard, Aimeos\MShop\Locale\Manager\Site\Standard, Aimeos\MShop\Media\Manager\Lists\Type\Standard, Aimeos\MShop\Media\Manager\Type\Standard, Aimeos\MShop\Plugin\Manager\Type\Standard, Aimeos\MShop\Price\Manager\Lists\Type\Standard, Aimeos\MShop\Price\Manager\Type\Standard, Aimeos\MShop\Product\Manager\Lists\Type\Standard, Aimeos\MShop\Product\Man...\Property\Type\Standard, Aimeos\MShop\Product\Manager\Standard, Aimeos\MShop\Product\Manager\Type\Standard, Aimeos\MShop\Service\Manager\Lists\Type\Standard, Aimeos\MShop\Service\Manager\Standard, Aimeos\MShop\Service\Manager\Type\Standard, Aimeos\MShop\Stock\Manager\Standard, Aimeos\MShop\Stock\Manager\Type\Standard, Aimeos\MShop\Supplier\Manager\Lists\Type\Standard, Aimeos\MShop\Supplier\Manager\Standard, Aimeos\MShop\Tag\Manager\Type\Standard, Aimeos\MShop\Text\Manager\Lists\Type\Standard, Aimeos\MShop\Text\Manager\Type\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
206
		$this->context->setUserId( $user->getId() );
207
208
		$params = array(
209
			'id' => $user->getId(),
210
			'include' => '',
211
		);
212
		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $this->view, $params );
213
		$this->view->addHelper( 'param', $helper );
214
215
216
		$response = $this->object->get( $this->view->request(), $this->view->response() );
217
		$result = json_decode( (string) $response->getBody(), true );
218
219
		$this->assertEquals( 200, $response->getStatusCode() );
220
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
221
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
222
223
		$this->assertEquals( 1, $result['meta']['total'] );
224
		$this->assertEquals( 'customer', $result['data']['type'] );
225
		$this->assertArrayNotHasKey( 'relationships', $result['data'] );
226
		$this->assertEquals( 0, count( $result['included'] ) );
227
228
		$this->assertArrayNotHasKey( 'errors', $result );
229
	}
230
231
232
	public function testGetControllerException()
233
	{
234
		$object = $this->getObject( 'getItem', $this->throwException( new \Aimeos\Controller\Frontend\Customer\Exception() ) );
235
236
		$response = $object->get( $this->view->request(), $this->view->response() );
237
		$result = json_decode( (string) $response->getBody(), true );
238
239
240
		$this->assertEquals( 403, $response->getStatusCode() );
241
		$this->assertArrayHasKey( 'errors', $result );
242
	}
243
244
245
	public function testGetMShopException()
246
	{
247
		$object = $this->getObject( 'getItem', $this->throwException( new \Aimeos\MShop\Exception() ) );
248
249
		$response = $object->get( $this->view->request(), $this->view->response() );
250
		$result = json_decode( (string) $response->getBody(), true );
251
252
253
		$this->assertEquals( 404, $response->getStatusCode() );
254
		$this->assertArrayHasKey( 'errors', $result );
255
	}
256
257
258
	public function testGetException()
259
	{
260
		$object = $this->getObject( 'getItem', $this->throwException( new \Exception() ) );
261
262
		$response = $object->get( $this->view->request(), $this->view->response() );
263
		$result = json_decode( (string) $response->getBody(), true );
264
265
266
		$this->assertEquals( 500, $response->getStatusCode() );
267
		$this->assertArrayHasKey( 'errors', $result );
268
	}
269
270
271
	public function testPatch()
272
	{
273
		$manager = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' );
274
		$item = $manager->createItem()->setCode( 'unittest-japi' )->setStatus( 1 );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Item\Iface as the method setCode() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Item\Standard, Aimeos\MShop\Catalog\Item\Standard, Aimeos\MShop\Common\Item\Type\Standard, Aimeos\MShop\Coupon\Item\Code\Standard, Aimeos\MShop\Customer\Item\Base, Aimeos\MShop\Customer\Item\Group\Standard, Aimeos\MShop\Customer\Item\Standard, Aimeos\MShop\Locale\Item\Currency\Standard, Aimeos\MShop\Locale\Item\Language\Standard, Aimeos\MShop\Locale\Item\Site\Standard, Aimeos\MShop\Order\Item\Base\Coupon\Standard, Aimeos\MShop\Order\Item\...duct\Attribute\Standard, Aimeos\MShop\Order\Item\...vice\Attribute\Standard, Aimeos\MShop\Order\Item\Base\Service\Base, Aimeos\MShop\Order\Item\Base\Service\Standard, Aimeos\MShop\Product\Item\Standard, Aimeos\MShop\Service\Item\Standard, Aimeos\MShop\Supplier\Item\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
275
		$manager->saveItem( $item );
276
277
		$this->context->setUserId( $item->getId() );
278
279
		$params = array( 'id' => $item->getId() );
280
		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $this->view, $params );
281
		$this->view->addHelper( 'param', $helper );
282
283
		$body = '{"data": {"attributes": {"customer.status": 0}}}	';
284
		$request = $this->view->request()->withBody( $this->view->response()->createStreamFromString( $body ) );
285
286
		$response = $this->object->patch( $request, $this->view->response() );
287
		$result = json_decode( (string) $response->getBody(), true );
288
289
		$manager->deleteItem( $item->getId() );
290
291
292
		$this->assertEquals( 200, $response->getStatusCode() );
293
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
294
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
295
296
		$this->assertEquals( 1, $result['meta']['total'] );
297
		$this->assertEquals( 'customer', $result['data']['type'] );
298
		$this->assertGreaterThan( 31, count( $result['data']['attributes'] ) );
299
		$this->assertEquals( 'unittest-japi', $result['data']['attributes']['customer.code'] );
300
301
		$this->assertArrayNotHasKey( 'errors', $result );
302
	}
303
304
305
	public function testPatchControllerException()
306
	{
307
		$object = $this->getObject( 'editItem', $this->throwException( new \Aimeos\Controller\Frontend\Customer\Exception() ) );
308
309
		$body = '{"data": {"attributes": []}}';
310
		$request = $this->view->request()->withBody( $this->view->response()->createStreamFromString( $body ) );
311
312
		$response = $object->patch( $request, $this->view->response() );
313
		$result = json_decode( (string) $response->getBody(), true );
314
315
316
		$this->assertEquals( 403, $response->getStatusCode() );
317
		$this->assertArrayHasKey( 'errors', $result );
318
	}
319
320
321
	public function testPatchMShopException()
322
	{
323
		$object = $this->getObject( 'editItem', $this->throwException( new \Aimeos\MShop\Exception() ) );
324
325
		$body = '{"data": {"attributes": []}}';
326
		$request = $this->view->request()->withBody( $this->view->response()->createStreamFromString( $body ) );
327
328
		$response = $object->patch( $request, $this->view->response() );
329
		$result = json_decode( (string) $response->getBody(), true );
330
331
332
		$this->assertEquals( 404, $response->getStatusCode() );
333
		$this->assertArrayHasKey( 'errors', $result );
334
	}
335
336
337
	public function testPatchException()
338
	{
339
		$object = $this->getObject( 'editItem', $this->throwException( new \Exception() ) );
340
341
		$body = '{"data": {"attributes": []}}';
342
		$request = $this->view->request()->withBody( $this->view->response()->createStreamFromString( $body ) );
343
344
		$response = $object->patch( $request, $this->view->response() );
345
		$result = json_decode( (string) $response->getBody(), true );
346
347
348
		$this->assertEquals( 500, $response->getStatusCode() );
349
		$this->assertArrayHasKey( 'errors', $result );
350
	}
351
352
353
	public function testPost()
354
	{
355
		$body = '{"data": {"attributes": {"customer.code": "unittest-japi"}}}';
356
		$request = $this->view->request()->withBody( $this->view->response()->createStreamFromString( $body ) );
357
358
359
		$response = $this->object->post( $request, $this->view->response() );
360
		$result = json_decode( (string) $response->getBody(), true );
361
362
		$this->assertEquals( 201, $response->getStatusCode() );
363
		$this->assertEquals( 1, count( $response->getHeader( 'Allow' ) ) );
364
		$this->assertEquals( 1, count( $response->getHeader( 'Content-Type' ) ) );
365
366
		$this->assertEquals( 1, $result['meta']['total'] );
367
		$this->assertNotNull( $result['data']['id'] );
368
		$this->assertEquals( 'customer', $result['data']['type'] );
369
		$this->assertGreaterThan( 31, count( $result['data']['attributes'] ) );
370
371
		$this->assertArrayNotHasKey( 'errors', $result );
372
373
374
		$manager = \Aimeos\MShop\Factory::createManager( $this->context, 'customer' );
375
		$manager->deleteItem( $result['data']['id'] );
376
	}
377
378
379
	public function testPostControllerException()
380
	{
381
		$object = $this->getObject( 'addItem', $this->throwException( new \Aimeos\Controller\Frontend\Customer\Exception() ) );
382
383
		$body = '{"data": {"attributes": {}}}';
384
		$request = $this->view->request()->withBody( $this->view->response()->createStreamFromString( $body ) );
385
386
		$response = $object->post( $request, $this->view->response() );
387
		$result = json_decode( (string) $response->getBody(), true );
388
389
390
		$this->assertEquals( 403, $response->getStatusCode() );
391
		$this->assertArrayHasKey( 'errors', $result );
392
	}
393
394
395
	public function testPostMShopException()
396
	{
397
		$object = $this->getObject( 'addItem', $this->throwException( new \Aimeos\MShop\Exception() ) );
398
399
		$body = '{"data": {"attributes": {}}}';
400
		$request = $this->view->request()->withBody( $this->view->response()->createStreamFromString( $body ) );
401
402
		$response = $object->post( $request, $this->view->response() );
403
		$result = json_decode( (string) $response->getBody(), true );
404
405
406
		$this->assertEquals( 404, $response->getStatusCode() );
407
		$this->assertArrayHasKey( 'errors', $result );
408
	}
409
410
411
	public function testPostException()
412
	{
413
		$response = $this->object->post( $this->view->request(), $this->view->response() );
414
		$result = json_decode( (string) $response->getBody(), true );
415
416
		$this->assertEquals( 500, $response->getStatusCode() );
417
		$this->assertArrayHasKey( 'errors', $result );
418
	}
419
420
421
	/**
422
	 * Returns a test object with a mocked customer controller
423
	 *
424
	 * @param string $method Customer controller method name to mock
425
	 * @param mixed $result Return value of the mocked method
426
	 */
427
	protected function getObject( $method, $result )
428
	{
429
		$cntl = $this->getMockBuilder( '\Aimeos\Controller\Frontend\Customer\Standard' )
430
			->setConstructorArgs( [$this->context] )
431
			->setMethods( [$method] )
432
			->getMock();
433
434
		$cntl->expects( $this->once() )->method( $method )->will( $result );
435
436
		\Aimeos\Controller\Frontend\Customer\Factory::injectController( '\Aimeos\Controller\Frontend\Customer\Standard', $cntl );
437
438
		$templatePaths = \TestHelperJapi::getTemplatePaths();
439
		$object = new \Aimeos\Client\JsonApi\Customer\Standard( $this->context, $this->view, $templatePaths, 'customer' );
440
441
		return $object;
442
	}
443
}