Skip to content

Conversation

@Sti3bas
Copy link
Contributor

@Sti3bas Sti3bas commented Sep 28, 2018

This PR adds ability to add your own database drivers.

Use case:

This was the single problem I encountered when tried to use SQLite database for tests.

I can now use SQLite in-memory databases for my tests by simply adding app('tenancy.db.drivers')->put('sqlite', \Tests\Support\SqliteDriver::class); to setUp method and creating custom SQLite driver:

class SqliteDriver implements DatabaseGenerator
{
    public function created(Created $event, array $config, Connection $connection): bool
    {
        return true;
    }
    
    public function updated(Updated $event, array $config, Connection $connection): bool
    {
        return true;
    }
    
    public function deleted(Deleted $event, array $config, Connection $connection): bool
    {
        return true;
    }
}

Copy link
Contributor

@luceos luceos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very impressive! A few requests for changes, but all is well 👍

@luceos luceos merged commit 516975e into tenancy:5.x Oct 1, 2018
@luceos
Copy link
Contributor

luceos commented Oct 1, 2018

Thank you, this is very valuable 👍

@anyb1s
Copy link

anyb1s commented Oct 5, 2018

@Sti3bas, how do you do your migrations and seeding with system/tenant memory dbs?
I am creating website and hostnames, then setting up the environment for the tenant and then tenant models say dont have their tables?

@Sti3bas
Copy link
Contributor Author

Sti3bas commented Oct 5, 2018

@anyb1s yes, you are right. That's because tenancy:migrate command purges the tenant connection which means that SQLite in memory database is getting deleted. I'm going to create a PR to fix this, but for now you can manually call Laravel's built-in migrate command (which doesn't purge the connection) after you create a website:

// create hostnames and website here

Artisan::call('migrate', [
    '--database' => 'tenant',
    '--force' => true,
    '--path' => config('tenancy.db.tenant-migrations-path'),
    '--realpath' => true,
]);

@anyb1s
Copy link

anyb1s commented Oct 5, 2018

@Sti3bas Thank you very much for the response but I am still struggling to get this working. (I am definitely doing something wrong! :)

In my phpunit xml I have the following: basically create
<env name="DB_CONNECTION" value="testing_system"/> <env name="TENANCY_SYSTEM_CONNECTION_NAME" value="testing_system"/> <env name="TENANCY_TENANT_CONNECTION_NAME" value="testing_tenant"/>

My database settings:
'testing_system' => [ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', ], 'testing_tenant' => [ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', ],

In my setup I use the website repository to create a website and a hostname attached to it and then set the environment.
I am using RefreshDatabase trait which runs the system migrations, but when I run the tenant migration even using the above still says the tables don't exist.

`
$hostname = new Hostname();
$hostname->fqdn = 'testing_tenant.testing';
$hostname->save();

       $website = new Website();
        $website->uuid = 'testing_tenant';
        $website->save();

        $this->artisan('migrate', [
            '--database' => 'testing_tenant',
            '--force' => true,
            '--path' => config('tenancy.db.tenant-migrations-path'),
            '--realpath' => true,
        ]);

        $website->hostnames()->save($hostname);
        app(Environment::class)->tenant($website);````

And then in my I test I do
$response = $this->json('GET', 'http://testing_tenant.testing/my-page');
saying testing_tenant database does not exists :(

@Sti3bas
Copy link
Contributor Author

Sti3bas commented Oct 5, 2018

@anyb1s I've spent few evenings trying to find the best solution and here is how I've made it to work:

tests/TenantAwareTestCase.php:

namespace Tests;

use Hyn\Tenancy\Models\Website;
use Hyn\Tenancy\Contracts\Repositories\WebsiteRepository;
use Hyn\Tenancy\Models\Hostname;
use Hyn\Tenancy\Contracts\Repositories\HostnameRepository;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Hyn\Tenancy\Providers\Tenants\RouteProvider;
use Hyn\Tenancy\Contracts\CurrentHostname;

class TenantAwareTestCase extends TestCase
{
    // turn on database transactions for both database connections
    // otherwise you will get an error when you will run multiple tests
    protected $connectionsToTransact = [
        'system',
        'tenant',
    ];
    
    // refresh system database
    use RefreshDatabase;

    protected function setUp()
    {
        parent::setUp();

        // register SQLite database driver (you can find the source code above)
        app('tenancy.db.drivers')->put('sqlite', SqliteDriver::class);

        // bypass tenant connection settings modifications
        config(['tenancy.db.tenant-division-mode' => 'bypass']);

        // create a website
        $website = new Website;
        app(WebsiteRepository::class)->create($website);

        // create a hostname
        $hostname = new Hostname;
        $hostname->fqdn = 'test.test.test';
        $hostname = app(HostnameRepository::class)->create($hostname);
        app(HostnameRepository::class)->attach($hostname, $website);
        
        // at this point, tenant conection settings is an empty array (not sure why)
        // you can hack this by setting it directly
        config([
            'database.connections.tenant' => [
                'driver' => 'sqlite',
                'database' => ':memory:',
            ],
        ]);

        // set current hostname
        app()->singleton(CurrentHostname::class, function () use ($hostname) {
            return $hostname;
        });

        // register tenant routes, by default web/tenants.php
        (new RouteProvider(app()))->boot();

        // migrate tenant database
        $this->artisan('migrate', [
            // this is a connection name, not a database name!!!
            '--database' => 'tenant',
            '--force' => true,
            '--path' => config('tenancy.db.tenant-migrations-path'),
            '--realpath' => true,
        ]);
    }
}

config/database.php:

'system' => [
    'driver' => env('TENANCY_DRIVER', 'mysql'),
    'host' => env('TENANCY_HOST', '127.0.0.1'),
    'port' => env('TENANCY_PORT', '3306'),
    'database' => env('TENANCY_DATABASE', 'tenancy'),
    'username' => env('TENANCY_USERNAME', 'tenancy'),
    'password' => env('TENANCY_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => true,
    'engine' => null,
],

phpunit.xml:

<env name="DB_CONNECTION" value="system"/>
<env name="TENANCY_DRIVER" value="sqlite"/>
<env name="TENANCY_DATABASE" value=":memory:"/>

@anyb1s
Copy link

anyb1s commented Oct 6, 2018

That's brilliant mate, thank you very much!
I managed to the migration bit working but for some reason still continue to get 404 when doing http tests. The route does not require any auth or anything.
Route::get('/', 'IndexController@index')->name('index');
$response = $this->get('/');

class HttpTest extends TenantAwareTestCase { /** @test */ public function gets_page() { $response = $this->get('/'); $response->assertStatus(200); } }

@Sti3bas
Copy link
Contributor Author

Sti3bas commented Oct 6, 2018

@anyb1s works fine for me with both global and tenant routes:

tests/Feature/ExampleTest.php:

namespace Tests\Feature;

use Tests\TenantAwareTestCase;

class ExampleTest extends TenantAwareTestCase
{
    /** @test */
    public function it_works_with_tenant_routes()
    {
        $this->get('/tenant')
            ->assertStatus(200)
            ->assertSee('Tenant');
    }

    /** @test */
    public function it_works_with_global_routes()
    {
        $this->get('/global')
            ->assertStatus(200)
            ->assertSee('Global');
    }
}

routes/web.php:

Route::get('/global', function () {
    return 'Global';
});

routes/tenants.php:

Route::get('/tenant', function () {
    return 'Tenant';
});

Both tests are passing.

@anyb1s
Copy link

anyb1s commented Oct 7, 2018

Thank you very much for the help!!!
Managed to get it running!
It is reasonably slow for a single test to pass (around 5s) but that is probably something on my look into.
Still thank you very much!

@Sti3bas
Copy link
Contributor Author

Sti3bas commented Oct 7, 2018

@anyb1s above tests runs in ~400ms each on my machine.

@anyb1s
Copy link

anyb1s commented Oct 13, 2018

@Sti3bas, my issue was had xdebug was enabled and reduced the number of hash rounds for the passwords and got it working around 500-800ms each.

Thanks again for the help, works like charm, got it working with passport (Everything is awesome)

@luceos
Copy link
Contributor

luceos commented Nov 2, 2018

@vpratfr Vincent please move this discussion out of this PR and/or this repository. It's too much noise. If there's something this package can do please create an issue or PR, I'll gladly look at either.

vpratfr added a commit to vpratfr/docs that referenced this pull request Nov 2, 2018
Getting started with testing. Related to tenancy/multi-tenant#627.
@Sti3bas Sti3bas mentioned this pull request Jan 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants