diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21ae92a0..b1d26494 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,8 @@ jobs: include: - laravel: "^10.0" php: "8.2" + - laravel: "^11.0" + php: "8.3" steps: - name: Checkout @@ -28,7 +30,7 @@ jobs: composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update composer update --prefer-dist --no-interaction - name: Run tests - run: ./vendor/bin/pest + run: COLUMNS=200 ./vendor/bin/pest --compact --colors=always env: DB_PASSWORD: password DB_USERNAME: root @@ -41,7 +43,7 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 with: - token: 24382d15-84e7-4a55-bea4-c4df96a24a9b + token: 24382d15-84e7-4a55-bea4-c4df96a24a9b # todo it's fine if this is here in plaintext, but move this to GH secrets eventually services: postgres: diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 56ee778e..3931ca36 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Check for todo0 + - name: Check for todo0 run: '! grep -r "todo0" --exclude-dir=workflows .' if: always() - name: Check for todo1 @@ -15,6 +15,6 @@ jobs: - name: Check for todo2 run: '! grep -r "todo2" --exclude-dir=workflows .' if: always() - - name: Check for skip() in tests - run: '! grep -r "skip(" --exclude-dir=workflows tests/' + - name: Check for non-todo skip()s in tests + run: '! grep -r "skip(" --exclude-dir=workflows tests/ | grep -v "todo"' if: always() diff --git a/.gitignore b/.gitignore index 5a5960b0..759825fb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ vendor/ .vscode/ .idea psysh +.phpunit.cache .phpunit.result.cache phpunit_var_*.xml coverage/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0095be7e..3de3a1b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,11 +4,13 @@ php-cs-fixer will fix code style violations in your pull requests. +To run it locally, use `composer cs`. + ## Running tests Run `composer docker-up` to start the containers. Then run `composer test` to run the tests. -If you need to pass additional flags to phpunit, use `./test --foo` instead of `composer test --foo`. Composer scripts unfortunately don't pass CLI arguments. +If you need to pass additional flags to phpunit, use `composer test --`, e.g. `composer test -- --filter="foo"`. Alternatively, you can use `./test --filter="foo"` If you want to run a specific test (or test file), you can also use `./t 'name of the test'`. This is equivalent to `./test --no-coverage --filter 'name of the test'` (`--no-coverage` speeds up the execution time). diff --git a/Dockerfile b/Dockerfile index 421e43d8..6b1fc440 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -# add amd64 platform to support Mac M1 -FROM --platform=linux/amd64 shivammathur/node:latest-amd64 +FROM shivammathur/node:latest +SHELL ["/bin/bash", "-c"] -ARG PHP_VERSION=8.2 +ARG PHP_VERSION=8.3 WORKDIR /var/www/html @@ -9,13 +9,18 @@ WORKDIR /var/www/html ENV TZ=Europe/London ENV LANG=en_GB.UTF-8 -# install MYSSQL ODBC Driver +# install MSSQL ODBC driver (1/2) RUN apt-get update \ && apt-get install -y gnupg2 \ && curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \ && curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list > /etc/apt/sources.list.d/mssql-release.list \ - && apt-get update \ - && ACCEPT_EULA=Y apt-get install -y unixodbc-dev=2.3.7 unixodbc=2.3.7 odbcinst1debian2=2.3.7 odbcinst=2.3.7 msodbcsql17 + && apt-get update + +# install MSSQL ODBC driver (2/2) +RUN if [[ $(uname -m) == "arm64" || $(uname -m) == "aarch64" ]]; \ + then ACCEPT_EULA=Y apt-get install -y unixodbc-dev msodbcsql18; \ + else ACCEPT_EULA=Y apt-get install -y unixodbc-dev=2.3.7 unixodbc=2.3.7 odbcinst1debian2=2.3.7 odbcinst=2.3.7 msodbcsql17; \ + fi # set PHP version RUN update-alternatives --set php /usr/bin/php$PHP_VERSION \ diff --git a/INTERNAL.md b/INTERNAL.md index 4b3297dd..3da9ceed 100644 --- a/INTERNAL.md +++ b/INTERNAL.md @@ -3,6 +3,7 @@ ## Updating the docker image used by the GH action 1. Login in to Docker Hub: `docker login -u archtechx -p` -2. Build the image (probably shut down docker-compose containers first): `docker-compose build --no-cache` -3. Tag a new image: `docker tag tenancy_test archtechx/tenancy:latest` -4. Push the image: `docker push archtechx/tenancy:latest` +1. Build the image (probably shut down docker-compose containers first): `DOCKER_DEFAULT_PLATFORM=linux/amd64 docker-compose build --no-cache` +1. Verify that tests pass on the new image: `composer test` +1. Tag a new image: `docker tag tenancy-test archtechx/tenancy:latest` +1. Push the image: `docker push archtechx/tenancy:latest` diff --git a/composer.json b/composer.json index 60713cc7..18bc62bf 100644 --- a/composer.json +++ b/composer.json @@ -17,21 +17,21 @@ "require": { "php": "^8.2", "ext-json": "*", - "illuminate/support": "^10.1", + "illuminate/support": "^10.1|^11.0", "facade/ignition-contracts": "^1.0.2", "spatie/ignition": "^1.4", "ramsey/uuid": "^4.7.3", - "stancl/jobpipeline": "2.0.0-rc1", + "stancl/jobpipeline": "2.0.0-rc2", "stancl/virtualcolumn": "dev-master", "spatie/invade": "^1.1" }, "require-dev": { - "laravel/framework": "^10.1", - "orchestra/testbench": "^8.0", + "laravel/framework": "^10.1|^11.0", + "orchestra/testbench": "^8.0|^9.0", "league/flysystem-aws-s3-v3": "^3.12.2", "doctrine/dbal": "^3.6.0", "spatie/valuestore": "^1.2.5", - "pestphp/pest": "^1.21", + "pestphp/pest": "^2.0", "larastan/larastan": "^2.4", "spatie/invade": "^1.1" }, @@ -60,16 +60,16 @@ } }, "scripts": { - "docker-up": "PHP_VERSION=8.2 docker-compose up -d", - "docker-down": "PHP_VERSION=8.2 docker-compose down", - "docker-rebuild": "PHP_VERSION=8.2 docker-compose up -d --no-deps --build", + "docker-up": "docker-compose up -d", + "docker-down": "docker-compose down", + "docker-rebuild": "PHP_VERSION=8.3 docker-compose up -d --no-deps --build", "docker-m1": "ln -s docker-compose-m1.override.yml docker-compose.override.yml", "coverage": "open coverage/phpunit/html/index.html", "phpstan": "vendor/bin/phpstan", "phpstan-pro": "vendor/bin/phpstan --pro", "cs": "php-cs-fixer fix --config=.php-cs-fixer.php", - "test": "PHP_VERSION=8.2 ./test --no-coverage", - "test-full": "PHP_VERSION=8.2 ./test" + "test": "./test --no-coverage --color=always", + "test-full": "./test --color=always" }, "minimum-stability": "dev", "prefer-stable": true, diff --git a/docker-compose-m1.override.yml b/docker-compose-m1.override.yml index 32e163e6..e74781de 100644 --- a/docker-compose-m1.override.yml +++ b/docker-compose-m1.override.yml @@ -1,7 +1,9 @@ services: mysql: - platform: linux/amd64 + # platform: linux/amd64 # either one works + image: arm64v8/mysql mysql2: - platform: linux/amd64 + # platform: linux/amd64 # either one works + image: arm64v8/mysql mssql: image: mcr.microsoft.com/azure-sql-edge diff --git a/phpunit.xml b/phpunit.xml index 0e0a8481..5bb62923 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,39 +1,34 @@ - - - - ./tests - - - - - ./src - - ./src/routes.php - - - - - - - - - - - - - - - - - - + + + + + + + + + + ./tests + + + + + + + + + + + + + + + + + ./src + + + ./src/routes.php + + diff --git a/src/Concerns/TenantAwareCommand.php b/src/Concerns/TenantAwareCommand.php index 5daa87f6..53774f83 100644 --- a/src/Concerns/TenantAwareCommand.php +++ b/src/Concerns/TenantAwareCommand.php @@ -10,8 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface; trait TenantAwareCommand { - /** @return int */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $tenants = $this->getTenants(); $exitCode = 0; diff --git a/t b/t index 3c74f2e8..1008e0f2 100755 --- a/t +++ b/t @@ -1,3 +1,3 @@ #!/bin/bash -docker-compose exec -T test vendor/bin/pest --no-coverage --filter "$@" +docker-compose exec -e COLUMNS=$(tput cols) -T test vendor/bin/pest --color=always --no-coverage --filter "$@" diff --git a/test b/test index d8de021e..c5b0e99b 100755 --- a/test +++ b/test @@ -1,3 +1,4 @@ #!/bin/bash -docker-compose exec -T test vendor/bin/pest "$@" +# --columns doesn't seem to work at the moment, so we're setting it using an environment variable +docker-compose exec -e COLUMNS=$(tput cols) -T test vendor/bin/pest "$@" diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php index ad626427..a06238b3 100644 --- a/tests/CommandsTest.php +++ b/tests/CommandsTest.php @@ -93,24 +93,6 @@ test('migrate command works with tenants option', function () { expect(Schema::hasTable('users'))->toBeTrue(); }); -test('migrate command loads schema state', function () { - $tenant = Tenant::create(); - - expect(Schema::hasTable('schema_users'))->toBeFalse(); - expect(Schema::hasTable('users'))->toBeFalse(); - - Artisan::call('tenants:migrate --schema-path="tests/Etc/tenant-schema.dump"'); - - expect(Schema::hasTable('schema_users'))->toBeFalse(); - expect(Schema::hasTable('users'))->toBeFalse(); - - tenancy()->initialize($tenant); - - // Check for both tables to see if missing migrations also get executed - expect(Schema::hasTable('schema_users'))->toBeTrue(); - expect(Schema::hasTable('users'))->toBeTrue(); -}); - test('migrate command only throws exceptions if skip-failing is not passed', function() { Tenant::create(); @@ -151,6 +133,37 @@ test('migrate command does not stop after the first failure if skip-failing is p expect($migratedTenants)->toBe(2); }); +test('the tenants migrate command uses the schema dump correctly', function (bool $schemaPathAsConfig) { + $artisanCommand = 'tenants:migrate'; + + if ($schemaPathAsConfig) { + // The schema dump path can be configured in 'tenancy.migration_parameters.--schema-path' + // The tenants:migrate command will use the schema dump located at that path by default + config(['tenancy.migration_parameters.--schema-path' => 'tests/Etc/tenant-schema.dump']); + } else { + // The schema dump path can be passed as an option to the tenants:migrate command + $artisanCommand .= ' --schema-path="tests/Etc/tenant-schema.dump"'; + } + + $tenant = Tenant::create(); + + Artisan::call($artisanCommand); + + // 'example' is a table included in the tests/Etc/tenant-schema dump + // 'users' is a table created by the migrations + // The tables weren't created in the central database + expect(Schema::hasTable('example'))->toBeFalse(); + expect(Schema::hasTable('users'))->toBeFalse(); + + tenancy()->initialize($tenant); + + // Both the table from the schema dump and the table from actual migrations + // Were created in the tenant database + expect(Schema::hasTable('example'))->toBeTrue(); + expect(Schema::hasTable('users'))->toBeTrue(); +})->with([true, false]) + ->skip(fn () => str(app()->version())->startsWith('10.'), 'todo@l10 drop laravel 10 support before release'); + test('dump command works', function () { $tenant = Tenant::create(); $schemaPath = 'tests/Etc/tenant-schema-test.dump'; @@ -190,26 +203,6 @@ test('dump command generates dump at the path specified in the tenancy migration expect($schemaPath)->toBeFile(); }); -test('migrate command correctly uses the schema dump located at the configured schema path by default', function () { - config(['tenancy.migration_parameters.--schema-path' => 'tests/Etc/tenant-schema.dump']); - $tenant = Tenant::create(); - - expect(Schema::hasTable('schema_users'))->toBeFalse(); - expect(Schema::hasTable('users'))->toBeFalse(); - - Artisan::call('tenants:migrate'); - - expect(Schema::hasTable('schema_users'))->toBeFalse(); - expect(Schema::hasTable('users'))->toBeFalse(); - - tenancy()->initialize($tenant); - - // schema_users is a table included in the tests/Etc/tenant-schema dump - // Check for both tables to see if missing migrations also get executed - expect(Schema::hasTable('schema_users'))->toBeTrue(); - expect(Schema::hasTable('users'))->toBeTrue(); -}); - test('rollback command works', function () { $tenant = Tenant::create(); Artisan::call('tenants:migrate'); diff --git a/tests/Etc/tenant-schema.dump b/tests/Etc/tenant-schema.dump index 6af9f019..892a11f1 100644 --- a/tests/Etc/tenant-schema.dump +++ b/tests/Etc/tenant-schema.dump @@ -1,66 +1,17 @@ -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -DROP TABLE IF EXISTS `failed_jobs`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `failed_jobs` ( - `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - `connection` text COLLATE utf8mb4_unicode_ci NOT NULL, - `queue` text COLLATE utf8mb4_unicode_ci NOT NULL, - `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL, - `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL, - `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; DROP TABLE IF EXISTS `migrations`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; + CREATE TABLE `migrations` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `batch` int(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `password_resets`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `password_resets` ( - `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - `created_at` timestamp NULL DEFAULT NULL, - KEY `password_resets_email_index` (`email`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -DROP TABLE IF EXISTS `users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `schema_users` ( +); + +DROP TABLE IF EXISTS `example`; + +CREATE TABLE `example` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - `email_verified_at` timestamp NULL DEFAULT NULL, - `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `users_email_unique` (`email`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - -INSERT INTO `migrations` VALUES (2,'2014_10_12_100000_testbench_create_password_resets_table',1); -INSERT INTO `migrations` VALUES (3,'2019_08_19_000000_testbench_create_failed_jobs_table',1); + PRIMARY KEY (`id`) +); diff --git a/tests/PrefixCacheBootstrapperTest.php b/tests/PrefixCacheBootstrapperTest.php index 68a9d33c..f44a939b 100644 --- a/tests/PrefixCacheBootstrapperTest.php +++ b/tests/PrefixCacheBootstrapperTest.php @@ -40,7 +40,7 @@ test('correct cache prefix is used in all contexts', function () { $bootstrapper = app(PrefixCacheTenancyBootstrapper::class); $expectCachePrefixToBe = function (string $prefix) { - expect($prefix . ':') // RedisStore suffixes prefix with ':' + expect($prefix) ->toBe(app('cache')->getPrefix()) ->toBe(app('cache.store')->getPrefix()) ->toBe(cache()->getPrefix()) @@ -72,9 +72,9 @@ test('correct cache prefix is used in all contexts', function () { config(['cache.prefix' => null]); // stop prefixing cache keys in central so we can provide prefix manually app('cache')->forgetDriver(config('cache.default')); - expect(cache($tenantOnePrefix . ':key'))->toBe('tenantone-value'); - expect(cache($tenantTwoPrefix . ':key'))->toBe('tenanttwo-value'); -}); + expect(cache($tenantOnePrefix . 'key'))->toBe('tenantone-value'); + expect(cache($tenantTwoPrefix . 'key'))->toBe('tenanttwo-value'); +})->skip(fn () => str(app()->version())->startsWith('10.'), 'todo@l10 drop laravel 10 support before release'); test('cache is persisted when reidentification is used', function () { $tenant1 = Tenant::create(); @@ -127,7 +127,7 @@ test('central cache is persisted', function () { expect(cache()->get('key'))->toBe('central'); expect(cache()->get('key2'))->toBe('central-two'); - + tenancy()->initialize($tenant1); expect(cache()->get('key'))->toBe('tenant'); expect(cache()->get('key2'))->toBeNull(); @@ -143,12 +143,12 @@ test('cache base prefix is customizable', function () { tenancy()->initialize($tenant1); - expect($originalPrefix . $prefixBase . $tenant1->getTenantKey() . ':') + expect($originalPrefix . $prefixBase . $tenant1->getTenantKey()) ->toBe(cache()->getPrefix()) ->toBe(cache()->store('redis2')->getPrefix()) // Non-default store gets prefixed correctly too ->toBe(app('cache')->getPrefix()) ->toBe(app('cache.store')->getPrefix()); -}); +})->skip(fn () => str(app()->version())->startsWith('10.'), 'todo@l10 drop laravel 10 support before release'); test('cache is prefixed correctly when using a repository injected in a singleton', function () { $this->app->singleton(CacheService::class); @@ -275,12 +275,12 @@ test('non default stores get prefixed too when specified in tenantCacheStores', tenancy()->initialize($tenant); // We didn't add a prefix generator for our 'redis2' driver, so we expect the prefix to be generated using the 'default' generator - expect($bootstrapper->generatePrefix($tenant) . ':') + expect($bootstrapper->generatePrefix($tenant)) ->toBe(cache()->getPrefix()) ->toBe(cache()->store('redis2')->getPrefix()); // Non-default store tenancy()->end(); -}); +})->skip(fn () => str(app()->version())->startsWith('10.'), 'todo@l10 drop laravel 10 support before release'); test('cache store prefix generation can be customized', function() { // Use custom prefix generator @@ -295,14 +295,14 @@ test('cache store prefix generation can be customized', function() { tenancy()->initialize($tenant = Tenant::create()); // Expect the 'redis' store to use the prefix generated by the custom generator - expect($customPrefixGenerator($tenant) . ':') + expect($customPrefixGenerator($tenant)) ->toBe(cache()->getPrefix()) ->toBe(cache()->store('redis2')->getPrefix()) // Non-default cache stores specified in $tenantCacheStores are prefixed too ->toBe(app('cache')->getPrefix()) ->toBe(app('cache.store')->getPrefix()); tenancy()->end(); -}); +})->skip(fn () => str(app()->version())->startsWith('10.'), 'todo@l10 drop laravel 10 support before release'); test('stores get prefixed using the default way if no prefix generator is specified', function() { $originalPrefix = config('cache.prefix'); @@ -315,10 +315,10 @@ test('stores get prefixed using the default way if no prefix generator is specif tenancy()->initialize($tenant); // All stores use the default way of generating the prefix when the prefix generator isn't specified - expect($defaultPrefix . ':') - ->toBe(app(PrefixCacheTenancyBootstrapper::class)->generatePrefix($tenant) . ':') + expect($defaultPrefix) + ->toBe(app(PrefixCacheTenancyBootstrapper::class)->generatePrefix($tenant)) ->toBe(cache()->getPrefix()) // Get prefix of the default store ('redis') ->toBe(cache()->store('redis2')->getPrefix()); tenancy()->end(); -}); +})->skip(fn () => str(app()->version())->startsWith('10.'), 'todo@l10 drop laravel 10 support before release'); diff --git a/tests/TestCase.php b/tests/TestCase.php index f33a2803..05fc1f3f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -44,7 +44,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase '--realpath' => true, ]); - // Laravel 6.x support + // Laravel 6.x support todo clean up $testResponse = class_exists('Illuminate\Testing\TestResponse') ? 'Illuminate\Testing\TestResponse' : 'Illuminate\Foundation\Testing\TestResponse'; $testResponse::macro('assertContent', function ($content) { $assertClass = class_exists('Illuminate\Testing\Assert') ? 'Illuminate\Testing\Assert' : 'Illuminate\Foundation\Testing\Assert'; @@ -68,9 +68,11 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase $app['config']->set([ 'database.default' => 'central', + 'cache.default' => 'redis', 'database.redis.cache.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'), 'database.redis.default.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'), 'database.redis.options.prefix' => 'foo', + 'database.redis.client' => 'predis', 'database.connections.central' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), @@ -91,11 +93,14 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase ]) : [], ], 'database.connections.sqlite.database' => ':memory:', + 'database.connections.mysql.charset' => 'utf8mb4', + 'database.connections.mysql.collation' => 'utf8mb4_unicode_ci', 'database.connections.mysql.host' => env('TENANCY_TEST_MYSQL_HOST', '127.0.0.1'), 'database.connections.sqlsrv.username' => env('TENANCY_TEST_SQLSRV_USERNAME', 'sa'), 'database.connections.sqlsrv.password' => env('TENANCY_TEST_SQLSRV_PASSWORD', 'P@ssword'), 'database.connections.sqlsrv.host' => env('TENANCY_TEST_SQLSRV_HOST', '127.0.0.1'), 'database.connections.sqlsrv.database' => null, + 'database.connections.sqlsrv.trust_server_certificate' => true, 'database.connections.pgsql.host' => env('TENANCY_TEST_PGSQL_HOST', '127.0.0.1'), 'tenancy.filesystem.disks' => [ 'local',