<commit_before><?php

declare(strict_types=1);

/*
 * This file is part of the ************************ package.
 * _____________                           _______________
 *  ______/     \__  _____  ____  ______  / /_  _________
 *   ____/ __   / / / / _ \/ __`\/ / __ \/ __ \/ __ \___
 *    __/ / /  / /_/ /  __/ /  \  / /_/ / / / / /_/ /__
 *      \_\ \_/\____/\___/_/   / / .___/_/ /_/ .___/
 *         \_\                /_/_/         /_/
 *
 * The PHP Framework For Code Poem As Free As Wind. <Query Yet Simple>
 * (c) 2010-2019 http://queryphp.com All rights reserved.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Tests\Database\Ddd;

use Leevel\Database\Ddd\Entity;
use Leevel\Database\Ddd\IUnitOfWork;
use Leevel\Database\Ddd\UnitOfWork;
use Tests\Database\DatabaseTestCase as TestCase;
use Tests\Database\Ddd\Entity\CompositeId;
use Tests\Database\Ddd\Entity\Guestbook;
use Tests\Database\Ddd\Entity\GuestbookRepository;
use Tests\Database\Ddd\Entity\Relation\Post;
use Throwable;

/**
 * UnitOfWork test.
 *
 * @author <NAME> <<EMAIL>>
 *
 * @since 2018.10.25
 *
 * @version 1.0
 *
 * @api(
 *     title="事务工作单元",
 *     path="orm/unitofwork",
 *     description="用事务工作单元更好地处理数据库相关工作。",
 * )
 */
class UnitOfWorkTest extends TestCase
{
    /**
     * @api(
     *     title="保存一个实体",
     *     description="",
     *     note="通过 persist 方法保存一个实体，并通过 flush 将实体持久化到数据库。",
     * )
     */
    public function testBaseUse(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => 'hello world',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $this->assertNull($post->id);

        $work->persist($post);

        $work->flush();

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);
    }

    /**
     * @api(
     *     title="保存多个实体",
     *     description="",
     *     note="底层会开启一个事务，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testPersist(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => 'hello world',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $this->assertNull($post->id);

        $post2 = new Post([
            'title'   => 'hello world',
            'user_id' => 2,
            'summary' => 'foo bar',
        ]);

        $this->assertNull($post2->id);

        $work->persist($post);
        $work->persist($post2);

        $work->flush();

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame(2, $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
    }

    /**
     * @api(
     *     title="新增实体",
     *     description="",
     *     note="底层执行的是 insert 语句，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testCreate(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => 'hello world',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $post2 = new Post([
            'title'   => 'hello world',
            'user_id' => 2,
            'summary' => 'foo bar',
        ]);

        $this->assertNull($post->id);
        $this->assertNull($post2->id);
        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $work->create($post);
        $work->create($post2);

        $this->assertTrue($work->created($post));
        $this->assertTrue($work->created($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame(2, $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
    }

    public function testCreateBefore(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => 'hello world',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $post2 = new Post([
            'title'   => 'hello world',
            'user_id' => 2,
            'summary' => 'foo bar',
        ]);

        $this->assertNull($post->id);
        $this->assertNull($post2->id);
        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $work->create($post);
        $work->createBefore($post2);

        $this->assertTrue($work->created($post));
        $this->assertTrue($work->created($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $this->assertSame('2', $post->id);
        $this->assertSame('2', $post['id']);
        $this->assertSame('2', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);

        $this->assertSame('1', $post2->id);
        $this->assertSame('1', $post2['id']);
        $this->assertSame('1', $post2->getterId());
        $this->assertSame(2, $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
    }

    /**
     * @api(
     *     title="更新实体",
     *     description="",
     *     note="底层执行的是 update 语句，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testUpdate(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $this->assertSame(
            '2',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 2,
                    'summary' => 'foo bar',
                ]));

        $post = Post::find(1);

        $post2 = Post::find(2);

        $this->assertInstanceof(Entity::class, $post);
        $this->assertInstanceof(Entity::class, $post2);
        $this->assertInstanceof(Post::class, $post);
        $this->assertInstanceof(Post::class, $post2);

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('post summary', $post->summary);
        $this->assertSame('hello world', $post->title);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame('2', $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
        $this->assertSame('hello world', $post2->title);

        $this->assertFalse($work->updated($post));
        $this->assertFalse($work->updated($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $post->title = 'new post title';
        $post->summary = 'new post summary';

        $post2->title = 'new post2 title';
        $post2->summary = 'new post2 summary';

        $work->update($post);
        $work->update($post2);

        $this->assertTrue($work->updated($post));
        $this->assertTrue($work->updated($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->updated($post));
        $this->assertFalse($work->updated($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('new post title', $post->title);
        $this->assertSame('new post summary', $post->summary);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame('2', $post2->userId);
        $this->assertSame('new post2 title', $post2->title);
        $this->assertSame('new post2 summary', $post2->summary);
    }

    /**
     * @api(
     *     title="删除实体",
     *     description="",
     *     note="底层执行的是 delete 语句，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testDelete(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $this->assertSame(
            '2',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 2,
                    'summary' => 'foo bar',
                ]));

        $post = Post::find(1);

        $post2 = Post::find(2);

        $this->assertInstanceof(Entity::class, $post);
        $this->assertInstanceof(Entity::class, $post2);
        $this->assertInstanceof(Post::class, $post);
        $this->assertInstanceof(Post::class, $post2);

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('post summary', $post->summary);
        $this->assertSame('hello world', $post->title);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame('2', $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
        $this->assertSame('hello world', $post2->title);

        $this->assertFalse($work->deleted($post));
        $this->assertFalse($work->deleted($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $work->delete($post);
        $work->delete($post2);

        $this->assertTrue($work->deleted($post));
        $this->assertTrue($work->deleted($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->deleted($post));
        $this->assertFalse($work->deleted($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $postAfter = Post::find(1);

        $post2After = Post::find(2);

        $this->assertNull($postAfter->id);
        $this->assertNull($postAfter['id']);
        $this->assertNull($postAfter->getterId());
        $this->assertNull($postAfter->userId);
        $this->assertNull($postAfter->title);
        $this->assertNull($postAfter->summary);

        $this->assertNull($post2After->id);
        $this->assertNull($post2After['id']);
        $this->assertNull($post2After->getterId());
        $this->assertNull($post2After->userId);
        $this->assertNull($post2After->title);
        $this->assertNull($post2After->summary);
    }

    /**
     * @api(
     *     title="刷新实体",
     *     description="",
     *     note="底层执行的是 select 语句，这个操作会读取数据库最新信息并刷新实体的属性。",
     * )
     */
    public function testRefresh(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $post = new Post([
            'id'      => 1,
            'title'   => 'old',
            'summary' => 'old',
        ], true);

        $this->assertSame(1, $post->getterId());
        $this->assertSame('old', $post->getterSummary());
        $this->assertSame('old', $post->getterTitle());

        $work->persist($post);
        $work->refresh($post);

        $this->assertSame('1', $post->getterId());
        $this->assertSame('post summary', $post->getterSummary());
        $this->assertSame('hello world', $post->getterTitle());

        $work->flush();

        $post = Post::find(1);

        $this->assertInstanceof(Entity::class, $post);
        $this->assertInstanceof(Post::class, $post);

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('post summary', $post->summary);
        $this->assertSame('hello world', $post->title);
    }

    /**
     * @api(
     *     title="手工启动事务 beginTransaction",
     *     description="",
     *     note="通常来说事务工作单元会自动帮你处理事务，可以通过手工 beginTransaction，成功 commit 或者失败 rollBack，系统提供了 API 让你也手工开启事务处理。",
     * )
     */
    public function testBeginTransaction(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $work->beginTransaction();

        $post = Post::find(1);
        $work->update($post);

        try {
            $post->title = 'new title';

            $work->flush();
            $work->commit();
        } catch (Throwable $e) {
            $work->close();
            $work->rollBack();
        }

        $this->assertSame('1', $post->getterId());
        $this->assertSame('new title', $post->getterTitle());
    }

    /**
     * @api(
     *     title="执行失败事务回滚 rollBack",
     *     description="",
     *     note="底层会自动运行一个事务，如果执行失败自动回滚，不会更新数据库。",
     * )
     */
    public function testFlushButRollBack(): void
    {
        $this->expectException(\Leevel\Database\ReplaceException::class);
        $this->expectExceptionMessage(
            'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry \'1\' for key \'PRIMARY\''
        );

        $work = UnitOfWork::make();

        $post = new Post([
            'id'      => 1,
            'title'   => 'old',
            'summary' => 'old',
        ]);

        $post2 = new Post([
            'id'      => 1,
            'title'   => 'old',
            'summary' => 'old',
        ]);

        $work->create($post);
        $work->create($post2);

        $work->flush();
    }

    /**
     * @api(
     *     title="事务包裹在闭包中 transaction",
     *     description="",
     *     note="可以将事务包裹在一个闭包中，如果执行失败自动回滚，不会更新数据库。",
     * )
     */
    public function testTransaction(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $work->transaction(function ($w) {
            $post = Post::find(1);
            $w->update($post);

            $post->title = 'new title';
        });

        $newPost = Post::find(1);

        $this->assertSame('1', $newPost->getterId());
        $this->assertSame('new title', $newPost->getterTitle());
    }

    /**
     * @api(
     *     title="事务包裹在闭包中失败回滚 transaction ",
     *     description="",
     *     note="可以将事务包裹在一个闭包中，执行失败自动回滚测试，不会更新数据库。",
     * )
     */
    public function testTransactionAndRollBack(): void
    {
        $this->expectException(\Leevel\Database\ReplaceException::class);
        $this->expectExceptionMessage(
            'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry \'1\' for key \'PRIMARY\''
        );

        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $work->transaction(function ($w) {
            $post = new Post([
                'id'      => 1,
                'title'   => 'old',
                'summary' => 'old',
            ]);

            $post2 = new Post([
                'id'      => 1,
                'title'   => 'old',
                'summary' => 'old',
            ]);

            $w->create($post);
            $w->create($post2);
        });

        $this->assertSame(0, $connect->table('post')->findCount());
    }

    /**
     * @api(
     *     title="设置根实体 setRootEntity",
     *     description="",
     *     note="系统默认读取基础的数据库配置来处理数据相关信息，设置跟实体可以更改事务处理的数据库连接。",
     * )
     */
    public function testSetRootEntity(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([<commit_msg>fix(tests): SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value<commit_after><?php

declare(strict_types=1);

/*
 * This file is part of the ************************ package.
 * _____________                           _______________
 *  ______/     \__  _____  ____  ______  / /_  _________
 *   ____/ __   / / / / _ \/ __`\/ / __ \/ __ \/ __ \___
 *    __/ / /  / /_/ /  __/ /  \  / /_/ / / / / /_/ /__
 *      \_\ \_/\____/\___/_/   / / .___/_/ /_/ .___/
 *         \_\                /_/_/         /_/
 *
 * The PHP Framework For Code Poem As Free As Wind. <Query Yet Simple>
 * (c) 2010-2019 http://queryphp.com All rights reserved.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Tests\Database\Ddd;

use Leevel\Database\Ddd\Entity;
use Leevel\Database\Ddd\IUnitOfWork;
use Leevel\Database\Ddd\UnitOfWork;
use Tests\Database\DatabaseTestCase as TestCase;
use Tests\Database\Ddd\Entity\CompositeId;
use Tests\Database\Ddd\Entity\Guestbook;
use Tests\Database\Ddd\Entity\GuestbookRepository;
use Tests\Database\Ddd\Entity\Relation\Post;
use Throwable;

/**
 * UnitOfWork test.
 *
 * @author <NAME> <<EMAIL>>
 *
 * @since 2018.10.25
 *
 * @version 1.0
 *
 * @api(
 *     title="事务工作单元",
 *     path="orm/unitofwork",
 *     description="用事务工作单元更好地处理数据库相关工作。",
 * )
 */
class UnitOfWorkTest extends TestCase
{
    /**
     * @api(
     *     title="保存一个实体",
     *     description="",
     *     note="通过 persist 方法保存一个实体，并通过 flush 将实体持久化到数据库。",
     * )
     */
    public function testBaseUse(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => 'hello world',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $this->assertNull($post->id);

        $work->persist($post);

        $work->flush();

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);
    }

    /**
     * @api(
     *     title="保存多个实体",
     *     description="",
     *     note="底层会开启一个事务，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testPersist(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => 'hello world',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $this->assertNull($post->id);

        $post2 = new Post([
            'title'   => 'hello world',
            'user_id' => 2,
            'summary' => 'foo bar',
        ]);

        $this->assertNull($post2->id);

        $work->persist($post);
        $work->persist($post2);

        $work->flush();

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame(2, $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
    }

    /**
     * @api(
     *     title="新增实体",
     *     description="",
     *     note="底层执行的是 insert 语句，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testCreate(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => 'hello world',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $post2 = new Post([
            'title'   => '<NAME>',
            'user_id' => 2,
            'summary' => 'foo bar',
        ]);

        $this->assertNull($post->id);
        $this->assertNull($post2->id);
        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $work->create($post);
        $work->create($post2);

        $this->assertTrue($work->created($post));
        $this->assertTrue($work->created($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame(2, $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
    }

    public function testCreateBefore(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $post = new Post([
            'title'   => '<NAME>',
            'user_id' => 1,
            'summary' => 'post summary',
        ]);

        $post2 = new Post([
            'title'   => '<NAME>',
            'user_id' => 2,
            'summary' => 'foo bar',
        ]);

        $this->assertNull($post->id);
        $this->assertNull($post2->id);
        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $work->create($post);
        $work->createBefore($post2);

        $this->assertTrue($work->created($post));
        $this->assertTrue($work->created($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->created($post));
        $this->assertFalse($work->created($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $this->assertSame('2', $post->id);
        $this->assertSame('2', $post['id']);
        $this->assertSame('2', $post->getterId());
        $this->assertSame(1, $post->userId);
        $this->assertSame('post summary', $post->summary);

        $this->assertSame('1', $post2->id);
        $this->assertSame('1', $post2['id']);
        $this->assertSame('1', $post2->getterId());
        $this->assertSame(2, $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
    }

    /**
     * @api(
     *     title="更新实体",
     *     description="",
     *     note="底层执行的是 update 语句，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testUpdate(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $this->assertSame(
            '2',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 2,
                    'summary' => 'foo bar',
                ]));

        $post = Post::find(1);

        $post2 = Post::find(2);

        $this->assertInstanceof(Entity::class, $post);
        $this->assertInstanceof(Entity::class, $post2);
        $this->assertInstanceof(Post::class, $post);
        $this->assertInstanceof(Post::class, $post2);

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('post summary', $post->summary);
        $this->assertSame('hello world', $post->title);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame('2', $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
        $this->assertSame('hello world', $post2->title);

        $this->assertFalse($work->updated($post));
        $this->assertFalse($work->updated($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $post->title = 'new post title';
        $post->summary = 'new post summary';

        $post2->title = 'new post2 title';
        $post2->summary = 'new post2 summary';

        $work->update($post);
        $work->update($post2);

        $this->assertTrue($work->updated($post));
        $this->assertTrue($work->updated($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->updated($post));
        $this->assertFalse($work->updated($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('new post title', $post->title);
        $this->assertSame('new post summary', $post->summary);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame('2', $post2->userId);
        $this->assertSame('new post2 title', $post2->title);
        $this->assertSame('new post2 summary', $post2->summary);
    }

    /**
     * @api(
     *     title="删除实体",
     *     description="",
     *     note="底层执行的是 delete 语句，只有全部保存成功才会真正持久化到数据库。",
     * )
     */
    public function testDelete(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $this->assertSame(
            '2',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 2,
                    'summary' => 'foo bar',
                ]));

        $post = Post::find(1);

        $post2 = Post::find(2);

        $this->assertInstanceof(Entity::class, $post);
        $this->assertInstanceof(Entity::class, $post2);
        $this->assertInstanceof(Post::class, $post);
        $this->assertInstanceof(Post::class, $post2);

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('post summary', $post->summary);
        $this->assertSame('hello world', $post->title);

        $this->assertSame('2', $post2->id);
        $this->assertSame('2', $post2['id']);
        $this->assertSame('2', $post2->getterId());
        $this->assertSame('2', $post2->userId);
        $this->assertSame('foo bar', $post2->summary);
        $this->assertSame('hello world', $post2->title);

        $this->assertFalse($work->deleted($post));
        $this->assertFalse($work->deleted($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $work->delete($post);
        $work->delete($post2);

        $this->assertTrue($work->deleted($post));
        $this->assertTrue($work->deleted($post2));
        $this->assertTrue($work->registered($post));
        $this->assertTrue($work->registered($post2));

        $work->flush();

        $this->assertFalse($work->deleted($post));
        $this->assertFalse($work->deleted($post2));
        $this->assertFalse($work->registered($post));
        $this->assertFalse($work->registered($post2));

        $postAfter = Post::find(1);

        $post2After = Post::find(2);

        $this->assertNull($postAfter->id);
        $this->assertNull($postAfter['id']);
        $this->assertNull($postAfter->getterId());
        $this->assertNull($postAfter->userId);
        $this->assertNull($postAfter->title);
        $this->assertNull($postAfter->summary);

        $this->assertNull($post2After->id);
        $this->assertNull($post2After['id']);
        $this->assertNull($post2After->getterId());
        $this->assertNull($post2After->userId);
        $this->assertNull($post2After->title);
        $this->assertNull($post2After->summary);
    }

    /**
     * @api(
     *     title="刷新实体",
     *     description="",
     *     note="底层执行的是 select 语句，这个操作会读取数据库最新信息并刷新实体的属性。",
     * )
     */
    public function testRefresh(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $post = new Post([
            'id'      => 1,
            'title'   => 'old',
            'summary' => 'old',
        ], true);

        $this->assertSame(1, $post->getterId());
        $this->assertSame('old', $post->getterSummary());
        $this->assertSame('old', $post->getterTitle());

        $work->persist($post);
        $work->refresh($post);

        $this->assertSame('1', $post->getterId());
        $this->assertSame('post summary', $post->getterSummary());
        $this->assertSame('hello world', $post->getterTitle());

        $work->flush();

        $post = Post::find(1);

        $this->assertInstanceof(Entity::class, $post);
        $this->assertInstanceof(Post::class, $post);

        $this->assertSame('1', $post->id);
        $this->assertSame('1', $post['id']);
        $this->assertSame('1', $post->getterId());
        $this->assertSame('1', $post->userId);
        $this->assertSame('post summary', $post->summary);
        $this->assertSame('hello world', $post->title);
    }

    /**
     * @api(
     *     title="手工启动事务 beginTransaction",
     *     description="",
     *     note="通常来说事务工作单元会自动帮你处理事务，可以通过手工 beginTransaction，成功 commit 或者失败 rollBack，系统提供了 API 让你也手工开启事务处理。",
     * )
     */
    public function testBeginTransaction(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $work->beginTransaction();

        $post = Post::find(1);
        $work->update($post);

        try {
            $post->title = 'new title';

            $work->flush();
            $work->commit();
        } catch (Throwable $e) {
            $work->close();
            $work->rollBack();
        }

        $this->assertSame('1', $post->getterId());
        $this->assertSame('new title', $post->getterTitle());
    }

    /**
     * @api(
     *     title="执行失败事务回滚 rollBack",
     *     description="",
     *     note="底层会自动运行一个事务，如果执行失败自动回滚，不会更新数据库。",
     * )
     */
    public function testFlushButRollBack(): void
    {
        $this->expectException(\Leevel\Database\ReplaceException::class);
        $this->expectExceptionMessage(
            'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry \'1\' for key \'PRIMARY\''
        );

        $work = UnitOfWork::make();

        $post = new Post([
            'id'      => 1,
            'title'   => 'old',
            'summary' => 'old',
            'user_id' => 0,
        ]);

        $post2 = new Post([
            'id'      => 1,
            'title'   => 'old',
            'summary' => 'old',
            'user_id' => 0,
        ]);

        $work->create($post);
        $work->create($post2);

        $work->flush();
    }

    /**
     * @api(
     *     title="事务包裹在闭包中 transaction",
     *     description="",
     *     note="可以将事务包裹在一个闭包中，如果执行失败自动回滚，不会更新数据库。",
     * )
     */
    public function testTransaction(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
                    'title'   => 'hello world',
                    'user_id' => 1,
                    'summary' => 'post summary',
                ]));

        $work->transaction(function ($w) {
            $post = Post::find(1);
            $w->update($post);

            $post->title = 'new title';
        });

        $newPost = Post::find(1);

        $this->assertSame('1', $newPost->getterId());
        $this->assertSame('new title', $newPost->getterTitle());
    }

    /**
     * @api(
     *     title="事务包裹在闭包中失败回滚 transaction ",
     *     description="",
     *     note="可以将事务包裹在一个闭包中，执行失败自动回滚测试，不会更新数据库。",
     * )
     */
    public function testTransactionAndRollBack(): void
    {
        $this->expectException(\Leevel\Database\ReplaceException::class);
        $this->expectExceptionMessage(
            'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry \'1\' for key \'PRIMARY\''
        );

        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $work->transaction(function ($w) {
            $post = new Post([
                'id'      => 1,
                'title'   => 'old',
                'summary' => 'old',
                'user_id' => 0,
            ]);

            $post2 = new Post([
                'id'      => 1,
                'title'   => 'old',
                'summary' => 'old',
                'user_id' => 0,
            ]);

            $w->create($post);
            $w->create($post2);
        });

        $this->assertSame(0, $connect->table('post')->findCount());
    }

    /**
     * @api(
     *     title="设置根实体 setRootEntity",
     *     description="",
     *     note="系统默认读取基础的数据库配置来处理数据相关信息，设置跟实体可以更改事务处理的数据库连接。",
     * )
     */
    public function testSetRootEntity(): void
    {
        $work = UnitOfWork::make();

        $this->assertInstanceof(UnitOfWork::class, $work);
        $this->assertInstanceof(IUnitOfWork::class, $work);

        $connect = $this->createDatabaseConnect();

        $this->assertSame(
            '1',
            $connect
                ->table('post')
                ->insert([
{'fraction_non_alphanumeric': 0.19430223158513582, 'fraction_numerical': 0.01143962962055704, 'mean_word_length': 2.051349129232379, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 936, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5270754', 'n_tokens_mistral': 13146, 'n_tokens_neox': 12926, 'n_words': 1819}