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/farmfun/reserveren.farmfun.be/tests/Unit/RouteTest.php
<?php

namespace Tests\Unit;

use App\Komma\Kms\Core\TranslationService;
use App\Komma\Kms\Core\TranslationServiceInterface;
use App\Komma\Pages\Kms\PageModelService;
use App\Komma\Pages\Kms\PageTranslationService;
use App\Komma\Pages\Models\Page;
use App\Komma\Pages\Models\PageTranslation;
use App\Komma\Routes\Models\Route;
use App\Komma\Routes\RouteService;
use App\Komma\Sites\Kms\SiteService;
use App\Komma\Sites\Models\Site;
use App\Komma\Sites\SiteServiceInterface;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Str;
use Tests\TestCase;

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

    /** @var RouteService */
    private $routeService;

    /** @var SiteService */
    private $siteService;

    /** @var TranslationServiceInterface */
    private $translationService;

    /**
     * @group RouteService
     * @test
     */
    public function instanceCreationTest()
    {
        $this->assertInstanceOf(RouteService::class, $this->routeService);
    }

    /**
     * Setup the test environment.
     *
     * @return void
     */
    protected function setUp(): void
    {
        parent::setUp();
        $this->routeService = new RouteService();
        $this->translationService = new PageTranslationService();
        $this->siteService = app(SiteServiceInterface::class);
        $this->siteService->setCurrentSiteToDefault();
    }

    /**
     * When you save a page a friendly url should be created.
     *
     * @group RouteService
     * @test
     */
    public function basicRouteCreationTestWithSiteThatHasSingleLanguage()
    {
        $languagesCount = $this->siteService->getCurrentSite()->languages()->count();
        if($languagesCount > 1) {
            $language = $this->siteService->getCurrentSite()->languages()->first();
            $this->siteService->getCurrentSite()->languages()->detach();
            $this->siteService->getCurrentSite()->languages()->attach($language);
        }

        //This test assumes that the current site has one languages (two prevent the language iso 2 being prefixed to each route)
        $this->assertTrue($this->siteService->getCurrentSite()->languages()->count() == 1);

        //Create a page translation with a page
        /** @var PageTranslation $pageTranslation */
        $pageTranslation = factory(PageTranslation::class)->create();
        $language = $pageTranslation->language()->first();

        /** @var Page $page */
        $page = $pageTranslation->translatable()->first();

        //Validate that the page translation did not have a route already
        self::assertEquals(0, $pageTranslation->route()->count());
        self::assertEquals(0, $pageTranslation->redirectRoutes()->count());

        //Give the page to the routeService. I now must create a route and link it to the page
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page);

        //Check that the translation has a route...
        self::assertEquals(1, $pageTranslation->route()->count());

        //...And it does not have a redirect route.
        self::assertEquals(0, $pageTranslation->redirectRoutes()->count());

        //Get the route so we can check its attributes
        $route = $pageTranslation->route()->first();

        $this->assertEquals('pages/'.$page->id, $route->route);
        $this->assertEquals('/'.Str::slug($pageTranslation->name), $route->alias);
        $this->assertEquals($pageTranslation->id, $route->page_translation_id);
        $this->assertEquals($this->siteService->getCurrentSite()->id, $route->site_id);
        $this->assertEquals($language->id, $route->language_id);
    }

    /**
     * When you save a page a friendly url should be created.
     *
     * @group RouteService
     * @test
     */
    public function basicRouteCreationTest()
    {
        //This test assumes that the current site has at least to languages (for the route alias prefix)
        $this->assertTrue($this->siteService->getCurrentSite()->languages()->count() >= 2);

        //Create a page translation with a page and make it a child of the first root page
        /** @var PageTranslation $pageTranslation */
        $pageTranslation = factory(PageTranslation::class)->create();
        $language = $pageTranslation->language()->first();

        /** @var Page $page */
        $page = $pageTranslation->translatable()->first();

        $rootPage = Page::where('lft', '=', '1')->first();
        $page->makeLastChildOf($rootPage);
        $page->save();

        //Validate that the page translation did not have a route already
        self::assertEquals(0, $pageTranslation->route()->count());
        self::assertEquals(0, $pageTranslation->redirectRoutes()->count());

        //Give the page to the routeService. It now must create a route and link it to the page
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page);

        //Check that the translation has a route...
        self::assertEquals(1, $pageTranslation->route()->count());

        //...And it does not have a redirect route.
        self::assertEquals(0, $pageTranslation->redirectRoutes()->count());

        //Get the route so we can check its attributes
        $route = $pageTranslation->route()->first();

        $this->assertEquals('pages/'.$page->id, $route->route);
        $this->assertEquals('/'.$language->iso_2.'/'.Str::slug($pageTranslation->name), $route->alias);
        $this->assertEquals($pageTranslation->id, $route->page_translation_id);
        $this->assertEquals($this->siteService->getCurrentSite()->id, $route->site_id);
        $this->assertEquals($language->id, $route->language_id);


        //Create another page translation with another page
        /** @var PageTranslation $pageTranslationTwo */
        $pageTranslationTwo = factory(PageTranslation::class)->create();
        $language = $pageTranslation->language()->first();

        /** @var Page $pageTwo */
        $pageTwo = $pageTranslationTwo->translatable()->first();

        //Make pageTwo a child of one.
        $pageTwo->makeLastChildOf($page);
        $pageTwo->save();

        //Give the page to the routeService. It now must create a route with the alias of page suffixed with the slug / name of pageTwo
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($pageTwo);
        $routePageTwo = $pageTranslationTwo->route()->first();

        $this->assertNotNull($routePageTwo);
        $routePageOne = $pageTranslation->route()->first();
        $this->assertNotNull($routePageOne);

        $this->assertEquals($routePageOne->alias.'/'.$pageTranslationTwo->slug, $routePageTwo->alias);
    }

    /**
     * When you edit a translations slug / name the route changes too to reflect the new slug / name.
     * This is done by creating a new route with the new changes in it. The old routes is converted in a redirect route
     * so that users using the old route will be redirected using the redirect route, to the new route.
     *
     * @group RouteService
     * @test
     */
    public function basicRedirectRouteCreationTest()
    {
        //This test assumes that the current site has at least to languages (for the route alias prefix)
        $this->assertTrue($this->siteService->getCurrentSite()->languages()->count() >= 2);

        //Create a page translation with a page
        /** @var PageTranslation $pageTranslation */
        $pageTranslation = factory(PageTranslation::class)->create();
        $language = $pageTranslation->language()->first();

        /** @var Page $page */
        $page = $pageTranslation->translatable()->first();

        //Validate that the page translation did not have a route already
        self::assertEquals(0, $pageTranslation->route()->count());
        self::assertEquals(0, $pageTranslation->redirectRoutes()->count());

        //Give the page to the routeService. I now must create a route and link it to the page
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page);

        //Edit the page translation and save it. But keep track of the old slug and name for later
        $newName = $this->faker->name;
        $oldSlug = $pageTranslation->slug;
        $oldName = $pageTranslation->name;
        $pageTranslation->slug = str_slug($newName);
        $pageTranslation->name = $newName;
        $pageTranslation->save();

        //Hand the page to route service again. It must detect that the page translation was changed and will convert the current translation route to a redirect route.
        //And it must create a new route for the translation.
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page);

        //Check that the translation has a new route...
        self::assertEquals(1, $pageTranslation->route()->count());

        //...And it now does have a redirect route
        self::assertEquals(1, $pageTranslation->redirectRoutes()->count());

        //Check that the new route matches the details of the translation
        $newRoute = $pageTranslation->route()->first();
        $this->assertEquals('pages/'.$page->id, $newRoute->route);
        $this->assertEquals('/'.$language->iso_2.'/'.Str::slug($pageTranslation->name), $newRoute->alias);
        $this->assertEquals($pageTranslation->id, $newRoute->page_translation_id);
        $this->assertEquals($this->siteService->getCurrentSite()->id, $newRoute->site_id);
        $this->assertEquals($language->id, $newRoute->language_id);

        //Check the redirect route is the same as the old route's details
        $redirectRoute = $pageTranslation->redirectRoutes()->first();
        $this->assertEquals('pages/'.$page->id, $redirectRoute->route);
        $this->assertEquals('/'.$language->iso_2.'/'.Str::slug($oldName), $redirectRoute->alias);
        $this->assertEquals($pageTranslation->id, $redirectRoute->page_translation_id);
        $this->assertEquals($this->siteService->getCurrentSite()->id, $redirectRoute->site_id);
        $this->assertEquals($language->id, $redirectRoute->language_id);
    }

    /**
     * Some routes potentially have the same alias as another route. Because they MAY be the same when they belong to
     * a different site, we test that they are the same.
     *
     * Example: Consider two pageTranslations which have exactly the same name and slug of "test". And they each belong
     * to a different Page. And both of those Pages have a different Site. Both sites have two languages, Dutch and English.
     * In that case their route aliases must be the same. Because they are on a different site with a different domain.
     * In this example the aliases must be: nl/test and nl/test
     *
     * @group RouteService
     * @test
     */
    public function testRoutesForTranslationsWithDifferentSiteAndPage()
    {
        //Check that we have at least two sites
        $this->assertTrue(Site::count() >= 2, 'This test does need at least 2 sites.');

        //This test assumes that the current site has at least to languages (for the route alias prefix)
        $this->assertTrue($this->siteService->getCurrentSite()->languages()->count() >= 2);

        //Create a page translation with a page
        /** @var PageTranslation $pageTranslation */
        $pageTranslation = factory(PageTranslation::class)->create();
        $language = $pageTranslation->language()->first();

        /** @var Page $page */
        $page = $pageTranslation->translatable()->first();

        //Create another page translation with a page.
        //The translations name and slug are set to the ones of the first. The site_id will be different.
        //This results for pretty much the same route for both translations when handed over to the route service.
        //But their site_id's will differ.
        /** @var PageTranslation $pageTranslation_two */
        $pageTranslation_two = factory(PageTranslation::class)->make();
        $pageTranslation_two->name = $pageTranslation->name;
        $pageTranslation_two->slug = $pageTranslation->slug;
        $pageTranslation_two->save();

        $language_two = $pageTranslation->language()->first();

        //Make sure the second page has a different site
        /** @var Page $page_two */
        $otherSite = Site::where('id', '!=', $page->site_id)->first();
        $page_two = $pageTranslation_two->translatable()->first();
        $page_two->site_id = $otherSite->id;
        $page_two->save();

        //Give the pages to the routeService. I now must create a route and link it to the pages.
        //The second page must have a different route and alias as the first one though the name and slug of the pages is the same
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page);
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page_two);

        //Get the route so we can check its attributes
        $routeOne = $pageTranslation->route()->first();
        $this->assertEquals(1, $pageTranslation->route()->count());

        $this->assertEquals('pages/'.$page->id, $routeOne->route);
        $this->assertEquals('/'.$language->iso_2.'/'.str_slug($pageTranslation->name), $routeOne->alias);
        $this->assertEquals($pageTranslation->id, $routeOne->page_translation_id);
        $this->assertEquals($this->siteService->getCurrentSite()->id, $routeOne->site_id);
        $this->assertEquals($language->id, $routeOne->language_id);

        //Get the route of the second translation so we can check its attributes.
        //It should be a bit different then the other route though the name and slug of the translations match. The site id's must be different
        $this->assertEquals(1, $pageTranslation_two->route()->count());
        $routeTwo = $pageTranslation_two->route()->first();

        $this->assertEquals('pages/'.$page_two->id, $routeTwo->route);
        $this->assertEquals('/'.$language_two->iso_2.'/'.str_slug($pageTranslation_two->name), $routeTwo->alias);
        $this->assertEquals($pageTranslation_two->id, $routeTwo->page_translation_id);
        $this->assertEquals($otherSite->id, $routeTwo->site_id);
        $this->assertEquals($language_two->id, $routeTwo->language_id);

        $this->assertNotEquals($routeOne->site_id, $routeTwo->site_id);
    }

    /**
     * Some routes potentially have the same alias as another route. Because they MUST differ when they belong to
     * the same site, we test that they differ in a way that that the second and up routes are suffixed with a number.
     *
     * Example: Consider two pageTranslations which have exactly the same name and slug of "test". And they each belong
     * to a different Page. And both of those Pages have the same Site. And The site has 2 languages, Dutch and English.
     * In that case their route aliases must be different too. Because they are on the same site.
     * In this example the aliases must be: nl/test and nl/test-1
     *
     * @group RouteService
     * @test
     */
    public function routesForDifferentPageButSameTranslationValues()
    {
        //Check that we have at least one site
        $this->assertTrue(Site::count() >= 1, 'This test does need at least 1 site.');

        //This test assumes that the current site has at least to languages (for the route alias prefix)
        $this->assertTrue($this->siteService->getCurrentSite()->languages()->count() >= 2);

        //Create a page translation with a page
        /** @var PageTranslation $pageTranslation */
        $pageTranslation = factory(PageTranslation::class)->create();
        $language = $pageTranslation->language()->first();

        /** @var Page $page */
        $page = $pageTranslation->translatable()->first();

        //Create another page translation with a page.
        //The translations name and slug are set to the ones of the first. The site_id will be different.
        //This results for pretty much the same route for both translations when handed over to the route service.
        //But it will suffix the latest route with a number.
        /** @var PageTranslation $pageTranslation_two */
        $pageTranslation_two = factory(PageTranslation::class)->make();
        $pageTranslation_two->name = $pageTranslation->name;
        $pageTranslation_two->slug = $pageTranslation->slug;
        $pageTranslation_two->save();

        $language_two = $pageTranslation->language()->first();

        //Make sure the second page has a different site
        /** @var Page $page_two */
        $page_two = $pageTranslation_two->translatable()->first();
        $page_two->site_id = $page->site_id;
        $page_two->save();

        //Give the pages to the routeService. I now must create a route and link it to the pages.
        //The second page must have a different route and alias as the first one though the name and slug of the pages is the same
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page);
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($page_two);

        //Get the route so we can check its attributes
        $routeOne = $pageTranslation->route()->first();
        $this->assertEquals(1, $pageTranslation->route()->count());

        $this->assertEquals('pages/'.$page->id, $routeOne->route);
        $this->assertEquals('/'.$language->iso_2.'/'.Str::slug($pageTranslation->name), $routeOne->alias);
        $this->assertEquals($pageTranslation->id, $routeOne->page_translation_id);
        $this->assertEquals($page->site_id, $routeOne->site_id);
        $this->assertEquals($language->id, $routeOne->language_id);

        //Get the route of the second translation so we can check its attributes.
        //It should be a bit different then the other route though the name and slug of the translations match
        $this->assertEquals(1, $pageTranslation_two->route()->count());
        $routeTwo = $pageTranslation_two->route()->first();

        $this->assertEquals('pages/'.$page_two->id, $routeTwo->route);
        //Notice the -1 suffix. This is because the alias without the suffix already is used in routeOne
        $this->assertEquals('/'.$language_two->iso_2.'/'.Str::slug($pageTranslation_two->name).'-1', $routeTwo->alias);
        $this->assertEquals($pageTranslation_two->id, $routeTwo->page_translation_id);
        $this->assertEquals($page_two->site_id, $routeTwo->site_id);
        $this->assertEquals($language->id, $routeTwo->language_id);
    }

    /**
     *  Consider this:
     *  a route for example has an alias of "services" and has a couple of subroutes with aliases "services/service-one"
     *  and "services/service-two". When the route with "services" as an alias is changed so that it's alias is "what-we-do",
     *  the aliasses of the subroutes must also be updated so that they are "what-we-do/service-one", "what-we-do/service-two"
     *
     *  The modification of subroutes when a parent route alias is edited, is tested in this method.
     *
     * @group RouteService
     * @test
     */
    public function testUpdatedAliasOfSubRoutesWhenParentIsChanged() {
        //Create a pageService. We need this later on
        $pageService = new PageModelService();

        //Create a root service page and two sub service pages
        /** @var PageTranslation $servicePageTranslation */
        $servicePageTranslation = factory(PageTranslation::class)->make();
        $servicePageTranslation->name = 'Services';
        $servicePageTranslation->slug = 'services';
        $servicePageTranslation->save();
        /** @var Page $servicePage */
        $servicePage = $servicePageTranslation->translatable()->first();
        $language = $servicePageTranslation->language()->first();


        /** @var PageTranslation $serviceOnePageTranslation */
        $serviceOnePageTranslation = factory(PageTranslation::class)->create();
        /** @var Page $serviceOnePage */
        $serviceOnePage = $serviceOnePageTranslation->translatable()->first();


        /** @var PageTranslation $serviceTwoPageTranslation */
        $serviceTwoPageTranslation = factory(PageTranslation::class)->create();
        /** @var Page $serviceTwoPage */
        $serviceTwoPage = $serviceTwoPageTranslation->translatable()->first();

        $serviceOnePage->makeLastChildOf($servicePage);
        $serviceTwoPage->makeLastChildOf($servicePage);

        //Generate routes for all the pages
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($servicePage);
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($serviceOnePage);
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($serviceTwoPage);


        //Modify the service page name and slug to "Things we do" and "things-we-do".
        $servicePageTranslation->name = "Things we do";
        $servicePageTranslation->slug = Str::slug($servicePageTranslation->name);
        $servicePageTranslation->save();

        //Update the route for the page. This now also should update the routes for the sub service pages.
        $this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($servicePage);

        //Get the routes
        $servicePageRoute = $servicePageTranslation->route()->first();
        $serviceOnePageRoute = $serviceOnePageTranslation->route()->first();
        $serviceTwoPageRoute = $serviceTwoPageTranslation->route()->first();

        //Assert them
        $this->assertEquals('/'.$language->iso_2.'/'.Str::slug($servicePageTranslation->name), $servicePageRoute->alias);
        $this->assertEquals($servicePageRoute->alias.'/'.Str::slug($serviceOnePageTranslation->name), $serviceOnePageRoute->alias);
        $this->assertEquals($servicePageRoute->alias.'/'.Str::slug($serviceTwoPageTranslation->name), $serviceTwoPageRoute->alias);

        //Delete the parent page and check that the other sub pages are delete to and don't have routes
        $routeTable = (new Route)->getTable();
        $servicePageRouteAlias = $servicePageRoute->alias;
        $servicePageOneRouteAlias = $serviceOnePageRoute->alias;
        $servicePageTwoRouteAlias = $serviceTwoPageRoute->alias;

        $this->routeService->destroyForModel($servicePage);
        $this->translationService->destroyForModel($servicePage);
        $pageService->destroyForModel($servicePage);
        $servicePage->delete();
        $serviceOnePage->delete();
        $serviceTwoPage->delete();

        $this->assertNull($servicePage->fresh());
        $this->assertNull($serviceOnePage->fresh());
        $this->assertNull($serviceTwoPage->fresh());
        $this->assertDatabaseMissing($routeTable, ['alias' => $servicePageRouteAlias]);
        $this->assertDatabaseMissing($routeTable, ['alias' => $servicePageOneRouteAlias]);
        $this->assertDatabaseMissing($routeTable, ['alias' => $servicePageTwoRouteAlias]);
    }

}