HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/shop.komma.nl/tests/Unit/PropertyTest.php
<?php

namespace Tests\Unit;

use App\Attributes\FreeProperties;
use App\Products\Product\ProductTranslation;
use App\Properties\Kms\PropertyService;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Illuminate\Support\Str;
use Komma\KMS\Globalization\Languages\Models\Language;
use App\Products\Product\Product;
use App\Properties\Models\PropertyKey;
use App\Properties\Models\PropertyKeyTranslation;
use App\Properties\Models\Property;
use App\Properties\Models\PropertyValue;
use App\Properties\Models\PropertyValueTranslation;
use PropertyFaker;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class PropertyTest extends TestCase
{
    use DatabaseTransactions; //Automatically rolls back database actions after tests

    /**
     * @group Properties
     * @test
     */
    public function testPropertyValue()
    {
        $value = new PropertyValue();
        $value->save();
        $this->assertDatabaseHas($value->getTable(), ['id' => $value->id]);
    }

    /**
     * @group Properties
     * @test
     */
    public function testPropertyKey()
    {
        $key = new PropertyKey();
        $key->save();
        $this->assertDatabaseHas($key->getTable(), ['id' => $key->id]);
    }

    /**
     * @group Properties
     * @test
     */
    public function testPropertyValueTranslation()
    {
        $value = new PropertyValue();
        $value->save();

        $propertyData = PropertyFaker::property();
        $language = Language::find(104);

        $translation = new PropertyValueTranslation();
        $translation->name = $propertyData[PropertyFaker::VALUE];
        $translation->language()->associate($language);
        $translation->translatable()->associate($value);
        $translation->save();
        $this->assertDatabaseHas($translation->getTable(), ['id' => $translation->id]);
    }

    /**
     * @group Properties
     * @test
     */
    public function testPropertyKeyTranslation()
    {
        $key = new PropertyKey();
        $key->save();

        $propertyData = PropertyFaker::property();
        $language = Language::find(104);

        $translation = new PropertyKeyTranslation();
        $translation->name = $propertyData[PropertyFaker::KEY];
        $translation->language()->associate($language);
        $translation->translatable()->associate($key);
        $translation->save();
        $this->assertDatabaseHas($translation->getTable(), ['id' => $translation->id]);
    }

    /**
     * @group Properties
     * @test
     */
    public function testProperty()
    {
        $propertyData = PropertyFaker::property();
        $language = Language::find(104);

        $value = new PropertyValue();
        $value->save();

        $key = new PropertyKey();
        $key->save();

        $valueTranslation = new PropertyValueTranslation();
        $valueTranslation->name = $propertyData[PropertyFaker::VALUE];
        $valueTranslation->language()->associate($language);
        $valueTranslation->translatable()->associate($value);
        $valueTranslation->save();

        $keyTranslation = new PropertyKeyTranslation();
        $keyTranslation->name = $propertyData[PropertyFaker::KEY];
        $keyTranslation->language()->associate($language);
        $keyTranslation->translatable()->associate($key);
        $keyTranslation->save();

        $property = new Property();
        $property->code_name = Str::random();
        $property->key()->associate($key);
        $property->value()->associate($value);
        $property->save();

        $this->assertDatabaseHas($property->getTable(), ['id' => $property->id]);
    }

    /**
     * @group Properties
     * @test
     */
    public function testPropertyLinkageToPropertizable()
    {
        $propertyData = PropertyFaker::property();
        $language = Language::find(104);

        $value = new PropertyValue();
        $value->save();

        $key = new PropertyKey();
        $key->save();

        $valueTranslation = new PropertyValueTranslation();
        $valueTranslation->name = $propertyData[PropertyFaker::VALUE];
        $valueTranslation->language()->associate($language);
        $valueTranslation->translatable()->associate($value);
        $valueTranslation->save();

        $keyTranslation = new PropertyKeyTranslation();
        $keyTranslation->name = $propertyData[PropertyFaker::KEY];
        $keyTranslation->language()->associate($language);
        $keyTranslation->translatable()->associate($key);
        $keyTranslation->save();

        $property = new Property();
        $property->code_name = Str::random();
        $property->key()->associate($key);
        $property->value()->associate($value);
        $property->save();

        $this->assertDatabaseHas($property->getTable(), ['id' => $property->id]);

        /** @var ProductTranslation $productTranslation */
        $productTranslation = factory(ProductTranslation::class)->create();
        $productTranslation->properties()->save($property);

        $this->assertDatabaseHas('propertizables', [
            'property_id' => $property->id,
            'propertizable_id' => $productTranslation->id,
            'propertizable_type' => get_class($productTranslation),
        ]);
    }

    /**
     * @group Properties
     * @test
     */
    public function testCreationOfNewProperty() {
        //Get the language that we are going to associate the Properties attribute. So that it appears like it is from a language tab
        $language = Language::where('iso_2', '=', 'nl')->first();
        $this->assertNotNull($language);

        //Create a ProductTranslation (propertizable). We are going to attach the properties to it.
        /** @var ProductTranslation $productTranslation */
        $productTranslation = factory(ProductTranslation::class)->create(['language_id' => $language->id]);

        //And of course we need a property service for the saving of the process
        $propertyService = new PropertyService();

        //Fake the creation of property data like it comes from the Properties attribute.
        $propertyValue = 'val_test_1';
        $propertyKey = 'key_test_1';

        $propertyData = [
            ['id' => -1, 'state' => Property::NEW, 'key' => $propertyKey, 'value' => $propertyValue]
        ];

        //Create the properties attribute, and give it the json representation of the property data we've created earlier. In real life the frontend will give us the data as json too.
        $attribute = (new FreeProperties(''))
            ->mapValueFrom(\Komma\KMS\Core\Attributes\Attribute::ValueFromItself, 'properties')
            ->setAssociatedLanguage($language)
            ->setValue(json_encode($propertyData));

        //At first, the ProductTranslation will not have any properties.
        $this->assertCount(0, $productTranslation->properties()->get());

        //Let the property service save the attribute for the product translation
        $propertyService->save($productTranslation, collect([$attribute]));

        //Now the ProductTranslation must have exactly 1 attribute (that came from the $propertyData)
        $this->assertCount(1, $productTranslation->properties()->get());

        //Check that that property has 1 value translation and 1 key translation in the given language and that it matches the data we used.
        //Due to how the database is constructed, it can only have 1 key and 1 value.
        /** @var Property $property */
        $property = $productTranslation->properties()->with('key.translations', 'value.translations')->first();
        $propertyKeyTranslation = $property->key->translations->where('language_id', '=', $language->id)->first();
        $propertyValueTranslation = $property->value->translations->where('language_id', '=', $language->id)->first();
        $this->assertNotNull($propertyKeyTranslation);
        $this->assertNotNull($propertyValueTranslation);

        $this->assertEquals(ucfirst(strtolower($propertyValue)), $propertyValueTranslation->value);
        $this->assertEquals(ucfirst(strtolower($propertyKey)), $propertyKeyTranslation->value);

        //Now, verify the load process. Basically it looks like the reverse of above.
        $attribute->setValue('');
        $this->assertEquals('', $attribute->getValue());
        $propertyService->load($productTranslation, collect([$attribute]));
        $expectedPropertyData = [[
            'id' => Property::latest()->first()->id,
            'state' => Property::PRISTINE,
            'key' => ucfirst(strtolower($propertyKey)),
            'value' => ucfirst(strtolower($propertyValue)),
        ]];
        $this->assertEquals(json_encode($expectedPropertyData), $attribute->getValue());
    }

    /**
     * @group Properties
     * @test
     */
    public function testModificationOfExistingPropertyWithNewKeyAndValue() {
        //****************************************************************************//
        //   Make sure prerequisites are met before the actual testing takes place    //
        //****************************************************************************//

        //Get the language that we are going to associate the Properties attribute. So that it appears like it is from a language tab
        $language = Language::where('iso_2', '=', 'nl')->first();

        //And of course we need a property service for the saving of the process
        $propertyService = new PropertyService();

        //Create a ProductTranslation (propertizable). We are going to attach the properties to it.
        /** @var ProductTranslation $productTranslation */
        $productTranslation = factory(ProductTranslation::class)->create(['language_id' => $language->id]);

        //Fake the creation of property data like it comes from the Properties attribute.
        $propertyValue = 'val_test_1';
        $propertyKey = 'key_test_1';

        $propertyData = [
            ['id' => -1, 'state' => Property::NEW, 'key' => $propertyKey, 'value' => $propertyValue]
        ];

        //Create the properties attribute, and give it the json representation of the property data we've created earlier. In real life the frontend will give us the data as json too.
        $attribute = (new FreeProperties(''))
            ->mapValueFrom(\Komma\KMS\Core\Attributes\Attribute::ValueFromItself, 'properties')
            ->setAssociatedLanguage($language)
            ->setValue(json_encode($propertyData));

        //Let the property service save the attribute for the product translation
        $propertyService->save($productTranslation, collect([$attribute]));

        //****************************************************************************//
        //   Test the modification of an existing property with a new key and value   //
        //   The most important part of this test                                     //
        //****************************************************************************//

        //Get the existing property so we can compare it later on
        $existingProperty = $productTranslation->properties()->with('key.translations', 'value.translations')->first();
        $this->assertInstanceOf(Property::class, $existingProperty);
        $this->assertTrue($existingProperty->exists);

        //Define new values
        $newPropertyKey = 'val_test_2';
        $newPropertyValue = 'key_test_2';

        $newPropertyData = [
            ['id' => $existingProperty->id, 'state' => Property::DIRTY, 'key' => $newPropertyKey, 'value' => $newPropertyValue]
        ];

        //Use the attribute to give it the new values
        $attribute->setValue(json_encode($newPropertyData));

        //At first, the ProductTranslation will have 1 property, namely the previously created one.
        $properties = $productTranslation->properties()->get();
        $this->assertCount(1, $productTranslation->properties()->get());

        //Let the property service save the attribute for the product translation
        $propertyService->save($productTranslation, collect([$attribute]));

        //Then it still should have 1 property!
        $this->assertCount(1, $productTranslation->properties()->with('key.translations', 'value.translations')->get());

        //But its translations...
        $keyTranslation = $productTranslation->properties->first()->key->translations->where('language_id', $language->id)->first();
        $valueTranslation = $productTranslation->properties->first()->value->translations->where('language_id', $language->id)->first();
        $this->assertInstanceOf(PropertyKeyTranslation::class, $keyTranslation);
        $this->assertInstanceOf(PropertyValueTranslation::class, $valueTranslation);

        //...must the new translations from the $newPropertyData array
        $this->assertEquals(ucfirst(strtolower($newPropertyData[0]['key'])), $keyTranslation->value);
        $this->assertEquals(ucfirst(strtolower($newPropertyData[0]['value'])), $valueTranslation->value);

        //And the old property key and value must not exist in the database anymore since they are not used by other properties...
        $this->assertCount(0, $properties[0]->key()->get());
        $this->assertCount(0, $properties[0]->value()->get());

        //...but new ones should exist!
        $this->assertCount(1, $productTranslation->properties->first()->key()->get());
        $this->assertCount(1, $productTranslation->properties->first()->value()->get());
    }

    /**
     * @group Properties
     * @test
     */
    public function testModificationOfExistingPropertyWithExistingKeyAndValue() {
        //****************************************************************************//
        //   Make sure prerequisites are met before the actual testing takes place    //
        //****************************************************************************//

        //Get the language that we are going to associate the Properties attribute. So that it appears like it is from a language tab
        $language = Language::where('iso_2', '=', 'nl')->first();

        //And of course we need a property service for the saving of the process
        $propertyService = new PropertyService();

        //Create a ProductTranslation (propertizable). We are going to attach the properties to it.
        /** @var ProductTranslation $productTranslation */
        $productTranslation = factory(ProductTranslation::class)->create(['language_id' => $language->id]);

        //Fake the creation of property data like it comes from the Properties attribute.
        $propertyValue = 'val_test_1';
        $propertyKey = 'key_test_1';

        $propertyData = [
            ['id' => -1, 'state' => Property::NEW, 'key' => $propertyKey, 'value' => $propertyValue]
        ];

        //Create the properties attribute, and give it the json representation of the property data we've created earlier. In real life the frontend will give us the data as json too.
        $attribute = (new FreeProperties(''))
            ->mapValueFrom(\Komma\KMS\Core\Attributes\Attribute::ValueFromItself, 'properties')
            ->setAssociatedLanguage($language)
            ->setValue(json_encode($propertyData));

        //Let the property service save the attribute for the product translation
        $propertyService->save($productTranslation, collect([$attribute]));

        //Get the existing property so we can compare it later on
        $existingProperty = $productTranslation->properties()->with('key.translations', 'value.translations')->first();

        $this->assertInstanceOf(Property::class, $existingProperty);
        $this->assertTrue($existingProperty->exists);

        //****************************************************************************//
        //   Make a second property in the db, with a new key and value.                //
        //****************************************************************************//

        //Define new values
        $newPropertyKey = 'val_test_2';
        $newPropertyValue = 'key_test_2';

        $secondPropertyData = [
            ['id' => -1, 'state' => Property::NEW, 'key' => $propertyKey, 'value' => $propertyValue]
        ];
        $attribute->setValue(json_encode($secondPropertyData));
        $propertyService->save($productTranslation, collect([$attribute]));

        //****************************************************************************//
        //   Test the modification of an existing property with an existing key and   //
        //   value. These must come from the second property we've created earlier.   //
        //   The most important part of this test.                                    //
        //****************************************************************************//

        $newPropertyData = [
            ['id' => $existingProperty->id, 'state' => Property::DIRTY, 'key' => $newPropertyKey, 'value' => $newPropertyValue]
        ];

        //Use the attribute to give it the new values
        $attribute->setValue(json_encode($newPropertyData));

        //At first, the ProductTranslation will have 2 property, namely the previously created ones.
        $properties = $productTranslation->properties()->latest()->get();
        $this->assertCount(2, $productTranslation->properties()->get());

        //Let the property service save the attribute for the product translation
        $propertyService->save($productTranslation, collect([$attribute]));

        //Then it still should have 2 properties!
        $this->assertCount(2, $productTranslation->properties()->with('key.translations', 'value.translations')->get());

        //But its translations...
        $keyTranslation = $productTranslation->properties->first()->key->translations->where('language_id', $language->id)->first();
        $valueTranslation = $productTranslation->properties->first()->value->translations->where('language_id', $language->id)->first();
        $this->assertInstanceOf(PropertyKeyTranslation::class, $keyTranslation);
        $this->assertInstanceOf(PropertyValueTranslation::class, $valueTranslation);

        //...must the new translations from the $newPropertyData array
        $this->assertEquals(ucfirst(strtolower($newPropertyData[0]['key'])), $keyTranslation->value);
        $this->assertEquals(ucfirst(strtolower($newPropertyData[0]['value'])), $valueTranslation->value);

        //And the old property key and value must exist in the database since they are used by other properties...
        $this->assertCount(1, $properties[0]->key()->get());
        $this->assertCount(1, $properties[0]->value()->get());

        //...but new ones should exist!
        $this->assertCount(1, $productTranslation->properties->first()->key()->get());
        $this->assertCount(1, $productTranslation->properties->first()->value()->get());
    }

    /**
     * @group Properties
     * @test
     */
    public function testDeletionOfProperty() {
        //Get the language that we are going to associate the Properties attribute. So that it appears like it is from a language tab
        $language = Language::where('iso_2', '=', 'nl')->first();

        //Create a ProductTranslation (propertizable). We are going to attach the properties to it.
        /** @var ProductTranslation $productTranslation */
        $productTranslation = factory(ProductTranslation::class)->create(['language_id' => $language->id]);

        //And of course we need a property service for the saving of the process
        $propertyService = new PropertyService();

        //Fake the creation of property data like it comes from the Properties attribute.
        $propertyValue = 'val_test_1';
        $propertyKey = 'key_test_1';

        $propertyData = [
            ['id' => -1, 'state' => Property::NEW, 'key' => $propertyKey, 'value' => $propertyValue]
        ];

        //Create the properties attribute, and give it the json representation of the property data we've created earlier. In real life the frontend will give us the data as json too.
        $attribute = (new FreeProperties())
            ->mapValueFrom(\Komma\KMS\Core\Attributes\Attribute::ValueFromItself, 'properties')
            ->setAssociatedLanguage($language)
            ->setValue(json_encode($propertyData));

        //Let the property service save the attribute for the product translation
        $propertyService->save($productTranslation, collect([$attribute]));

        //Now the ProductTranslation must have exactly 1 attribute (that came from the $propertyData). It also must have 1 key, value and 1 keyTranslation and 1 ValueTranslation
        $properties = $productTranslation->properties()->with('key.translations', 'value.translations')->get();
        $property = $properties[0];
        $this->assertCount(1, $properties);
        $this->assertNotNull(1, $property->key);
        $this->assertCount(1, $property->key->translations);
        $this->assertNotNull(1, $property->value);
        $this->assertCount(1, $property->value->translations);

        //Delete the property like the model service will do so
        $propertyService->destroyForModel($productTranslation);
        $this->assertNull(Property::find($property->id));
        $this->assertNull(PropertyKey::find($property->key->id));
        $this->assertNull(PropertyValue::find($property->value->id));
        
        $property->key->translations->each(function(PropertyKeyTranslation $propertyKeyTranslation) {
            $this->assertNull(PropertyKeyTranslation::find($propertyKeyTranslation->id));    
        });

        $property->value->translations->each(function(PropertyValueTranslation $propertyValueTranslation) {
            $this->assertNull(PropertyValueTranslation::find($propertyValueTranslation->id));
        });
    }
}