AliceBundle icon indicating copy to clipboard operation
AliceBundle copied to clipboard

RefreshDatabaseTrait is not executed in time

Open renja-g opened this issue 3 years ago • 4 comments

I was trying to use RefreshDatabaseTrait for API testing but for some reason the assert gave me 404.

<?php

namespace App\Tests\Functional;

use App\test\CustomApiTestCase;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;


class UserResourceTest extends CustomApiTestCase
{
    use RefreshDatabaseTrait;

    public function testGetItemUser(): void
    {
        $client = static::createClient();
        
        $user = $this->createUser($client, '[email protected]', 'Test_password_1!', 'Test User 1');

        $client->request('GET', '/api/users/' . $user->getId());
        $this->assertResponseStatusCodeSame(401, 'GET item (no auth)');
    }
}

After a long time of testing, I found out that with this code only the first assert fails.

    public function testGetItemUser(): void
    {
        $client = static::createClient();

        $user = $this->createUser($client, '[email protected]', 'Test_password_1!', 'Test User 1');
        
        $client->request('GET', '/api/users/' . $user->getId());
        $this->assertResponseStatusCodeSame(401, 'GET item (no auth 1)');

        $client->request('GET', '/api/users/' . $user->getId());
        $this->assertResponseStatusCodeSame(401, 'GET item (no auth 2)');
    }

It feels like the database refresh is not finishing in time so that the first user is deleted

1. database refresh start
2. Create User
3. database refresh end

renja-g avatar Jan 04 '23 15:01 renja-g

nvm, I'm not even creating two users. but it is ward because if I remove use RefreshDatabaseTrait; the code works like this:

    public function testGetItemUser(): void
    {
        $client = static::createClient();
        
        $user = $this->createUser($client, '[email protected]', 'Test_password_1!', 'Test User 1');

        $client->request('GET', '/api/users/' . $user->getId());
        $this->assertResponseStatusCodeSame(401, 'GET item (no auth)');
    }

renja-g avatar Jan 04 '23 15:01 renja-g

Isn't it an issue with static::createClient() that re-creates a kernel with maybe a different connection?

theofidry avatar Jan 04 '23 17:01 theofidry

Isn't it an issue with static::createClient() that re-creates a kernel with maybe a different connection?

But then why does it work if I remove the use RefreshDatabaseTrait;

renja-g avatar Jan 04 '23 20:01 renja-g

also, when running the full test the first two tests run true fine but when hitting the third I get an SQL Duplicate entry error for "[email protected]" but this should not be happening if the reset would have already happend.

<?php

namespace App\Tests\Functional;

use App\test\CustomApiTestCase;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;


class UserResourceTest extends CustomApiTestCase
{
    use RefreshDatabaseTrait;

    // Tests:
    // POST (no auth)
    public function testPostUser(): void
    {
        $client = static::createClient();

        // POST (no auth)
        $this->createUser($client, '[email protected]', 'Test_password_1!', 'Test User 1');
        $this->assertResponseStatusCodeSame(201, 'POST (no auth)');
    }

    // Tests:
    // GET collection (no auth)
    // GET collection (auth)
    // GET collection (admin auth)
    public function testGetCollectionUser(): void
    {
        $client = static::createClient();

        // GET collection (no auth)
        $client->request('GET', '/api/users');
        $this->assertResponseStatusCodeSame(401, 'GET collection (no auth)');


        // GET collection (auth)
        $user = $this->createUser($client, '[email protected]', 'Test_password_1!', 'Test User 1');
        $token = $this->createAuth($client, $user);
        $client->request('GET', '/api/users', [
            'headers' => [
                'Authorization' => 'Bearer ' . $token,
            ],
        ]);
        $this->assertResponseStatusCodeSame(403, 'GET collection (auth)');


        // GET collection (admin auth)
        $admin = $this->createAdmin($client, '[email protected]', 'Test_password_1!', 'Test Admin 1');
        $token = $this->createAuth($client, $admin);
        $client->request('GET', '/api/users', [
            'headers' => [
                'Authorization' => 'Bearer ' . $token,
            ],
        ]);
        $this->assertResponseStatusCodeSame(200, 'GET collection (admin auth)');
    }


    // Tests:
    // GET item (no auth)
    // GET item (auth owne)
    // GET item (auth *)
    // GET item (admin auth own)
    // GET item (admin auth *)
    public function testGetItemUser(): void
    {
        $client = static::createClient();
        // GET item (no auth)
        $user = $this->createUser($client, '[email protected]', 'Test_password_1!', 'Test User 1');

        $client->request('GET', '/api/users/' . $user->getId());
        $this->assertResponseStatusCodeSame(401, 'GET item (no auth 2)');

        $client->request('GET', '/api/users/' . $user->getId());
        $this->assertResponseStatusCodeSame(401, 'GET item (no auth 2)');
    }
}

renja-g avatar Jan 05 '23 09:01 renja-g

I'm not sure but it looks similar to my problem.

When I load fixtures from files (use R...DatabaseTrait in test class) I can't save any new entity to the database by using EntityManager or by making a request to the api.

@renja-g confirm if it's the same in your case. If not @theofidry I'll create a new issue.

d0niek avatar Feb 16 '24 17:02 d0niek

I've fixed my issue. The problem was that the "base test class" and specific "test class" use R...DatabaseTrait so it was loaded twice. With this for loaded entities as fixtures every new EntityClass was marked as MANAGED by doctrine EntityManager (UnitOfWork).

d0niek avatar Feb 19 '24 16:02 d0niek

so it was loaded twice

I wonder if there would be a way to detect this and throw an error in this case?

theofidry avatar Feb 20 '24 08:02 theofidry

I don't know exactly the tests flow but is bootKernel run before test-case and ensureKernelShutdown after? If yes maybe add some flag (?) there and check it?

d0niek avatar Feb 20 '24 08:02 d0niek

Actually it looks like this is already handled? https://github.com/theofidry/AliceBundle/blob/master/src/PhpUnit/RefreshDatabaseTrait.php#L38 So there maybe more to it

theofidry avatar Feb 20 '24 08:02 theofidry

Yes, I saw it! But ReloadDatabaseTrait is missing it.

d0niek avatar Feb 20 '24 09:02 d0niek

I think this may be related to mixing up Refresh and Reload.

The fix is NOT to add what is in Refresh to Reload. The idea of Reload is to populate the database at kernel boot, if there is already fixtures a purge (via delete or truncate) is done before populating the database. So even calling it several times over should be fine (although expensive for no reasons).

Where this will get messy is if Reload is mixed up with Refresh. Indeed Refresh populates the DB once and then uses transactions to rollback. But purging with a truncate will be effective and remove the data regardless of the transaction. Calling it twice should also have no effect (besides having nested transactions).

I am not sure if this really addresses the original case, but I will close this as I cannot find anything. if you encounter the problem and want me to look into it please provide a small reproducer that I can debug.

theofidry avatar Mar 08 '24 20:03 theofidry