From fc4b2c3fc173d06e1bccc697ea2b1e34b4ed5ae0 Mon Sep 17 00:00:00 2001
From: Camille Simiand <camille.simiand@tetras-libre.fr>
Date: Fri, 21 Jan 2022 15:37:11 +0100
Subject: [PATCH] Add functional tests

---
 ...d26-aee1-4dc3-8dd7-83c57063e8e1project.xml |  22 ---
 tests/files_for_tests/image_for_test.jpeg     | Bin 0 -> 5591 bytes
 tests/files_for_tests/projext.xml             |  78 +++++++++
 tests/functional/ProjectControllerTest.php    | 165 +++++++++++++++---
 4 files changed, 222 insertions(+), 43 deletions(-)
 delete mode 100644 legacy/62b16d26-aee1-4dc3-8dd7-83c57063e8e1project.xml
 create mode 100644 tests/files_for_tests/image_for_test.jpeg
 create mode 100644 tests/files_for_tests/projext.xml

diff --git a/legacy/62b16d26-aee1-4dc3-8dd7-83c57063e8e1project.xml b/legacy/62b16d26-aee1-4dc3-8dd7-83c57063e8e1project.xml
deleted file mode 100644
index 5bf6037..0000000
--- a/legacy/62b16d26-aee1-4dc3-8dd7-83c57063e8e1project.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE rekall>
-<project>
-  <video url="https://youtu.be/Ssm_mm8zyS0"/>
-  <document key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d">
-    <meta ctg="Rekall-&gt;Author" cnt=""/>
-    <meta ctg="Rekall-&gt;Date/Time" cnt="2015:08:31 18:06:16"/>
-    <meta ctg="Rekall-&gt;Import Date" cnt="2015:08:31 18:06:16"/>
-    <meta ctg="Rekall User Infos-&gt;User Name" cnt=""/>
-    <meta ctg="Rekall-&gt;Location Name" cnt="48.867359621322265, 2.3618561655447228"/>
-    <meta ctg="Rekall-&gt;Location GPS" cnt="48.867359621322265, 2.3618561655447228"/>
-    <meta ctg="Rekall User Infos-&gt;User IP" cnt="127.0.0.1"/>
-    <meta ctg="Rekall-&gt;Comments" cnt=""/>
-    <meta ctg="Rekall-&gt;Keywords" cnt=""/>
-    <meta ctg="Rekall-&gt;Group" cnt=""/>
-    <meta ctg="Rekall-&gt;Visibility" cnt=""/>
-    <meta ctg="Rekall-&gt;Type" cnt="rekall/marker"/>
-    <meta ctg="Rekall-&gt;Flag" cnt="Marker"/>
-    <meta ctg="Rekall-&gt;Name" cnt="New note"/>
-  </document>
-  <tag key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d" timeStart="0" timeEnd="10" version="0"/>
-</project>
diff --git a/tests/files_for_tests/image_for_test.jpeg b/tests/files_for_tests/image_for_test.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..639529067e09340f8af5eaf6d4da7bd4b8d85679
GIT binary patch
literal 5591
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
Ciez^H

literal 0
HcmV?d00001

diff --git a/tests/files_for_tests/projext.xml b/tests/files_for_tests/projext.xml
new file mode 100644
index 0000000..115a167
--- /dev/null
+++ b/tests/files_for_tests/projext.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<!DOCTYPE rekall>
+<project>
+  <video url="https://youtu.be/dQw4w9WgXcQ"/>
+  <document key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d">
+    <meta ctg="Rekall-&gt;Author" cnt=""/>
+    <meta ctg="Rekall-&gt;Date/Time" cnt="2015:08:31 18:06:16"/>
+    <meta ctg="Rekall-&gt;Import Date" cnt="2015:08:31 18:06:16"/>
+    <meta ctg="Rekall User Infos-&gt;User Name" cnt=""/>
+    <meta ctg="Rekall-&gt;Location Name" cnt="48.867359621322265, 2.3618561655447228"/>
+    <meta ctg="Rekall-&gt;Location GPS" cnt="48.867359621322265, 2.3618561655447228"/>
+    <meta ctg="Rekall User Infos-&gt;User IP" cnt="127.0.0.1"/>
+    <meta ctg="Rekall-&gt;Comments" cnt=""/>
+    <meta ctg="Rekall-&gt;Keywords" cnt=""/>
+    <meta ctg="Rekall-&gt;Group" cnt=""/>
+    <meta ctg="Rekall-&gt;Visibility" cnt=""/>
+    <meta ctg="Rekall-&gt;Type" cnt="rekall/marker"/>
+    <meta ctg="Rekall-&gt;Flag" cnt="Marker"/>
+    <meta ctg="Rekall-&gt;Name" cnt="New note"/>
+  </document>
+  <tag key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d" timeStart="0" timeEnd="10" version="0"/>
+  <edition key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d" metadataKey="Rekall-&gt;Name" metadataValue="First note" version="0"/>
+  <edition key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d" metadataKey="Rekall-&gt;Comments" metadataValue="This is a note for tests purpose." version="0"/>
+  <edition key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d" metadataKey="Rekall-&gt;Link" metadataValue="https://youtu.be/dQw4w9WgXcQ" version="0"/>
+  <edition key="marker-1f10323d5f4a1c119e777e59c2f00dcc650e750d" metadataKey="Rekall-&gt;Author" metadataValue="Rick Astley" version="0"/>
+  <document key="marker-45f08bfa88924f466539f2ae41b509f7823929db">
+    <meta ctg="Rekall-&gt;Author" cnt=""/>
+    <meta ctg="Rekall-&gt;Date/Time" cnt="2022:01:21 14:09:09"/>
+    <meta ctg="Rekall-&gt;Import Date" cnt="2022:01:21 14:09:09"/>
+    <meta ctg="Rekall-&gt;Location Name" cnt=""/>
+    <meta ctg="Rekall-&gt;Location GPS" cnt=""/>
+    <meta ctg="Rekall User Infos-&gt;User Name" cnt=""/>
+    <meta ctg="Rekall User Infos-&gt;User IP" cnt="192.168.176.3"/>
+    <meta ctg="Rekall-&gt;Comments" cnt=""/>
+    <meta ctg="Rekall-&gt;Type" cnt="rekall/marker"/>
+    <meta ctg="Rekall-&gt;Flag" cnt="Marker"/>
+    <meta ctg="Rekall-&gt;Name" cnt="New note"/>
+    <meta ctg="key" cnt="marker-45f08bfa88924f466539f2ae41b509f7823929db"/>
+  </document>
+  <tag key="marker-45f08bfa88924f466539f2ae41b509f7823929db" timeStart="0" timeEnd="15" version="0"/>
+  <edition key="marker-45f08bfa88924f466539f2ae41b509f7823929db" metadataKey="Rekall-&gt;Name" metadataValue="Second note" version="0"/>
+  <edition key="marker-45f08bfa88924f466539f2ae41b509f7823929db" metadataKey="Rekall-&gt;Author" metadataValue="Bob" version="0"/>
+  <edition key="marker-45f08bfa88924f466539f2ae41b509f7823929db" metadataKey="Rekall-&gt;Comments" metadataValue="Empty" version="0"/>
+  <tag key="marker-45f08bfa88924f466539f2ae41b509f7823929db" timeStart="30" timeEnd="75" version="0"/>
+  <edition key="marker-45f08bfa88924f466539f2ae41b509f7823929db" metadataKey="Rekall-&gt;Link" metadataValue="http://undefined" version="0"/>
+  <tag key="marker-45f08bfa88924f466539f2ae41b509f7823929db" timeStart="30" timeEnd="75" version="0"/>
+  <edition key="marker-45f08bfa88924f466539f2ae41b509f7823929db" metadataKey="Rekall-&gt;Highlight" metadataValue="true" version="0"/>
+  <document>
+    <meta ctg="Rekall-&gt;Author" cnt=""/>
+    <meta ctg="File-&gt;File Access Date/Time" cnt="2022:01:21 15:10:38"/>
+    <meta ctg="File-&gt;File Creation Date/Time" cnt="2022:01:21 15:10:38"/>
+    <meta ctg="File-&gt;File Modification Date/Time" cnt="2022:01:21 15:10:38"/>
+    <meta ctg="Rekall-&gt;Date/Time" cnt="2022:01:21 15:10:38"/>
+    <meta ctg="Rekall-&gt;Import Date" cnt="2022:01:21 14:10:39"/>
+    <meta ctg="Rekall-&gt;Location Name" cnt=""/>
+    <meta ctg="Rekall-&gt;Location GPS" cnt=""/>
+    <meta ctg="Rekall User Infos-&gt;User Name" cnt=""/>
+    <meta ctg="Rekall User Infos-&gt;User IP" cnt="192.168.176.3"/>
+    <meta ctg="Rekall-&gt;Comments" cnt=""/>
+    <meta ctg="File-&gt;Hash" cnt="2B0F9FA5055C514A9464616416A1EA63A72FD88B"/>
+    <meta ctg="Rekall-&gt;Flag" cnt="File"/>
+    <meta ctg="File-&gt;Thumbnail" cnt=""/>
+    <meta ctg="File-&gt;Owner" cnt="www-data"/>
+    <meta ctg="File-&gt;MIME Type" cnt="image/jpeg"/>
+    <meta ctg="File-&gt;File Type" cnt="image/jpeg"/>
+    <meta ctg="Rekall-&gt;Type" cnt="image/jpeg"/>
+    <meta ctg="File-&gt;File Name" cnt="image_for_test.jpeg"/>
+    <meta ctg="File-&gt;Extension" cnt="jpeg"/>
+    <meta ctg="File-&gt;Basename" cnt="image_for_test"/>
+    <meta ctg="Rekall-&gt;Name" cnt="image_for_test"/>
+    <meta ctg="Rekall-&gt;Extension" cnt="JPEG"/>
+    <meta ctg="Rekall-&gt;Folder" cnt=""/>
+    <meta ctg="Rekall-&gt;File Size" cnt="5591"/>
+    <meta ctg="Rekall-&gt;File Size (MB)" cnt="0.0053319931030273"/>
+    <meta ctg="key" cnt="/image_for_test.jpeg"/>
+  </document>
+  <tag key="/image_for_test.jpeg" timeStart="0" timeEnd="15" version="0"/>
+</project>
diff --git a/tests/functional/ProjectControllerTest.php b/tests/functional/ProjectControllerTest.php
index 45d2830..5b2ef48 100644
--- a/tests/functional/ProjectControllerTest.php
+++ b/tests/functional/ProjectControllerTest.php
@@ -17,6 +17,7 @@ class ProjectControllerTest extends WebTestCase
     private Form $form;
     private const TEST_DIR_PATH = __DIR__ . '/../../legacy/';
     private const CAPSULE_NAME = 'TestCapsuleName';
+    private const TEST_FILES_DIRECTORY = "../tests/files_for_tests/";
 
     protected function setUp(): void
     {
@@ -39,32 +40,20 @@ class ProjectControllerTest extends WebTestCase
         }
 
         $this->client->loginUser($verified_user);
-
-        $crawler = $this->client->request('GET', '/create');
-        $this->assertResponseIsSuccessful();
-
-        $this->client->enableProfiler();
-
-        $submit_button = $crawler->selectButton('Create a capsule');
-        $this->form = $submit_button->form();
     }
 
     protected function tearDown(): void
     {
         parent::tearDown();
-        $file_system = new Filesystem();
-        $file_system->remove(self::TEST_DIR_PATH . self::CAPSULE_NAME);
 
         $capsule_repository = $this->object_manager->getRepository(Capsule::class);
-        $detached_last_capsule = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]);
+        $capsule = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]);
 
-        if (! $detached_last_capsule instanceof Capsule) {
+        if (! $capsule instanceof Capsule) {
             throw new \Exception("Capsule does not exist.");
         }
 
-        $last_capsule = $this->object_manager->merge($detached_last_capsule);
-        $this->object_manager->remove($last_capsule);
-        $this->object_manager->flush();
+        $this->deleteCapsuleDirectoryAndInDatabase($capsule);
     }
 
     /** @phpstan-ignore-next-line  */
@@ -94,6 +83,14 @@ class ProjectControllerTest extends WebTestCase
 
     public function testProjectDirectoryWithCorrespondingXMLFileIsCreatedWhenCapsuleCreationIsSuccessful(): void
     {
+        $crawler = $this->client->request('GET', '/create');
+        $this->assertResponseIsSuccessful();
+
+        $this->client->enableProfiler();
+
+        $submit_button = $crawler->selectButton('Create a capsule');
+        $this->form = $submit_button->form();
+
         $video_url = "https://TestUrl";
         $this->form['create_capsule_form[name]'] = self::CAPSULE_NAME;
         $this->form['create_capsule_form[video_url]'] = $video_url;
@@ -137,11 +134,7 @@ class ProjectControllerTest extends WebTestCase
 
     public function testProjectDirectoryWithCorrespondingPasswordFileIsCreatedWhenCapsuleCreationIsSuccessful(): void
     {
-        $video_url = "https://TestUrl";
-        $this->form['create_capsule_form[name]'] = self::CAPSULE_NAME;
-        $this->form['create_capsule_form[video_url]'] = $video_url;
-
-        $this->client->submit($this->form);
+        $this->createCapsule();
 
         $this->assertResponseRedirects(
             '/my_capsules',
@@ -159,7 +152,6 @@ class ProjectControllerTest extends WebTestCase
             throw new \Exception("Capsule does not exist.");
         }
 
-        $capsule_name_in_db = $capsule_in_db->getName();
         $capsule_directory = $capsule_in_db->getLinkPath();
 
         $this->assertDirectoryExists($capsule_directory);
@@ -175,4 +167,135 @@ class ProjectControllerTest extends WebTestCase
             "The password should be saved in projectPassword.txt file"
         );
     }
+
+    public function testDuplicatedDirectoryDuplicatedFilesFromParentWhenCapsuleDuplicationIsSuccessful(): void
+    {
+        $duplicated_capsule_name = 'duplicated ' . self::CAPSULE_NAME;
+        $this->createCapsule();
+
+        $capsule_repository = $this->object_manager->getRepository(Capsule::class);
+        $parent_capsule = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]);
+
+        if (! $parent_capsule instanceof Capsule) {
+            throw new \Exception("Capsule does not exist.");
+        }
+
+        $parent_capsule_directory = $parent_capsule->getLinkPath();
+
+        $crawler = $this->client->request('GET', '/capsule/duplicate/' . $parent_capsule->getId());
+        $this->assertResponseIsSuccessful();
+
+        $this->client->enableProfiler();
+
+        $submit_button = $crawler->selectButton('Validate');
+        $this->form = $submit_button->form();
+        $this->form['duplicate_capsule_form[name]'] = $duplicated_capsule_name;
+        $this->client->submit($this->form);
+
+        $this->assertResponseRedirects(
+            '/my_capsules',
+            302,
+            'Once the capsule is duplicated, the user should be redirected to its capsules lists'
+        );
+
+        $this->client->followRedirect();
+        $this->assertResponseIsSuccessful('/my_capsules');
+
+        $capsule_repository = $this->object_manager->getRepository(Capsule::class);
+        $duplicated_capsule_in_db = $capsule_repository->findOneBy(['name' => $duplicated_capsule_name]);
+
+        if (! $duplicated_capsule_in_db instanceof Capsule) {
+            throw new \Exception("Capsule does not exist.");
+        }
+
+        $duplicated_capsule_directory = $duplicated_capsule_in_db->getLinkPath();
+
+        $this->assertDirectoryExists($duplicated_capsule_directory);
+        $this->assertDirectoryIsReadable($duplicated_capsule_directory);
+        $this->assertXmlFileEqualsXmlFile(
+            $this->getXmlFilePath($duplicated_capsule_directory),
+            $this->getXmlFilePath($duplicated_capsule_directory)
+        );
+        $this->assertAllFilesOfFileDirectoryAreSameExceptPasswordOne(
+            $parent_capsule_directory,
+            $duplicated_capsule_directory
+        );
+
+        $password_file_path = $this->getPasswordFilePath($duplicated_capsule_directory);
+        $password = file_get_contents($password_file_path, true);
+
+        $this->assertTrue(false !== $password, "The projectPassword.txt should be readable");
+        $this->assertSame(
+            $duplicated_capsule_in_db->getPassword(),
+            $password,
+            "The password should be saved in projectPassword.txt file"
+        );
+
+        $this->deleteCapsuleDirectoryAndInDatabase($duplicated_capsule_in_db);
+    }
+
+    private function assertAllFilesOfFileDirectoryAreSameExceptPasswordOne(
+        string $parent_capsule_directory,
+        string $duplicated_capsule_directory
+    ): void {
+        $parent_capsule_files = scandir($parent_capsule_directory . "/file/");
+
+        if (! is_array($parent_capsule_files)) {
+            return;
+        }
+
+        foreach ($parent_capsule_files as $key => $file) {
+            if ($file === "projectPassword.txt" || is_dir($file)) {
+                return;
+            }
+
+            $this->assertFileExists($duplicated_capsule_directory . "/file/" . $file);
+            $this->assertFileEquals($file, $duplicated_capsule_directory . "/file/" . $file);
+        }
+    }
+
+    private function createCapsule(): void
+    {
+        $crawler = $this->client->request('GET', '/create');
+        $this->assertResponseIsSuccessful();
+
+        $this->client->enableProfiler();
+
+        $submit_button = $crawler->selectButton('Create a capsule');
+        $this->form = $submit_button->form();
+
+        $video_url = "https://TestUrl";
+        $this->form['create_capsule_form[name]'] = self::CAPSULE_NAME;
+        $this->form['create_capsule_form[video_url]'] = $video_url;
+
+        $this->client->submit($this->form);
+
+        $capsule_repository = $this->object_manager->getRepository(Capsule::class);
+        $capsule = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]);
+
+        if (! $capsule instanceof Capsule) {
+            throw new \Exception("Capsule does not exist.");
+        }
+
+        $capsule_directory_path = $capsule->getLinkPath();
+        $file_system = new Filesystem();
+        $file_system->mirror(
+            self::TEST_FILES_DIRECTORY,
+            $capsule_directory_path . '/file/',
+            null,
+            ['override' => true]
+        );
+    }
+
+    private function deleteCapsuleDirectoryAndInDatabase(Capsule $capsule): void
+    {
+        $file_system = new Filesystem();
+
+        $capsule_directory_path = $capsule->getLinkPath();
+        $file_system->remove(self::TEST_DIR_PATH . $capsule_directory_path);
+
+        $capsule_to_delete = $this->object_manager->merge($capsule);
+        $this->object_manager->remove($capsule_to_delete);
+        $this->object_manager->flush();
+    }
 }
-- 
GitLab