Laragear/Meta

View on GitHub
src/Testing/InteractsWithServiceProvider.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Laragear\Meta\Testing;

use DateTimeInterface;
use Illuminate\Auth\AuthManager;
use Illuminate\Console\Scheduling\Event;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Contracts\Auth\Access\Gate;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use InvalidArgumentException;
use function now;
use function preg_replace;
use ReflectionException;
use ReflectionMethod;

/**
 * @internal
 */
trait InteractsWithServiceProvider
{
    /**
     * Assert that a service manager contains a given driver.
     *
     * @param  string  $service
     * @param  string  $driver
     * @param  class-string|string|null  $class
     * @return void
     */
    protected function assertHasDriver(string $service, string $driver, string $class = null): void
    {
        $manager = $this->app->make($service);

        try {
            $instance = $manager instanceof AuthManager ? $manager->guard($driver) : $manager->driver($driver);
        } catch (InvalidArgumentException) {
            static::fail("The '$service' service doesn't have the driver '$driver'.");
        }

        static::assertNotNull($instance, "The '$driver' for '$service' returns null.");

        if ($class) {
            static::assertInstanceOf($class, $instance, "The the driver '$driver' is not an instance of '$class'.");
        }
    }

    /**
     * Assert the services are registered in the Service Container.
     *
     * @param  string  ...$services
     * @return void
     */
    protected function assertHasServices(string ...$services): void
    {
        foreach ($services as $service) {
            static::assertThat(
                $this->app->bound($service),
                static::isTrue(),
                "The '$service' was not registered in the Service Container."
            );
        }
    }

    /**
     * Assert a service is registered as a shared instance.
     *
     * @param  string  ...$services
     * @return void
     */
    protected function assertHasSingletons(string ...$services): void
    {
        $this->assertHasServices(...$services);

        foreach ($services as $service) {
            static::assertThat(
                $this->app->isShared($service),
                static::isTrue(),
                "The '$service' is registered as a shared instance in the Service Container."
            );
        }
    }

    /**
     * Assert a service is registered as a shared instance.
     *
     * @param  string  ...$services
     * @return void
     */
    protected function assertHasShared(string ...$services): void
    {
        $this->assertHasSingletons(...$services);
    }

    /**
     * Assert that the config file is merged into the application using the given key.
     *
     * @param  string  $file
     * @param  string|null  $configKey
     * @return void
     */
    protected function assertConfigMerged(string $file, string $configKey = null): void
    {
        $configKey ??= Str::of($file)->beforeLast('.php')->afterLast('/')->afterLast('\\')->toString();

        static::assertThat(
            $this->app->make('config')->has($configKey),
            static::isTrue(),
            "The configuration file was not merged as '$configKey'."
        );

        static::assertSame(
            $this->app->make('files')->getRequire($file),
            $this->app->make('config')->get($configKey),
            "The configuration file in '$file' is not the same for '$configKey'."
        );
    }

    /**
     * Asserts that the given files are set to be published.
     *
     * @param  string  $file
     * @param  string  $tag
     * @return void
     */
    protected function assertPublishes(string $file, string $tag): void
    {
        static::assertArrayHasKey($tag, ServiceProvider::$publishGroups, "The '$tag' is not a publishable tag.");

        static::assertContains(
            $file, ServiceProvider::$publishGroups[$tag], "The '$file' is not publishable in the '$tag' tag."
        );
    }

    /**
     * Assert that the migration files in the given path are published.
     *
     * @param  string  $dir
     * @param  string  $tag
     * @return void
     */
    protected function assertPublishesMigrations(string $dir, string $tag = 'migrations'): void
    {
        static::assertArrayHasKey($tag, ServiceProvider::$publishGroups, "The '$tag' is not a publishable tag.");

        $files = $this->app->make('files')->files($dir);

        static::assertNotEmpty($files, "The '$dir' has no migration files.");

        $this->travelTo(now()->startOfSecond(), function () use ($tag, $files): void {
            $prefix = now()->format('Y_m_d_His');

            foreach ($files as $file) {
                $filename = $prefix.'_'.preg_replace('/^[\d|_]+/', '', $file->getFilename());

                static::assertContains(
                    $this->app->databasePath("migrations/$filename"),
                    ServiceProvider::$publishGroups[$tag],
                    "The '$filename' is not publishable in the '$tag' tag."
                );
            }
        });
    }

    /**
     * Assert the translation namespace is registered.
     *
     * @param  string  $path
     * @param  string  $namespace
     * @return void
     */
    protected function assertHasTranslations(string $path, string $namespace): void
    {
        $namespaces = $this->app->make('translator')->getLoader()->namespaces();

        static::assertArrayHasKey($namespace, $namespaces, "The '$namespace' translations were not registered.");
        static::assertSame($path, $namespaces[$namespace], "The '$namespace' does not correspond to the path '$path'.");
    }

    /**
     * Assert the view namespace is registered.
     *
     * @param  string  $path
     * @param  string  $namespace
     * @return void
     */
    protected function assertHasViews(string $path, string $namespace): void
    {
        $namespaces = $this->app->make('view')->getFinder()->getHints();

        static::assertArrayHasKey($namespace, $namespaces, "The '$namespace' views were not registered.");
        static::assertContains($path, $namespaces[$namespace], "The '$namespace' does not correspond to the path '$path'.");
    }

    /**
     * Assert the blade components are registered.
     *
     * @param  string  $alias
     * @param  string  $component
     * @return void
     */
    protected function assertHasBladeComponent(string $alias, string $component): void
    {
        $aliases = $this->app->make('blade.compiler')->getClassComponentAliases();

        static::assertArrayHasKey($alias, $aliases, "The '$alias' is not registered as component.");
        static::assertSame($component, $aliases[$alias], "The '$component' component is not registered as '$alias'.");
    }

    /**
     * Assert the blade directives are registered.
     *
     * @param  string  ...$directives
     * @return void
     */
    protected function assertHasBladeDirectives(string ...$directives): void
    {
        $list = $this->app->make('blade.compiler')->getCustomDirectives();

        foreach ($directives as $directive) {
            static::assertArrayHasKey($directive, $list, "The '$directive' was not registered as a blade directive.");
        }
    }

    /**
     * Assert the validation rules are registered.
     *
     * @param  string  ...$rules
     * @return void
     */
    protected function assertHasValidationRules(string ...$rules): void
    {
        $extensions = $this->app->make('validator')->make([], [])->extensions;

        foreach ($rules as $rule) {
            static::assertArrayHasKey($rule, $extensions, "The '$rule' rule was not registered in the validator.");
        }
    }

    /**
     * Assert the middleware are aliased.
     *
     * @param  string  $alias
     * @param  string  $middleware
     * @return void
     */
    protected function assertHasMiddlewareAlias(string $alias, string $middleware): void
    {
        $registered = $this->app->make('router')->getMiddleware();

        static::assertArrayHasKey($alias, $registered, "The '$alias' alias was not registered as middleware.");
        static::assertSame($middleware, $registered[$alias], "The '$middleware' was not aliased as '$alias' middleware.");
    }

    /**
     * Assert the middleware is registered globally.
     *
     * @param  string  ...$middleware
     * @return void
     */
    protected function assertHasGlobalMiddleware(string ...$middleware): void
    {
        $kernel = $this->app->make(Kernel::class);

        foreach ($middleware as $class) {
            static::assertThat(
                $kernel->hasMiddleware($class),
                static::isTrue(),
                "The '$class' middleware was not registered as global."
            );
        }
    }

    /**
     * Assert the middleware is registered in a middleware group.
     *
     * @param  string  $group
     * @param  string  $middleware
     * @return void
     */
    protected function assertHasMiddlewareInGroup(string $group, string $middleware): void
    {
        $list = $this->app->make(Kernel::class)->getMiddlewareGroups();

        static::assertThat(
            $list, static::arrayHasKey($group), "The middleware group '$group' is not defined by default."
        );

        static::assertThat(
            $list[$group],
            static::containsEqual($middleware),
            "The middleware '$middleware' is not part of the '$group' group."
        );
    }

    /**
     * Assert the gate has a given ability.
     *
     * @param  string  ...$abilities
     * @return void
     */
    protected function assertGateHasAbility(string ...$abilities): void
    {
        $gates = $this->app->make(Gate::class)->abilities();

        foreach ($abilities as $ability) {
            static::assertThat($gates, static::arrayHasKey($ability), "The '$ability' is not registered as a gate.");
        }
    }

    /**
     * Assert that a model has registered a Policy.
     *
     * @param  string  $model
     * @param  string  ...$abilities
     * @return void
     */
    protected function assertGateHasPolicy(string $model, string ...$abilities): void
    {
        $policy = $this->app->make(Gate::class)->getPolicyFor($model);

        static::assertNotNull($policy, "The policy for '$model' does not exist.");

        $target = get_class($policy);

        foreach ($abilities as $ability) {
            try {
                $method = new ReflectionMethod($policy, $ability);
            } catch (ReflectionException) {
                static::fail("The '$ability' ability is not declared in the '$target' policy for '$model'.");
            }

            static::assertThat(
                $method->isPublic() && ! $method->isStatic(),
                static::isTrue(),
                "The '$ability' ability declared in '$target' is private/protected or static."
            );
        }
    }

    /**
     * Asserts a task is scheduled.
     *
     * @param  string  $task
     * @return void
     */
    protected function assertHasScheduledTask(string $task): void
    {
        $contains = Collection::make($this->app->make(Schedule::class)->events())
            ->contains(static function (Event $event) use ($task): bool {
                return Str::of($event->command)->after('artisan')->contains($task)
                    || $event->description === $task;
            });

        static::assertThat($contains, static::isTrue(), "The '$task' is has not been scheduled.");
    }

    /**
     * Assert that a scheduled task will run at the given date.
     *
     * @param  string  $task
     * @param  \DateTimeInterface  $date
     * @return void
     */
    protected function assertScheduledTaskRunsAt(string $task, DateTimeInterface $date): void
    {
        $this->assertHasScheduledTask($task);

        $contains = $this->travelTo($date, function () use ($task): bool {
            return $this->app->make(Schedule::class)->dueEvents($this->app)
                ->contains(static function (Event $event) use ($task): bool {
                    return Str::of($event->command)->after('artisan')->contains($task)
                        || $event->description === $task;
                });
        });

        static::assertThat($contains, static::isTrue(), "The '$task' is not scheduled to run at '$date'.");
    }

    /**
     * Assert the given class has registered the given macros.
     *
     * @param  string|class-string  $macroable
     * @param  string  ...$macros
     * @return void
     */
    protected function assertHasMacro(string $macroable, string ...$macros): void
    {
        $call = $macroable === Builder::class ? 'hasGlobalMacro' : 'hasMacro';

        foreach ($macros as $macro) {
            static::assertThat(
                $macroable::{$call}($macro), static::isTrue(), "The macro '$macro' for '$macroable' is missing."
            );
        }
    }
}