<?php

/**
 * This file is part of the Propel package.
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT License
 */

require_once dirname(__FILE__) . '/../../../../tools/helpers/bookstore/BookstoreTestBase.php';

/**
 * Tests for SluggableBehavior class
 *
 * @author     François Zaninotto
 * @version    $Revision$
 * @package    generator.behavior.sluggable
 */
class SluggableBehaviorTest extends BookstoreTestBase
{
    public function testParameters()
    {
        $table13 = Table13Peer::getTableMap();
        $this->assertEquals(count($table13->getColumns()), 3, 'Sluggable adds one columns by default');
        $this->assertTrue(method_exists('Table13', 'getSlug'), 'Sluggable adds a slug column by default');
        $table14 = Table14Peer::getTableMap();
        $this->assertEquals(count($table14->getColumns()), 3, 'Sluggable does not add a column when it already exists');
        $this->assertTrue(method_exists('Table14', 'getUrl'), 'Sluggable allows customization of slug_column name');
        $this->assertTrue(method_exists('Table14', 'getSlug'), 'Sluggable adds a standard getter for the slug column');
    }

    public function testObjectGetter()
    {
        $this->assertTrue(method_exists('Table13', 'getSlug'), 'Sluggable adds a getter for the slug column');
        $t = new Table13();
        $t->setSlug('foo');
        $this->assertEquals('foo', $t->getSlug(), 'getSlug() returns the object slug');
        $this->assertTrue(method_exists('Table14', 'getSlug'), 'Sluggable adds a getter for the slug column, even if the column does not have the default name');
        $t = new Table14();
        $t->setUrl('foo');
        $this->assertEquals('foo', $t->getSlug(), 'getSlug() returns the object slug');
    }

    public function testObjectSetter()
    {
        $this->assertTrue(method_exists('Table13', 'setSlug'), 'Sluggable adds a setter for the slug column');
        $t = new Table13();
        $t->setSlug('foo');
        $this->assertEquals('foo', $t->getSlug(), 'setSlug() sets the object slug');
        $this->assertTrue(method_exists('Table14', 'setSlug'), 'Sluggable adds a setter for the slug column, even if the column does not have the default name');
        $t = new Table14();
        $t->setSlug('foo');
        $this->assertEquals('foo', $t->getUrl(), 'setSlug() sets the object slug');
    }

    public function testObjectCreateRawSlug()
    {
        $t = new TestableTable13();
        $this->assertEquals('n-a', $t->createRawSlug(), 'createRawSlug() returns an empty string for an empty object with no pattern');
        $t->setTitle('Hello, World');
        $this->assertEquals('hello-world', $t->createRawSlug(), 'createRawSlug() returns the cleaned up object string representation by default');

        $t = new TestableTable14();
        $this->assertEquals('/foo/n-a/bar', $t->createRawSlug(), 'createRawSlug() returns a slug for an empty object with a pattern');
        $t->setTitle('Hello, World');
        $this->assertEquals('/foo/hello-world/bar', $t->createRawSlug(), 'createRawSlug() returns a slug based on a pattern');
    }

    public static function cleanupSlugProvider()
    {
        return array(
            array('', 'n-a'),
            array('foo', 'foo'),
            array('foo bar', 'foo-bar'),
            array('foo  bar', 'foo-bar'),
            array('FoO', 'foo'),
            array('fôo', 'foo'),
            array(' foo ', 'foo'),
            array('f/o:o', 'f-o-o'),
            array('foo1', 'foo1'),
        );
    }

    /**
     * @dataProvider cleanupSlugProvider
     */
    public function testObjectCleanupSlugPart($in, $out)
    {
        $t = new TestableTable13();
        $this->assertEquals($out, $t->cleanupSlugPart($in), 'cleanupSlugPart() cleans up the slug part');
    }

    public static function limitSlugSizeProvider()
    {
        return array(
            array('123', '123'),
            array(str_repeat('*', 80), str_repeat('*', 80)),
            array(str_repeat('*', 97), str_repeat('*', 97)),
            array(str_repeat('*', 98), str_repeat('*', 97)),
            array(str_repeat('*', 99), str_repeat('*', 97)),
            array(str_repeat('*', 100), str_repeat('*', 97)),
            array(str_repeat('*', 150), str_repeat('*', 97)),
        );
    }

    /**
     * @dataProvider limitSlugSizeProvider
     */
    public function testObjectLimitSlugSize($in, $out)
    {
        $t = new TestableTable14();
        $this->assertEquals($out, $t->limitSlugSize($in), 'limitSlugsize() limits the slug size');
    }

    public function testObjectMakeSlugUnique()
    {
        Table13Query::create()->deleteAll();
        $t = new TestableTable13();
        $this->assertEquals('', $t->makeSlugUnique(''), 'makeSlugUnique() returns the input slug when the input is empty');
        $this->assertEquals('foo', $t->makeSlugUnique('foo'), 'makeSlugUnique() returns the input slug when the table is empty');
        $t->setSlug('foo');
        $t->save();
        $t = new TestableTable13();
        $this->assertEquals('bar', $t->makeSlugUnique('bar'), 'makeSlugUnique() returns the input slug when the table does not contain a similar slug');
        $t->save();
        $t = new TestableTable13();
        $this->assertEquals('foo-1', $t->makeSlugUnique('foo'), 'makeSlugUnique() returns an incremented input when it already exists');
        $t->setSlug('foo-1');
        $t->save();
        $t = new TestableTable13();
        $this->assertEquals('foo-2', $t->makeSlugUnique('foo'), 'makeSlugUnique() returns an incremented input when it already exists');

        TableWithScopeQuery::create()->deleteAll();
        $t = new TestTableWithScope();
        $this->assertEquals('foo', $t->makeSlugUnique('foo'), 'makeSlugUnique() returns the input slug when the table is empty');
        $t->setSlug('foo');
        $t->setScope(1);
        $t->save();
        $t = new TestTableWithScope();
        $t->setScope(1);
        $this->assertEquals('foo-1', $t->makeSlugUnique('foo'), 'makeSlugUnique() returns an incremented input when it already exists');
        $t->setSlug('foo-1');
        $t->save();
        $t = new TestTableWithScope();
        $t->setScope(1);
        $this->assertEquals('foo-2', $t->makeSlugUnique('foo'), 'makeSlugUnique() returns an incremented input when it already exists');
    }

    public function testObjectCreateSlug()
    {
        Table13Query::create()->deleteAll();
        $t = new TestableTable13();
        $this->assertEquals('n-a', $t->createSlug(), 'createSlug() returns n-a for an empty object');
        $t->setTitle('Hello, World!');
        $this->assertEquals('hello-world', $t->createSlug(), 'createSlug() returns a cleaned up slug');
        $t->setSlug('hello-world');
        $t->save();
        $t = new TestableTable13();
        $t->setTitle('Hello; wOrld');
        $this->assertEquals('hello-world-1', $t->createSlug(), 'createSlug() returns a unique slug');

        Table14Query::create()->deleteAll();
        $t = new TestableTable14();
        $this->assertEquals('/foo/n-a/bar', $t->createSlug(), 'createSlug() returns a slug for an empty object with a pattern');
        $t->setTitle('Hello, World!');
        $this->assertEquals('/foo/hello-world/bar', $t->createSlug(), 'createSlug() returns a cleaned up slug');
        $t->setSlug('/foo/hello-world/bar');
        $t->save();
        $t = new TestableTable14();
        $t->setTitle('Hello; wOrld:');
        $this->assertEquals('/foo/hello-world/bar/1', $t->createSlug(), 'createSlug() returns a unique slug');
    }

    public function testObjectPreSave()
    {
        Table14Query::create()->deleteAll();
        $t = new Table14();
        $t->save();
        $this->assertEquals('/foo/n-a/bar', $t->getSlug(), 'preSave() sets a default slug for empty objects');
        $t = new Table14();
        $t->setTitle('Hello, World');
        $t->save();
        $this->assertEquals('/foo/hello-world/bar', $t->getSlug(), 'preSave() sets a cleaned up slug for objects');
        $t = new Table14();
        $t->setTitle('Hello, World');
        $t->save();
        $this->assertEquals('/foo/hello-world/bar/1', $t->getSlug(), 'preSave() sets a unique slug for objects');
        $t = new Table14();
        $t->setTitle('Hello, World');
        $t->setSlug('/foo/custom/bar');
        $t->save();
        $this->assertEquals('/foo/custom/bar', $t->getSlug(), 'preSave() uses the given slug if it exists');
        $t = new Table14();
        $t->setTitle('Hello, World');
        $t->setSlug('/foo/custom/bar');
        $t->save();
        $this->assertEquals('/foo/custom/bar/1', $t->getSlug(), 'preSave() uses the given slug if it exists and makes it unique');
    }

    public function testObjectSlugLifecycle()
    {
        Table13Query::create()->deleteAll();
        $t = new Table13();
        $t->setTitle('Hello, World');
        $t->save();
        $this->assertEquals('hello-world', $t->getSlug(), 'preSave() creates a slug for new objects');
        $t->setSlug('hello-bar');
        $t->save();
        $this->assertEquals('hello-bar', $t->getSlug(), 'setSlug() allows to override default slug');
        $t->setSlug('');
        $t->save();
        $this->assertEquals('hello-world', $t->getSlug(), 'setSlug(null) relaunches the slug generation');

        Table14Query::create()->deleteAll();
        $t = new Table14();
        $t->setTitle('Hello, World2');
        $t->setSlug('hello-bar2');
        $t->save();
        $this->assertEquals('hello-bar2', $t->getSlug(), 'setSlug() allows to override default slug, even before save');
        $t->setSlug('');
        $t->save();
        $this->assertEquals('/foo/hello-world2/bar', $t->getSlug(), 'setSlug(null) relaunches the slug generation');
    }

    public function testObjectSlugAutoUpdate()
    {
        Table13Query::create()->deleteAll();
        $t = new Table13();
        $t->setTitle('Hello, World');
        $t->save();
        $this->assertEquals('hello-world', $t->getSlug(), 'preSave() creates a slug for new objects');
        $t->setTitle('Hello, My World');
        $t->save();
        $this->assertEquals('hello-my-world', $t->getSlug(), 'preSave() autoupdates slug on object change');
        $t->setTitle('Hello, My Whole New World');
        $t->setSlug('hello-bar');
        $t->save();
        $this->assertEquals('hello-bar', $t->getSlug(), 'preSave() does not autoupdate slug when it was set by the user');
    }

    public function testObjectSlugAutoUpdatePermanent()
    {
        Table14Query::create()->deleteAll();
        $t = new Table14();
        $t->setTitle('Hello, World');
        $t->save();
        $this->assertEquals('/foo/hello-world/bar', $t->getSlug(), 'preSave() creates a slug for new objects');
        $t->setTitle('Hello, My World');
        $t->save();
        $this->assertEquals('/foo/hello-world/bar', $t->getSlug(), 'preSave() does not autoupdate slug on object change for permanent slugs');
        $t->setSlug('hello-bar');
        $t->save();
        $this->assertEquals('hello-bar', $t->getSlug(), 'setSlug() still works for permanent slugs');
    }

    public function testQueryFindOneBySlug()
    {
        $this->assertFalse(method_exists('Table13Query', 'findOneBySlug'), 'The generated query does not provide a findOneBySlug() method if the slug column is "slug".');
        $this->assertTrue(method_exists('Table14Query', 'findOneBySlug'), 'The generated query provides a findOneBySlug() method if the slug column is not "slug".');

        Table14Query::create()->deleteAll();
        $t1 = new Table14();
        $t1->setTitle('Hello, World');
        $t1->save();
        $t2 = new Table14();
        $t2->setTitle('Hello, Cruel World');
        $t2->save();
        $t = Table14Query::create()->findOneBySlug('/foo/hello-world/bar');
        $this->assertEquals($t1, $t, 'findOneBySlug() returns a single object matching the slug');
    }

    public function testUniqueViolationWithoutScope()
    {
        TableWithScopeQuery::create()->deleteAll();
        $t = new TableWithScope();
        $t->setTitle('Hello, World');
        $t->save();
        $this->assertEquals('hello-world', $t->getSlug());

        try {
            $t = new TableWithScope();
            $t->setTitle('Hello, World');
            $t->save();

            $this->fail('Exception expected');
        } catch (Exception $e) {
           $this->assertTrue(true, 'Exception successfully thrown');
       }
    }

    public function testNoUniqueViolationWithScope()
    {
        TableWithScopeQuery::create()->deleteAll();
        $t = new TableWithScope();
        $t->setTitle('Hello, World');
        $t->save();
        $this->assertEquals('hello-world', $t->getSlug());

        try {
            $t = new TableWithScope();
            $t->setTitle('Hello, World');
            $t->setScope(1);
            $t->save();

            $this->assertEquals('hello-world', $t->getSlug());
        } catch (Exception $e) {
            $this->fail($e->getMessage());
        }
    }

    public function testPatternNonPermanent()
    {
        Table14PatternQuery::create()->deleteAll();

        $t = new Table14Pattern();
        $t->setTitle('Hello, World');
        $t->setTags('test,tag,php');
        $t->save();
        $this->assertEquals('/foo/hello-world/bar/test-tag-php', $t->getUrl());

        $t->setTags('php,propel');
        $t->save();
        $this->assertEquals('/foo/hello-world/bar/php-propel', $t->getUrl());

        $t->setTitle('Title2');
        $t->save();
        $this->assertEquals('/foo/title2/bar/php-propel', $t->getUrl());
    }

    public function testNumberOfQueriesForMakeUniqSlug()
    {
        Table13Query::create()->deleteAll();
        $con = Propel::getConnection(Table13Peer::DATABASE_NAME);

        for ($i=0; $i < 5; $i++) {
            $nbQuery = $con->getQueryCount();

            $t = new Table13();
            $t->setTitle('Hello, World');
            $t->save($con);

            $this->assertLessThanOrEqual(4, $con->getQueryCount() - $nbQuery, 'no more than 4 query to get a slug when it already exist');
        }
    }

    public function testSlugRegexp()
    {
        Table13Query::create()->deleteAll();
        $con = Propel::getConnection(Table13Peer::DATABASE_NAME);

        for ($i=0; $i < 3; $i++) {
            $t = new Table13();
            $t->setTitle('Hello, World');
            $t->save($con);
        }
        $this->assertEquals('hello-world-2', $t->getSlug());

        $t = new Table13();
        $t->setTitle('World');
        $t->save($con);

        $this->assertEquals('world', $t->getSlug());

        $t = new Table13();
        $t->setTitle('World');
        $t->save($con);

        $this->assertEquals('world-1', $t->getSlug());

        $t = new Table13();
        $t->setTitle('Hello, World');
        $t->save($con);

        $this->assertEquals('hello-world-3', $t->getSlug());

        $t = new Table13();
        $t->setTitle('World');
        $t->save($con);

        $this->assertEquals('world-2', $t->getSlug());

        $t = new Table13();
        $t->setTitle('World 000');
        $t->save($con);

        $this->assertEquals('world-000', $t->getSlug());

        $t = new Table13();
        $t->setTitle('World');
        $t->save($con);

        $this->assertEquals('world-101', $t->getSlug());

        $t = new Table13();
        $t->setTitle('World');
        $t->save($con);

        $this->assertEquals('world-102', $t->getSlug());
    }
}

class TestableTable13 extends Table13
{
    public function createSlug()
    {
        return parent::createSlug();
    }

    public function createRawSlug()
    {
        return parent::createRawSlug();
    }

    public static function cleanupSlugPart($slug, $separator = '-')
    {
        return parent::cleanupSlugPart($slug, $separator);
    }

    public function makeSlugUnique($slug, $separator = '-', $increment = 0)
    {
        return parent::makeSlugUnique($slug, $separator, $increment);
    }
}

class TestableTable14 extends Table14
{
    public function createSlug()
    {
        return parent::createSlug();
    }

    public function createRawSlug()
    {
        return parent::createRawSlug();
    }

    public static function limitSlugSize($slug, $incrementReservedSpace = 3)
    {
        return parent::limitSlugSize($slug, $incrementReservedSpace);
    }
}

class TestTableWithScope extends TableWithScope
{
    public function createSlug()
    {
        return parent::createSlug();
    }

    public function createRawSlug()
    {
        return parent::createRawSlug();
    }

    public static function cleanupSlugPart($slug, $separator = '-')
    {
        return parent::cleanupSlugPart($slug, $separator);
    }

    public function makeSlugUnique($slug, $separator = '-', $increment = 0)
    {
        return parent::makeSlugUnique($slug, $separator, $increment);
    }
}
