From 8d3ffa5cb6f0c317a68f5e18652533cc6be0d5c2 Mon Sep 17 00:00:00 2001 From: Nick J Date: Tue, 7 Nov 2023 10:33:34 +1000 Subject: [PATCH] Allow config and temp directories to be configured independently. --- Dockerfile | 29 ++++++++ Makefile | 5 ++ camel/Camel.php | 9 ++- docker-compose.yml | 11 +++ phpunit.xml | 1 + src/Commands/GenerateDocumentation.php | 30 +++++--- src/Configuration/CacheConfiguration.php | 46 ++++++++++++ src/Extracting/ApiDetails.php | 5 +- .../GroupedEndpointsFactory.php | 12 ++- .../GroupedEndpointsFromApp.php | 10 ++- .../GroupedEndpointsFromCamelDir.php | 5 +- src/Writing/Writer.php | 25 ++++--- tests/GenerateDocumentation/OutputTest.php | 73 +++++++++++++++---- tests/Unit/CacheConfigurationTest.php | 37 ++++++++++ 14 files changed, 244 insertions(+), 54 deletions(-) create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 docker-compose.yml create mode 100644 src/Configuration/CacheConfiguration.php create mode 100644 tests/Unit/CacheConfigurationTest.php diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b469041f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# docker-compose up -d && docker logs -f scribe_app_1 +FROM ubuntu:jammy +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Etc/UTC + +# APT +RUN apt-get -qq update && apt-get install -qq \ + make \ + curl \ + php \ + php-curl \ + php-xml \ + php-sqlite3 \ + php-bcmath \ + php-curl \ + php-gd \ + php-imagick \ + php-intl \ + php-mbstring \ + php-pdo \ + php-zip \ + php-soap \ + php-pcov \ + git \ + p7zip-full + +# Composer Install +RUN curl -sS https://getcomposer.org/installer | php && \ + mv composer.phar /usr/local/bin/composer diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..7c1b2d5f --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +install: + composer install + +test: install + composer test-ci diff --git a/camel/Camel.php b/camel/Camel.php index 28d03d30..46e00c46 100644 --- a/camel/Camel.php +++ b/camel/Camel.php @@ -6,20 +6,21 @@ use Illuminate\Support\Arr; use Illuminate\Support\Str; use Knuckles\Camel\Output\OutputEndpointData; +use Knuckles\Scribe\Configuration\CacheConfiguration; use Knuckles\Scribe\Tools\Utils; use Symfony\Component\Yaml\Yaml; class Camel { - public static function cacheDir(string $docsName = 'scribe'): string + public static function cacheDir(CacheConfiguration $docsObject): string { - return ".$docsName/endpoints.cache"; + return $docsObject . "/endpoints.cache"; } - public static function camelDir(string $docsName = 'scribe'): string + public static function camelDir(CacheConfiguration $docsObject): string { - return ".$docsName/endpoints"; + return $docsObject . "/endpoints"; } /** diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..ca66d286 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +# Allow devs to run the unit tests in a dockerized environment +# clear && docker-compose up -d && docker logs -f scribe_app_1 +version: '3.7' +services: + app: + build: + context: ./ + volumes: + - ./:/testing + working_dir: /testing + command: make test diff --git a/phpunit.xml b/phpunit.xml index 813fd914..a25bbb4e 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -41,6 +41,7 @@ tests/Unit/ExtractorTest.php tests/Unit/ExtractorPluginSystemTest.php tests/Unit/ConfigDifferTest.php + tests/Unit/CacheConfigurationTest.php tests/Unit/ExtractedEndpointDataTest.php diff --git a/src/Commands/GenerateDocumentation.php b/src/Commands/GenerateDocumentation.php index c899377a..76440a57 100644 --- a/src/Commands/GenerateDocumentation.php +++ b/src/Commands/GenerateDocumentation.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\URL; use Illuminate\Support\Str; use Knuckles\Camel\Camel; +use Knuckles\Scribe\Configuration\CacheConfiguration; use Knuckles\Scribe\GroupedEndpoints\GroupedEndpointsFactory; use Knuckles\Scribe\Matching\RouteMatcherInterface; use Knuckles\Scribe\Tools\ConsoleOutputUtils as c; @@ -24,6 +25,7 @@ class GenerateDocumentation extends Command {--no-extraction : Skip extraction of route and API info and just transform the YAML and Markdown files into HTML} {--no-upgrade-check : Skip checking for config file upgrades. Won't make things faster, but can be helpful if the command is buggy} {--config=scribe : choose which config file to use} + {--cache-directory= : choose which cache directory to use} "; protected $description = 'Generate API documentation from your Laravel/Dingo routes.'; @@ -34,7 +36,8 @@ class GenerateDocumentation extends Command protected bool $forcing; - protected string $configName; + /** @var CacheConfiguration */ + protected CacheConfiguration $configObject; public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFactory $groupedEndpointsFactory): void { @@ -47,9 +50,9 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact } // Extraction stage - extract endpoint info either from app or existing Camel files (previously extracted data) - $groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher, $this->configName); + $groupedEndpointsInstance = $groupedEndpointsFactory->make($this, $routeMatcher, $this->configObject); $extractedEndpoints = $groupedEndpointsInstance->get(); - $userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($this->configName)); + $userDefinedEndpoints = Camel::loadUserDefinedEndpoints(Camel::camelDir($this->configObject)); $groupedEndpoints = $this->mergeUserDefinedEndpoints($extractedEndpoints, $userDefinedEndpoints); // Output stage @@ -61,7 +64,7 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact $this->writeExampleCustomEndpoint(); } - $writer = new Writer($this->docConfig, $this->configName); + $writer = new Writer($this->docConfig, $this->configObject); $writer->writeDocs($groupedEndpoints); $this->upgradeConfigFileIfNeeded(); @@ -98,12 +101,17 @@ public function bootstrap(): void c::bootstrapOutput($this->output); - $this->configName = $this->option('config'); - if (!config($this->configName)) { - throw new \InvalidArgumentException("The specified config (config/{$this->configName}.php) doesn't exist."); + $configName = $this->option('config'); + if (!config($configName)) { + throw new \InvalidArgumentException("The specified config (config/{$configName}.php) doesn't exist."); } - $this->docConfig = new DocumentationConfig(config($this->configName)); + $this->configObject = new CacheConfiguration($configName, $configName, true); + if ($this->hasOption('cache-directory') && !empty($this->option('cache-directory'))) { + $this->configObject = new CacheConfiguration($this->option('cache-directory'), $configName, false); + } + + $this->docConfig = new DocumentationConfig(config($this->configObject->getScribeConfigFile())); // Force root URL so it works in Postman collection $baseUrl = $this->docConfig->get('base_url') ?? config('app.url'); @@ -146,7 +154,7 @@ protected function mergeUserDefinedEndpoints(array $groupedEndpoints, array $use protected function writeExampleCustomEndpoint(): void { // We add an example to guide users in case they need to add a custom endpoint. - copy(__DIR__ . '/../../resources/example_custom_endpoint.yaml', Camel::camelDir($this->configName) . '/custom.0.yaml'); + copy(__DIR__ . '/../../resources/example_custom_endpoint.yaml', Camel::camelDir($this->configObject) . '/custom.0.yaml'); } protected function upgradeConfigFileIfNeeded(): void @@ -155,12 +163,12 @@ protected function upgradeConfigFileIfNeeded(): void $this->info("Checking for any pending upgrades to your config file..."); try { - if (! $this->laravel['files']->exists($this->laravel->configPath("{$this->configName}.php"))) { + if (! $this->laravel['files']->exists($this->laravel->configPath($this->configObject->getScribeConfigFile() . ".php"))) { $this->info("No config file to upgrade."); return; } - $upgrader = Upgrader::ofConfigFile("config/{$this->configName}.php", __DIR__ . '/../../config/scribe.php') + $upgrader = Upgrader::ofConfigFile("config/" . $this->configObject->getScribeConfigFile() . ".php", __DIR__ . '/../../config/scribe.php') ->dontTouch( 'routes', 'example_languages', 'database_connections_to_transact', 'strategies', 'laravel.middleware', 'postman.overrides', 'openapi.overrides', 'groups', 'examples.models_source' diff --git a/src/Configuration/CacheConfiguration.php b/src/Configuration/CacheConfiguration.php new file mode 100644 index 00000000..b19ccf8d --- /dev/null +++ b/src/Configuration/CacheConfiguration.php @@ -0,0 +1,46 @@ +cacheDir = $cacheDir; + $this->scribeConfig = $scribeConfig; + $this->isHidden = $isHidden; + } + + /** + * @return string + */ + public function getScribeConfigFile(): string + { + return $this->scribeConfig; + } + + /** + * @return string + */ + public function __toString(): string + { + return ($this->isHidden ? '.' : '') . $this->cacheDir; + } +} diff --git a/src/Extracting/ApiDetails.php b/src/Extracting/ApiDetails.php index febe4b9c..d4285e75 100644 --- a/src/Extracting/ApiDetails.php +++ b/src/Extracting/ApiDetails.php @@ -2,6 +2,7 @@ namespace Knuckles\Scribe\Extracting; +use Knuckles\Scribe\Configuration\CacheConfiguration; use Knuckles\Scribe\Tools\ConsoleOutputUtils as c; use Knuckles\Scribe\Tools\Utils as u; use Knuckles\Scribe\Tools\DocumentationConfig; @@ -23,9 +24,9 @@ class ApiDetails private array $lastKnownFileContentHashes = []; - public function __construct(DocumentationConfig $config = null, bool $preserveUserChanges = true, string $docsName = 'scribe') + public function __construct(DocumentationConfig $config = null, bool $preserveUserChanges = true, CacheConfiguration $docsName) { - $this->markdownOutputPath = ".{$docsName}"; //.scribe by default + $this->markdownOutputPath = $docsName; //.scribe by default // If no config is injected, pull from global. Makes testing easier. $this->config = $config ?: new DocumentationConfig(config($docsName)); $this->baseUrl = $this->config->get('base_url') ?? config('app.url'); diff --git a/src/GroupedEndpoints/GroupedEndpointsFactory.php b/src/GroupedEndpoints/GroupedEndpointsFactory.php index 022454ef..c2c4de88 100644 --- a/src/GroupedEndpoints/GroupedEndpointsFactory.php +++ b/src/GroupedEndpoints/GroupedEndpointsFactory.php @@ -3,12 +3,16 @@ namespace Knuckles\Scribe\GroupedEndpoints; use Knuckles\Scribe\Commands\GenerateDocumentation; +use Knuckles\Scribe\Configuration\CacheConfiguration; use Knuckles\Scribe\Matching\RouteMatcherInterface; class GroupedEndpointsFactory { - public function make(GenerateDocumentation $command, RouteMatcherInterface $routeMatcher, string $docsName = 'scribe'): GroupedEndpointsContract - { + public function make( + GenerateDocumentation $command, + RouteMatcherInterface $routeMatcher, + CacheConfiguration $docsName + ): GroupedEndpointsContract { if ($command->isForcing()) { return static::fromApp($command, $routeMatcher, false, $docsName); } @@ -24,12 +28,12 @@ public static function fromApp( GenerateDocumentation $command, RouteMatcherInterface $routeMatcher, bool $preserveUserChanges, - string $docsName = 'scribe' + CacheConfiguration $docsName ): GroupedEndpointsFromApp { return new GroupedEndpointsFromApp($command, $routeMatcher, $preserveUserChanges, $docsName); } - public static function fromCamelDir(string $docsName = 'scribe'): GroupedEndpointsFromCamelDir + public static function fromCamelDir(CacheConfiguration $docsName): GroupedEndpointsFromCamelDir { return new GroupedEndpointsFromCamelDir($docsName); } diff --git a/src/GroupedEndpoints/GroupedEndpointsFromApp.php b/src/GroupedEndpoints/GroupedEndpointsFromApp.php index 18209ffb..987f55ab 100644 --- a/src/GroupedEndpoints/GroupedEndpointsFromApp.php +++ b/src/GroupedEndpoints/GroupedEndpointsFromApp.php @@ -10,6 +10,7 @@ use Knuckles\Camel\Extraction\ExtractedEndpointData; use Knuckles\Camel\Output\OutputEndpointData; use Knuckles\Scribe\Commands\GenerateDocumentation; +use Knuckles\Scribe\Configuration\CacheConfiguration; use Knuckles\Scribe\Exceptions\CouldntGetRouteDetails; use Knuckles\Scribe\Extracting\ApiDetails; use Knuckles\Scribe\Extracting\Extractor; @@ -34,10 +35,11 @@ class GroupedEndpointsFromApp implements GroupedEndpointsContract public static string $cacheDir; public function __construct( - private GenerateDocumentation $command, private RouteMatcherInterface $routeMatcher, - private bool $preserveUserChanges = true, protected string $docsName = 'scribe' - ) - { + private GenerateDocumentation $command, + private RouteMatcherInterface $routeMatcher, + private bool $preserveUserChanges = true, + protected CacheConfiguration $docsName + ) { $this->docConfig = $command->getDocConfig(); static::$camelDir = Camel::camelDir($this->docsName); diff --git a/src/GroupedEndpoints/GroupedEndpointsFromCamelDir.php b/src/GroupedEndpoints/GroupedEndpointsFromCamelDir.php index ad80a69f..b78450d5 100644 --- a/src/GroupedEndpoints/GroupedEndpointsFromCamelDir.php +++ b/src/GroupedEndpoints/GroupedEndpointsFromCamelDir.php @@ -3,12 +3,13 @@ namespace Knuckles\Scribe\GroupedEndpoints; use Knuckles\Camel\Camel; +use Knuckles\Scribe\Configuration\CacheConfiguration; class GroupedEndpointsFromCamelDir implements GroupedEndpointsContract { - protected string $docsName; + protected CacheConfiguration $docsName; - public function __construct(string $docsName = 'scribe') + public function __construct(CacheConfiguration $docsName) { $this->docsName = $docsName; } diff --git a/src/Writing/Writer.php b/src/Writing/Writer.php index c3a03539..f24a594c 100644 --- a/src/Writing/Writer.php +++ b/src/Writing/Writer.php @@ -3,6 +3,7 @@ namespace Knuckles\Scribe\Writing; use Illuminate\Support\Facades\Storage; +use Knuckles\Scribe\Configuration\CacheConfiguration; use Knuckles\Scribe\Tools\ConsoleOutputUtils as c; use Knuckles\Scribe\Tools\DocumentationConfig; use Knuckles\Scribe\Tools\Globals; @@ -15,7 +16,7 @@ class Writer * The "name" of this docs instance. By default, it is "scribe". * Used for multi-docs. */ - public string $docsName; + public CacheConfiguration $docsName; private DocumentationConfig $config; @@ -40,21 +41,21 @@ class Writer private string $laravelAssetsPath; - public function __construct(DocumentationConfig $config = null, $docsName = 'scribe') + public function __construct(DocumentationConfig $config = null, CacheConfiguration $docsName) { $this->docsName = $docsName; // If no config is injected, pull from global, for easier testing. - $this->config = $config ?: new DocumentationConfig(config($docsName)); + $this->config = $config ?: new DocumentationConfig(config($docsName->getScribeConfigFile())); $this->isStatic = $this->config->get('type') === 'static'; - $this->markdownOutputPath = ".{$docsName}"; //.scribe by default + $this->markdownOutputPath = $docsName; //.scribe by default $this->laravelTypeOutputPath = $this->getLaravelTypeOutputPath(); $this->staticTypeOutputPath = rtrim($this->config->get('static.output_path', 'public/docs'), '/'); $this->laravelAssetsPath = $this->config->get('laravel.assets_directory') ? '/' . $this->config->get('laravel.assets_directory') - : "/vendor/$this->docsName"; + : "/vendor/" . $this->docsName->getScribeConfigFile(); } /** @@ -86,8 +87,8 @@ protected function writePostmanCollection(array $groups): void $collectionPath = "{$this->staticTypeOutputPath}/collection.json"; file_put_contents($collectionPath, $collection); } else { - Storage::disk('local')->put("{$this->docsName}/collection.json", $collection); - $collectionPath = Storage::disk('local')->path("$this->docsName/collection.json"); + Storage::disk('local')->put($this->docsName->getScribeConfigFile() . "/collection.json", $collection); + $collectionPath = Storage::disk('local')->path($this->docsName->getScribeConfigFile() . "/collection.json"); } c::success("Wrote Postman collection to: {$this->makePathFriendly($collectionPath)}"); @@ -105,8 +106,8 @@ protected function writeOpenAPISpec(array $parsedRoutes): void $specPath = "{$this->staticTypeOutputPath}/openapi.yaml"; file_put_contents($specPath, $spec); } else { - Storage::disk('local')->put("{$this->docsName}/openapi.yaml", $spec); - $specPath = Storage::disk('local')->path("$this->docsName/openapi.yaml"); + Storage::disk('local')->put($this->docsName->getScribeConfigFile() . "/openapi.yaml", $spec); + $specPath = Storage::disk('local')->path($this->docsName->getScribeConfigFile() . "/openapi.yaml"); } c::success("Wrote OpenAPI specification to: {$this->makePathFriendly($specPath)}"); @@ -180,8 +181,8 @@ protected function performFinalTasksForLaravelType(): void // Rewrite asset links to go through Laravel $contents = preg_replace('#href="\.\./docs/css/(.+?)"#', 'href="{{ asset("' . $this->laravelAssetsPath . '/css/$1") }}"', $contents); $contents = preg_replace('#src="\.\./docs/(js|images)/(.+?)"#', 'src="{{ asset("' . $this->laravelAssetsPath . '/$1/$2") }}"', $contents); - $contents = str_replace('href="../docs/collection.json"', 'href="{{ route("' . $this->docsName . '.postman") }}"', $contents); - $contents = str_replace('href="../docs/openapi.yaml"', 'href="{{ route("' . $this->docsName . '.openapi") }}"', $contents); + $contents = str_replace('href="../docs/collection.json"', 'href="{{ route("' . $this->docsName->getScribeConfigFile() . '.postman") }}"', $contents); + $contents = str_replace('href="../docs/openapi.yaml"', 'href="{{ route("' . $this->docsName->getScribeConfigFile() . '.openapi") }}"', $contents); file_put_contents("$this->laravelTypeOutputPath/index.blade.php", $contents); } @@ -228,7 +229,7 @@ protected function getLaravelTypeOutputPath(): ?string { if ($this->isStatic) return null; - return config('view.paths.0', function_exists('base_path') ? base_path("resources/views") : "resources/views") . "/$this->docsName"; + return config('view.paths.0', function_exists('base_path') ? base_path("resources/views") : "resources/views") . "/" . $this->docsName->getScribeConfigFile(); } /** diff --git a/tests/GenerateDocumentation/OutputTest.php b/tests/GenerateDocumentation/OutputTest.php index 52f4ede8..a6dc73d6 100644 --- a/tests/GenerateDocumentation/OutputTest.php +++ b/tests/GenerateDocumentation/OutputTest.php @@ -139,39 +139,82 @@ public function generates_laravel_type_output() /** @test */ public function supports_multi_docs_in_laravel_type_output() + { + $this->supports_base_test(["--config" => "scribe_admin"], '.scribe_admin'); + } + + /** @test */ + public function supports_separate_hidden_cache_directory() + { + $this->supports_base_test([ + "--config" => "scribe_admin", + "--cache-directory" => ".scribe_admin_dir" + ], '.scribe_admin_dir'); + } + + /** @test */ + public function supports_separate_non_hidden_cache_directory() + { + $this->supports_base_test([ + "--config" => "scribe_admin", + "--cache-directory" => "scribe_admin_dir" + ], 'scribe_admin_dir'); + } + + /** @test */ + public function supports_unrelated_temp_directory() + { + $this->supports_base_test([ + "--config" => "bananas_are_good", + "--cache-directory" => "5.5/Apple/26" + ], '5.5/Apple/26'); + } + + /** + * Base test for + * - supports_multi_docs_in_laravel_type_output + * - supports_separate_hidden_cache_directory + * - supports_separate_non_hidden_cache_directory + * - supports_unrelated_temp_directory + * + * @param $generate + * @param $assertDir + * @return void + */ + private function supports_base_test($generate, $assertDir) { RouteFacade::post('/api/withQueryParameters', [TestController::class, 'withQueryParameters']); - config(['scribe_admin' => config('scribe')]); + config([$generate['--config'] => config('scribe')]); $title = "The Real Admin API"; - config(['scribe_admin.title' => $title]); - config(['scribe_admin.type' => 'laravel']); - config(['scribe_admin.postman.enabled' => true]); - config(['scribe_admin.openapi.enabled' => true]); + config([$generate['--config'] . '.title' => $title]); + config([$generate['--config'] . '.type' => 'laravel']); + config([$generate['--config'] . '.postman.enabled' => true]); + config([$generate['--config'] . '.openapi.enabled' => true]); - $output = $this->generate(["--config" => "scribe_admin"]); + $output = $this->generate($generate); $this->assertStringContainsString( - "Wrote Blade docs to: vendor/orchestra/testbench-core/laravel/resources/views/scribe_admin", $output + "Wrote Blade docs to: vendor/orchestra/testbench-core/laravel/resources/views/" . $generate['--config'], $output ); $this->assertStringContainsString( - "Wrote Laravel assets to: vendor/orchestra/testbench-core/laravel/public/vendor/scribe_admin", $output + "Wrote Laravel assets to: vendor/orchestra/testbench-core/laravel/public/vendor/" . $generate['--config'], $output ); $this->assertStringContainsString( - "Wrote Postman collection to: vendor/orchestra/testbench-core/laravel/storage/app/scribe_admin/collection.json", $output + "Wrote Postman collection to: vendor/orchestra/testbench-core/laravel/storage/app/" . $generate['--config'] . "/collection.json", $output ); $this->assertStringContainsString( - "Wrote OpenAPI specification to: vendor/orchestra/testbench-core/laravel/storage/app/scribe_admin/openapi.yaml", $output + "Wrote OpenAPI specification to: vendor/orchestra/testbench-core/laravel/storage/app/" . $generate['--config'] . "/openapi.yaml", $output ); $paths = collect([ - Storage::disk('local')->path('scribe_admin/collection.json'), - Storage::disk('local')->path('scribe_admin/openapi.yaml'), - View::getFinder()->find('scribe_admin/index'), + Storage::disk('local')->path( $generate['--config'] . '/collection.json'), + Storage::disk('local')->path($generate['--config'] . '/openapi.yaml'), + View::getFinder()->find($generate['--config'] . '/index'), ]); $paths->each(fn($path) => $this->assertFileContainsString($path, $title)); $paths->each(fn($path) => unlink($path)); - $this->assertDirectoryExists(".scribe_admin"); - Utils::deleteDirectoryAndContents(".scribe_admin"); + $this->assertDirectoryExists($assertDir); + Utils::deleteDirectoryAndContents($assertDir); } /** @test */ diff --git a/tests/Unit/CacheConfigurationTest.php b/tests/Unit/CacheConfigurationTest.php new file mode 100644 index 00000000..e5422cd3 --- /dev/null +++ b/tests/Unit/CacheConfigurationTest.php @@ -0,0 +1,37 @@ +assertEquals('.scribe', $cacheConfiguration); + } + + /** @test */ + public function object_coerses_into_non_hidden_string() + { + $cacheConfiguration = new CacheConfiguration('scribe', 'scribe', false); + $this->assertEquals('scribe', $cacheConfiguration); + } + + /** @test */ + public function object_coerses_into_hidden_string_for_subdirs() + { + $cacheConfiguration = new CacheConfiguration('scribe/bob', 'scribe', true); + $this->assertEquals('.scribe/bob', $cacheConfiguration); + } + + /** @test */ + public function object_coerses_into_non_hidden_string_for_subdirs() + { + $cacheConfiguration = new CacheConfiguration('scribe/bob/dave', 'scribe', false); + $this->assertEquals('scribe/bob/dave', $cacheConfiguration); + } +}