Repository: qossmic/deptrac (php)

Issue:
deptrac.yaml file examples - and init command broken
Hello,

   First off thank you for maintaining/developing this library. I had version 0.10.2 (via deptrac-shim) installed for quite awhile and upgraded recently to 0.24.0. When I did the upgrade I couldn't get anything to work from the old file. I looked at the examples I could find and most were something along the lines of

```
deptrac:
   paths: ...
````

When I modified mine, it complained it couldn't find anything to handle deptrac (something along those lines). Took a bit of digging around, switching versions using this repository directly for me to get it figured out. I tried the `init` command but that got me an error about not being able to create the file. Once I worked around that issue I could see what the default file was which is as follows:

```
parameters:
   paths: ...
```

Once I made that change my original config file functioned as expected.

I can submit some PRs but there are quite a few examples I wasn't entirely sure how mine is wrong. So, if the examples are infact no longer accurate, I can submit a PR but thought I would ask first.

Also the init command seemed broken unless the file already exists.
For example in `Supportive/File/Dumper.php` it creates a new `SplFileInfo.php` using the file path. And then [here](https://github.com/qossmic/deptrac/blob/main/src/Supportive/File/Dumper.php#L33) you check if its writeable but the file doesn't exist so it isn't. Again if a PR would help I can submit one to test if the file already exists before the writeable test etc.



Pull request description:
Fix init with existing file.
Fixes #977
Closes #982

Patch:
diff --git a/src/Supportive/File/Dumper.php b/src/Supportive/File/Dumper.php
index 288c4996..658f965f 100644
--- a/src/Supportive/File/Dumper.php
+++ b/src/Supportive/File/Dumper.php
@@ -8,6 +8,7 @@ use Qossmic\Deptrac\Supportive\File\Exception\FileAlreadyExistsException;
 use Qossmic\Deptrac\Supportive\File\Exception\FileNotWritableException;
 use SplFileInfo;
 use Symfony\Component\Filesystem\Filesystem;
+use function is_writable;
 
 class Dumper
 {
@@ -30,7 +31,7 @@ class Dumper
         if ($filesystem->exists($target->getPathname())) {
             throw FileAlreadyExistsException::alreadyExists($target);
         }
-        if (!$target->isWritable()) {
+        if (!is_writable($target->getPath())) {
             throw FileNotWritableException::notWritable($target);
         }
 


Test patch:
diff --git a/tests/Supportive/File/DumperTest.php b/tests/Supportive/File/DumperTest.php
new file mode 100644
index 00000000..46e9281a
--- /dev/null
+++ b/tests/Supportive/File/DumperTest.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Tests\Qossmic\Deptrac\Supportive\File;
+
+use PHPUnit\Framework\TestCase;
+use Qossmic\Deptrac\Supportive\File\Dumper;
+use Qossmic\Deptrac\Supportive\File\Exception\FileAlreadyExistsException;
+use Qossmic\Deptrac\Supportive\File\Exception\FileNotWritableException;
+use SplFileInfo;
+use function file_exists;
+use function file_get_contents;
+use function is_writable;
+use function rtrim;
+use function sprintf;
+use function sys_get_temp_dir;
+use function tempnam;
+use function uniqid;
+use function unlink;
+use const DIRECTORY_SEPARATOR;
+
+final class DumperTest extends TestCase
+{
+    private string $sourceFile;
+    private Dumper $dumper;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->sourceFile = __DIR__.'/Fixtures/deptrac.yaml';
+        $this->dumper = new Dumper($this->sourceFile);
+    }
+
+    protected function tearDown(): void
+    {
+        parent::tearDown();
+
+        unset($this->dumper);
+    }
+
+    public function testFailsWhenFileAlreadyExists(): void
+    {
+        $targetFile = $this->sourceFile;
+
+        $this->expectException(FileAlreadyExistsException::class);
+
+        $this->dumper->dump($targetFile);
+    }
+
+    public function testFailsWhenFileIsNotWritable(): void
+    {
+        $tempDir = sprintf('%s/%s', rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR), uniqid());
+        if (!mkdir($tempDir) || !chmod($tempDir, 0444)) {
+            $this->markTestSkipped(sprintf('Skipping test. Could not create readonly temporary directory "%s". Please check permissions', $tempDir));
+        }
+        $tempFile = new SplFileInfo($tempDir.DIRECTORY_SEPARATOR.'deptrac.yaml');
+
+        $this->expectException(FileNotWritableException::class);
+
+        $this->dumper->dump($tempFile->getPathname());
+    }
+
+    public function testCopiesTemplateToNewFile(): void
+    {
+        $tempFilename = tempnam(sys_get_temp_dir(), 'deptrac');
+        if (false === $tempFilename) {
+            $this->markTestSkipped('Skipping test. Could not create temporary file. Please check permissions');
+        }
+        $tempFile = new SplFileInfo($tempFilename);
+        unlink($tempFile->getPathname());
+        $tempDir = $tempFile->getPath();
+        if (!is_writable($tempDir)) {
+            $this->markTestSkipped(sprintf('Skipping test. Can not write to temporary directory "%s". Please check your permissions.', $tempDir));
+        }
+        if ($tempFile->isFile()) {
+            $this->fail(sprintf('Temporary file "%s" already exists.', $tempFile->getPathname()));
+        }
+
+        $this->dumper->dump($tempFile->getPathname());
+
+        self::assertTrue(file_exists($tempFile->getPathname()));
+        self::assertSame(file_get_contents($this->sourceFile), file_get_contents($tempFile->getPathname()));
+
+        @unlink($tempFile->getPathname());
+        @rmdir($tempDir);
+    }
+}
diff --git a/tests/Supportive/File/Fixtures/deptrac.yaml b/tests/Supportive/File/Fixtures/deptrac.yaml
new file mode 100644
index 00000000..763bc43f
--- /dev/null
+++ b/tests/Supportive/File/Fixtures/deptrac.yaml
@@ -0,0 +1,24 @@
+deptrac:
+  paths:
+    - ./src
+  exclude_files:
+    - '#.*test.*#'
+  layers:
+    - name: Controller
+      collectors:
+        - type: className
+          value: .*Controller.*
+    - name: Repository
+      collectors:
+        - type: className
+          value: .*Repository.*
+    - name: Service
+      collectors:
+        - type: className
+          value: .*Service.*
+  ruleset:
+    Controller:
+      - Service
+    Service:
+      - Repository
+    Repository: