php8.1-xml php8.1-zip php8.1-gd php8.1-bcmath php8.1-mysql php8.1-xdebug
# Install composer to a custom location
-RUN mkdir /scripts && \
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+
+# Clone down a BookStack instance for us to play with
+RUN mkdir -p /var/www/bookstack && \
+ cd /var/www/bookstack && \
+ git clone https://github.com/BookStackApp/BookStack.git --branch release --single-branch ./ && \
+ composer install --no-dev && \
+ cp .env.example .env && \
+ php artisan key:generate
+
+# Update env options
+RUN sed -i 's/^DB_HOST=.*/DB_HOST=db/' /var/www/bookstack/.env && \
+ sed -i 's/^DB_DATABASE=.*/DB_DATABASE=bookstack/' /var/www/bookstack/.env && \
+ sed -i 's/^DB_USERNAME=.*/DB_USERNAME=bookstack/' /var/www/bookstack/.env && \
+ sed -i 's/^DB_PASSWORD=.*/DB_PASSWORD=bookstack/' /var/www/bookstack/.env
CMD ["/bin/bash"]
\ No newline at end of file
services:
app:
build: .
+ working_dir: /cli
volumes:
- ./:/cli
depends_on:
php compile.php
```
+Tests can be ran via PHPUnit within the docker environment as described below. **It is not advised** to run tests outside of this docker environment since tests are written to an expected pre-configured system environment.
+
#### Docker Environment
A docker-compose setup exists to create a clean, contained environment, which is important for this project since the
CLI checks and interacts with many system-level elements.
```bash
+# Build the environment container
+docker compose build
+
# Enter the environment
-docker compose run -w /cli app
+docker compose run app
# From there you'll be dropped into a bash shell within the project directory.
# You could proceed to install dependencies via composer via:
# Then you can run tests via:
vendor/bin/phpunit
+
+# To clean-up and delete the environment:
+docker compose rm -fsv
```
+Within the environment a pre-existing BookStack instance can be found at `/var/www/bookstack` for testing.
+
### Contributing
I welcome issues and PRs but keep in mind that I'd like to keep the feature-set narrow to limit support/maintenance burden.
$app->add(new InitCommand());
$app->add(new RestoreCommand());
+
return $app;
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace Tests;
+
+use PHPUnit\Framework\Assert;
+use Symfony\Component\Console\Tester\CommandTester;
+
+class CommandResult
+{
+
+ public function __construct(
+ protected CommandTester $tester,
+ protected ?\Exception $error
+ ) { }
+
+ public function assertSuccessfulExit(): void
+ {
+ Assert::assertEquals(0, $this->tester->getStatusCode());
+ }
+
+ public function assertErrorExit(): void
+ {
+ Assert::assertTrue($this->error !== null);
+ }
+
+ public function assertStdoutContains(string $needle): void
+ {
+ Assert::assertStringContainsString($needle, $this->tester->getDisplay());
+ }
+
+ public function assertStderrContains(string $needle): void
+ {
+ Assert::assertStringContainsString($needle, $this->error->getMessage() ?? '');
+ }
+
+
+}
\ No newline at end of file
$dir = $this->getEmptyTestDir();
chdir($dir);
- $commandTester = $this->runCommand('init');
- $commandTester->assertCommandIsSuccessful();
+ $result = $this->runCommand('init');
+ $result->assertSuccessfulExit();
$this->assertFileExists($dir . '/vendor/autoload.php');
$this->assertFileExists($dir . '/.env');
$envData = file_get_contents($dir . '/.env');
$this->assertMatchesRegularExpression('/^APP_KEY=base64:.{30,60}$/m', $envData);
- $output = $commandTester->getDisplay();
- $this->assertStringContainsString("A BookStack install has been initialized at: " . $dir, $output);
+ $result->assertStdoutContains("A BookStack install has been initialized at: " . $dir);
+
+ $this->deleteDirectory($dir);
+ }
+
+ public function test_command_custom_path_validation()
+ {
+ $dir = $this->getEmptyTestDir();
+ $file = $dir . '/out.txt';
+ file_put_contents($file, 'hello');
+
+ $result = $this->runCommand('init', ['target-directory' => $file]);
+ $result->assertErrorExit();
+ $result->assertStderrContains("Was provided [{$file}] as an install path but existing file provided.");
+
+ $result = $this->runCommand('init', ['target-directory' => $dir]);
+ $result->assertErrorExit();
+ $result->assertStderrContains("Expected install directory to be empty but existing files found in [{$dir}] target location.");
+
+ $result = $this->runCommand('init', ['target-directory' => '/my/duck/says/hello']);
+ $result->assertErrorExit();
+ $result->assertStderrContains("Could not resolve provided [/my/duck/says/hello] path to an existing folder.");
$this->deleteDirectory($dir);
}
return require dirname(__DIR__) . '/src/app.php';
}
- protected function runCommand(string $command, array $args = []): CommandTester
+ protected function runCommand(string $command, array $args = []): CommandResult
{
$app = $this->getApp();
$command = $app->find($command);
+ $err = null;
$commandTester = new CommandTester($command);
- $commandTester->execute($args);
- return $commandTester;
+ try {
+ $commandTester->execute($args);
+ } catch (\Exception $exception) {
+ $err = $exception;
+ }
+
+ return new CommandResult($commandTester, $err);
}
}
\ No newline at end of file