ldc-crm/tests/CRM/ApiV2/CustomerTest.php
2025-09-08 14:59:53 +02:00

716 lines
26 KiB
PHP

<?php
namespace Tests\CRM\ApiV2;
use App\Enums\ApiError;
use App\Enums\ApiStatus;
use App\Enums\Country;
use App\Models\Brand;
use App\Models\Customer;
use App\Models\CustomerInfo;
use App\Models\CustomerLogin;
use App\Models\SingleOptin;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Str;
use Tests\CreatesApplication;
class CustomerTest extends ApiBase
{
use CreatesApplication;
use DatabaseTransactions;
/**
* Test Customer relations to its related models.
*
* @return void
*/
public function testCustomerRelations(): void
{
extract($this->getCustomer());
$infos = $customer->infos;
$logins = $customer->logins;
$brands = $customer->brands;
$optInHistory = $customer->optInHistory;
$this->assertInstanceOf(Collection::class, $infos);
$this->assertInstanceOf(Collection::class, $logins);
$this->assertInstanceOf(Collection::class, $brands);
$this->assertInstanceOf(Collection::class, $optInHistory);
$this->assertInstanceOf(CustomerInfo::class, $customer->info($brands->first()));
$this->assertInstanceOf(CustomerLogin::class, $customer->login($brands->first()));
}
/**
* Test customer Authentification ok.
* route: apiV2.customers.authenticate
*
* @return void
*/
public function testApiAuthenticateCustomerShouldWork(): void
{
extract($this->getCustomer(true));
$login->password = 'testPassword';
$login->saveOrFail();
$response = $this->callApiWithToken(
$service,
'post',
route('apiV2.customers.authenticate', []),
[
'email' => $customer->email,
'password' => 'testPassword'
]
);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
$this->assertNotEmpty($json->json('data'));
$this->assertNotEmpty($json->json('data.firstname'));
$this->assertNotEmpty($json->json('data.lastname'));
$this->assertNotEmpty($json->json('data.birth_date'));
$this->assertNotEmpty($json->json('data.address'));
$this->assertNotEmpty($json->json('data.phone'));
$this->assertNotEmpty($json->json('data.country'));
$this->assertNotEmpty($json->json('data.city'));
$this->assertNotEmpty($json->json('data.postcode'));
$this->assertNotEmpty($json->json('data.hashcode'));
}
/**
* Customer authentification should fail if the password is wrong.
* route: apiV2.customers.authenticate
*
* @return void
*/
public function testApiAuthenticateCustomerShouldFailIfWrongPassword(): void
{
extract($this->getCustomer());
$response = $this->callApiWithToken(
$service,
'post',
route('apiV2.customers.authenticate', []),
[
'email' => $customer->email,
'password' => 'azdqdqdazd'
]
);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertNotEmpty($json->json('data'));
$this->assertNotEmpty($json->json('data.message'));
$this->assertNotEmpty($json->json('data.code'));
$this->assertEquals(ApiError::authFailed->value(), $json->json('data.code'));
$this->assertEquals(ApiError::authFailed->label(), $json->json('data.message'));
}
/**
* Customer authentification should fail if the email is wrong.
* route: apiV2.customers.authenticate
*
* @return void
*/
public function testApiAuthenticateCustomerShouldFailIfWrongEmail(): void
{
extract($this->getCustomer());
$response = $this->callApiWithToken(
$service,
'post',
route('apiV2.customers.authenticate', []),
[
'email' => 'wrongemail@hotmail.fr',
'password' => 'azdqdqdazd'
]
);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertNotEmpty($json->json('data'));
$this->assertNotEmpty($json->json('data.message'));
$this->assertNotEmpty($json->json('data.code'));
$this->assertEquals(ApiError::authFailed->value(), $json->json('data.code'));
$this->assertEquals(ApiError::authFailed->label(), $json->json('data.message'));
}
/**
* Customer authentification should fail if the customer is not activated.
* route: apiV2.customers.authenticate
*
* @return void
*/
public function testApiAuthenticateCustomerShouldFailIfCustomerNotActivated(): void
{
$login = CustomerLogin::query()->where('status', false)->firstOrFail();
/** @var \App\Models\Brand $brand */
$brand = $login->brand;
/** @var \App\Models\Customer $customer */
$customer = $login->customer;
/** @var \App\Models\Service $service */
$service = $brand->services()->firstOrFail();
$response = $this->callApiWithToken(
$service,
'post',
route('apiV2.customers.authenticate', []),
[
'email' => $customer->email,
'password' => 'testPassword'
]
);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertNotEmpty($json->json('data'));
$this->assertNotEmpty($json->json('data.message'));
$this->assertNotEmpty($json->json('data.code'));
$this->assertEquals(ApiError::customerNotActivated->value(), $json->json('data.code'));
$this->assertEquals(ApiError::customerNotActivated->label(), $json->json('data.message'));
}
/**
* Customer store ok.
* route: apiV2.customers.store
*
* @return void
*/
public function testApiStoreCustomerOk(): void
{
extract($this->getCustomer());
// * Single optin should be deleted when creating new customer.
$singleOptin = new SingleOptin();
$singleOptin->email = 'newcustomer@hotmail.fr';
$singleOptin->brand()->associate($brand);
$singleOptin->saveOrFail();
$data = [
'firstname' => 'Jean',
'lastname' => 'Bond',
'birth_date' => \date('1992-03-08'),
'address' => '10 rue des toto',
'email' => 'newcustomer@hotmail.fr',
'password' => '123.Mot.De.P4sse',
'phone' => '+33654821562',
'country' => Country::france->value(),
'city' => 'Connerré',
'postcode' => '72160',
'opt_in' => true
];
$response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
$this->assertNotEmpty($json->json('data.firstname'));
$this->assertNotEmpty($json->json('data.lastname'));
$this->assertNotEmpty($json->json('data.birth_date'));
$this->assertNotEmpty($json->json('data.address'));
$this->assertNotEmpty($json->json('data.phone'));
$this->assertNotEmpty($json->json('data.country'));
$this->assertNotEmpty($json->json('data.city'));
$this->assertNotEmpty($json->json('data.postcode'));
$this->assertNotEmpty($json->json('data.hashcode'));
$this->assertEquals($data['firstname'], $json->json('data.firstname'));
$this->assertEquals($data['lastname'], $json->json('data.lastname'));
$this->assertEquals($data['birth_date'], $json->json('data.birth_date'));
$this->assertEquals($data['address'], $json->json('data.address'));
$this->assertEquals($data['phone'], $json->json('data.phone'));
$this->assertEquals($data['country'], $json->json('data.country'));
$this->assertEquals($data['city'], $json->json('data.city'));
$this->assertEquals($data['postcode'], $json->json('data.postcode'));
}
/**
* Customer store should fail if customer already exists.
* route: apiV2.customers.store
*
* @return void
*/
public function testApiStoreCustomerShouldFailIfCustomerAlreadyExists(): void
{
extract($this->getCustomer());
$data = [
'firstname' => 'Jean',
'lastname' => 'Bond',
'birth_date' => \date('1992-03-08'),
'address' => '10 rue des toto',
'email' => 'newcustomer@hotmail.fr',
'password' => '123.Mot.De.P4sse',
'phone' => '+33654821562',
'country' => Country::france->value(),
'city' => 'Connerré',
'postcode' => '72160',
'opt_in' => true
];
$this->createNewCustomer($data, $brand);
$response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $data);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(ApiError::customerExists->value(), $json->json('data.code'));
$this->assertEquals(ApiError::customerExists->label(), $json->json('data.message'));
}
/**
* Customer store validationFailures.
* route: apiV2.customers.store
*
* @return void
*/
public function testApiStoreCustomerValidationFailures(): void
{
extract($this->getCustomer());
$data = [
'firstname' => 'Jean',
'lastname' => 'Bond',
'birth_date' => \date('1992-03-08'),
'address' => '10 rue des toto',
'email' => 'newcustomer.failures@hotmail.fr',
'password' => '123.Mot.De.P4sse',
'phone' => '+33654821562',
'country' => Country::france->value(),
'city' => 'Connerré',
'postcode' => '72160',
'opt_in' => true
];
$sendData = $data;
$sendData['password'] = 'password';
$response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(
ApiError::validationFailed->value(),
$json->json('data.code'),
'Password strengh should be checked'
);
$sendData = $data;
$sendData['postcode'] = '12';
$response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(
ApiError::validationFailed->value(),
$json->json('data.code'),
'Post code should be checked'
);
$sendData = $data;
$sendData['country'] = '12';
$response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(
ApiError::validationFailed->value(),
$json->json('data.code'),
'Country code should be checked'
);
$sendData = $data;
$sendData['phone'] = '+49-89-636-48018';
$response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(
ApiError::validationFailed->value(),
$json->json('data.code'),
'Country code should be checked'
);
$sendData = $data;
$sendData['email'] = 'toto@nonexistingfqdndomain.lol';
$response = $this->callApiWithToken($service, 'post', route('apiV2.customers.store'), $sendData);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(
ApiError::validationFailed->value(),
$json->json('data.code'),
'Email code should be checked'
);
}
/**
* Test activate customer ok.
* route: apiV2.customers.activate
*
* @return void
*/
public function testActivateCustomerOk(): void
{
extract($this->getCustomer(false));
$login->status = false;
$login->saveOrFail();
$response = $this->callApiWithToken($service, 'post', route(
'apiV2.customers.activate',
['serviceCustomer' => $info->hashcode]
), []);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
}
/**
* Test activate customer already activated.
* route: apiV2.customers.activate
*
* @return void
*/
public function testActivateCustomerAlreadyActivated(): void
{
extract($this->getCustomer(true));
$login->status = true;
$login->saveOrFail();
$response = $this->callApiWithToken($service, 'post', route(
'apiV2.customers.activate',
['serviceCustomer' => $info->hashcode]
), []);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(ApiError::customerAlreadyActive->value(), $json->json('data.code'));
$this->assertEquals(ApiError::customerAlreadyActive->label(), $json->json('data.message'));
}
/**
* Test customer update ok.
* route: apiV2.customers.update
*
* @return void
*/
public function testApiUpdateCustomerOk(): void
{
extract($this->getCustomer());
$data = [
'firstname' => 'Jean2',
'lastname' => 'Bond2',
'birth_date' => \date('1992-03-09'),
'address' => '10 rue des toto',
'email' => 'newcustomertoupdate@hotmail.fr',
'password' => '123.Mot.De.P4sseNew',
'phone' => '+33654821563',
'country' => Country::luxembourg->value(),
'city' => 'Connerré2',
'postcode' => '72161',
'opt_in' => 0
];
$newdata = [
'firstname' => 'Jean3',
'lastname' => 'Bond3',
'birth_date' => \date('1992-03-07'),
'address' => '10 rue des toto3',
'email' => 'newcustomertoupdate3@hotmail.fr',
'password' => '123.Mot.De.P4sseNew3',
'phone' => '+33654821564',
'country' => Country::france->value(),
'city' => 'Connerré3',
'postcode' => '72163',
'opt_in' => 1
];
$this->createNewCustomer($data, $brand);
foreach ($data as $rowName => $row) {
$response = $this->callApiWithToken($service, 'patch', route(
'apiV2.customers.update',
['serviceCustomer' => $info->hashcode]
), [$rowName => $newdata[$rowName]]);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
if ($rowName != 'password') {
$this->assertNotNull($json->json('data.' . $rowName), "Failed asserting $rowName is not null");
$this->assertEquals(
$newdata[$rowName],
$json->json('data.' . $rowName),
"Failed asserting $rowName is equal to $newdata[$rowName]"
);
} else {
$this->assertNull($json->json('data.' . $rowName), "Failed asserting $rowName is null");
$this->assertEquals(
null,
$json->json('data.' . $rowName),
"Failed asserting $rowName is equal to null"
);
}
} //end foreach
// Address2 does not exists in crm.
$response = $this->callApiWithToken($service, 'patch', route(
'apiV2.customers.update',
['serviceCustomer' => $info->hashcode]
), ['address2' => 'anything']);
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(ApiError::validationFailed->value(), $json->json('data.code'));
}
/**
* Test customer update should fail if no changes are provided.
* route: apiV2.customers.update
*
* @return void
*/
public function testApiUpdateCustomerShouldFailWhenNothingChanges(): void
{
extract($this->getCustomer());
$data = [
'firstname' => 'Jean2',
'lastname' => 'Bond2',
'birth_date' => \date('1992-03-09'),
'address' => '10 rue des toto',
'email' => 'anewcustomertoupdate@hotmail.fr',
'password' => '123.Mot.De.P4sseNew',
'phone' => '+33654821563',
'country' => Country::luxembourg->value(),
'city' => 'Connerré2',
'postcode' => '72161',
'opt_in' => 0
];
$this->createNewCustomer($data, $brand);
$response = $this->callApiWithToken($service, 'patch', route(
'apiV2.customers.update',
['serviceCustomer' => $info->hashcode]
));
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals($json->json('data.code'), ApiError::validationFailed->value());
}
/**
* Test customer show ok.
* route: apiV2.customers.show
*
* @return void
*/
public function testApiGetCustomerOk(): void
{
extract($this->getCustomer());
$response = $this->callApiWithToken($service, 'get', route(
'apiV2.customers.show',
['serviceCustomer' => $info->hashcode]
));
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
$this->assertNotEmpty($json->json('data.firstname'));
$this->assertNotEmpty($json->json('data.lastname'));
$this->assertNotEmpty($json->json('data.birth_date'));
$this->assertNotEmpty($json->json('data.address'));
$this->assertNotEmpty($json->json('data.phone'));
$this->assertNotEmpty($json->json('data.country'));
$this->assertNotEmpty($json->json('data.city'));
$this->assertNotEmpty($json->json('data.postcode'));
$this->assertNotEmpty($json->json('data.hashcode'));
}
/**
* Test customer show using email ok.
* route: apiV2.customers.show
*
* @return void
*/
public function testApiGetCustomerByEmailOk(): void
{
extract($this->getCustomer());
$response = $this->callApiWithToken($service, 'get', route(
'apiV2.customers.show',
['serviceCustomer' => $customer->email]
));
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
$this->assertNotEmpty($json->json('data.firstname'));
$this->assertNotEmpty($json->json('data.lastname'));
$this->assertNotEmpty($json->json('data.birth_date'));
$this->assertNotEmpty($json->json('data.address'));
$this->assertNotEmpty($json->json('data.phone'));
$this->assertNotEmpty($json->json('data.country'));
$this->assertNotEmpty($json->json('data.city'));
$this->assertNotEmpty($json->json('data.postcode'));
$this->assertNotEmpty($json->json('data.hashcode'));
}
/**
* Test customer show not found.
* route: apiV2.customers.show
*
* @return void
*/
public function testApiGetCustomerNotFound(): void
{
extract($this->getCustomer());
$csClassName = \array_slice(explode('\\', get_class($customer)), -1)[0];
$uuid = Str::uuid();
$response = $this->callApiWithToken($service, 'get', route(
'apiV2.customers.show',
['serviceCustomer' => $uuid]
));
$json = $response->decodeResponseJson();
/** @var \App\Models\Customer */
$customer = Customer::whereHas('infos', function ($q) use ($uuid) {
$q->where('hashcode', $uuid);
})->first();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(ApiError::modelNotFound->value(), $json->json('data.code'));
$this->assertEquals(
sprintf(ApiError::modelNotFound->label(), $csClassName),
$json->json('data.message')
);
}
/**
* Test delete customer.
* route: apiV2.customer.destroy
*
* @return void
*/
public function testApiDestroyCustomer(): void
{
extract($this->getCustomer());
$csClassName = \array_slice(explode('\\', get_class($customer)), -1)[0];
$service = $brand->services()->firstOrFail();
$response = $this->callApiWithToken($service, 'delete', route(
'apiV2.customers.destroy',
['serviceCustomer' => $info->hashcode]
));
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
$this->assertEmpty($json->json('data'));
$response = $this->callApiWithToken($service, 'get', route(
'apiV2.customers.show',
['serviceCustomer' => $info->hashcode]
));
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(
sprintf(ApiError::modelNotFound->label(), $csClassName),
$json->json('data.message'),
'Customer should have been deleted.'
);
}
/**
* Test delete customer all of data.
* route: apiV2.customer.destroy
*
* @return void
*/
public function testApiCompletelyDestroyCustomer(): void
{
$customer = Customer::whereHas('logins', null, '>', 1)->firstOrFail();
/** @var \Illuminate\Database\Eloquent\Collection $logins */
$logins = $customer->logins;
/** @var \App\Models\CustomerLogin $keepLogin */
$keepLogin = $logins->pull(0);
$logins->map(function (CustomerLogin $login) use ($customer) {
CustomerInfo::whereHas('customer', function ($q) use ($customer) {
$q->whereId($customer->id);
})->whereHas('brand', function ($q) use ($login) {
$q->whereId($login->brand->id);
})->delete();
$login->delete();
});
$this->assertTrue(
(bool) $customer->logins()->count(),
'This test assumes customer belongs to multiple brand.'
);
$csClassName = \array_slice(explode('\\', get_class($customer)), -1)[0];
/** @var \App\Models\CustomerInfo $info */
$info = $customer->info($keepLogin->brand, Customer::FIRST_OR_FAIL);
/** @var \App\Models\Service $service */
$service = $keepLogin->brand->services()->firstOrFail();
$response = $this->callApiWithToken($service, 'delete', route(
'apiV2.customers.destroy',
['serviceCustomer' => $info->hashcode]
));
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::OK);
$this->assertEmpty($json->json('data'));
$response = $this->callApiWithToken($service, 'get', route(
'apiV2.customers.show',
['serviceCustomer' => $info->hashcode]
));
$json = $response->decodeResponseJson();
$this->assertApiResponseStatusIs($json, ApiStatus::KO);
$this->assertEquals(
sprintf(ApiError::modelNotFound->label(), $csClassName),
$json->json('data.message'),
'Customer should have been deleted.'
);
}
/**
* Make sute a customer exists and contains data.
*
* @param array $data
* @param \App\Models\Brand $brand
* @return void
*/
private function createNewCustomer(array $data, Brand $brand): void
{
$customer = Customer::whereEmail($data['email'])
->whereHas('brands', function ($q) use ($brand) {
$q->where('brands.id', '=', $brand->id);
})->firstOrNew();
$customer->fill($data);
$saved = $customer->save();
// * Delete Single optin if exists.
/** @var \App\Models\SingleOptin|null $singleOptin */
$singleOptin = SingleOptin::query()->where('email', $data['email'])
->whereHas('brand', function ($q) use ($brand) {
$q->where('brands.id', '=', $brand->id);
})->first();
if ($singleOptin) {
$saved = $singleOptin->delete() && $saved;
}
/** @var \App\Models\CustomerInfo */
$customerInfo = $customer->info($brand, Customer::FIRST_OR_NEW);
$customerInfo->fill($data);
$customerInfo->brand()->associate($brand);
$customerInfo->customer()->associate($customer);
$saved = $customerInfo->save() && $saved;
/** @var \App\Models\CustomerLogin */
$customerLogin = $customer->login($brand, Customer::FIRST_OR_NEW);
$customerLogin->fill($data);
$customerLogin->brand()->associate($brand);
$customerLogin->customer()->associate($customer);
$saved = $customerLogin->save() && $saved;
$this->assertTrue($saved, 'A new customer should have been created.');
}
}