MDL-69549 core: Add context export API
[moodle.git] / lib / tests / content / export / exportable_items / exportable_filearea_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Unit tests for core\content\exportable_items\exportable_filearea.
19  *
20  * @package     core
21  * @category    test
22  * @copyright   2020 Andrew Nicols <andrew@nicols.co.uk>
23  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 declare(strict_types=1);
28 namespace core\content\export\exportable_items;
30 use advanced_testcase;
31 use context;
32 use context_system;
33 use core\content\export\zipwriter;
34 use core\content\export\exported_item;
35 use moodle_url;
36 use stdClass;
38 /**
39  * Unit tests for the `exportable_filearea` export item class.
40  *
41  * @coversDefaultClass core\content\exportable_items\exportable_filearea
42  */
43 class exportable_filearea_test extends advanced_testcase {
45     /**
46      * Ensure that the the exportable_filearea does not fetch files when none exist.
47      */
48     public function test_no_files(): void {
49         $exportable = new exportable_filearea(
50             context_system::instance(),
51             'fake',
52             'Some fake filearea',
53             'filearea',
54             1
55         );
57         $this->assertInstanceOf(exportable_filearea::class, $exportable);
58     }
60     /**
61      * Ensure that the exportable_filearea returns all stored_file items for only the specified itemid, but those which
62      * are not included in the archive receive a pluginfile URL.
63      */
64     public function test_specified_itemid_excluded_from_zip(): void {
65         $this->resetAfterTest(true);
67         // Setup for test.
68         $user = $this->getDataGenerator()->create_user();
69         $context = context_system::instance();
70         $component = 'fake';
71         $filearea = 'myfirstfilearea';
73         $files1 = $this->create_files(context_system::instance(), $component, $filearea, 1);
74         $files2 = $this->create_files(context_system::instance(), $component, $filearea, 2);
75         $files3 = $this->create_files(context_system::instance(), $component, $filearea, 3);
76         $otherfiles2 = $this->create_files(context_system::instance(), $component, "other{$filearea}", 2);
78         $exportable = new exportable_filearea(
79             $context,
80             $component,
81             'Some filearea description',
82             $filearea,
83             2
84         );
86         // There is only one exportable.
87         $this->assertInstanceOf(exportable_filearea::class, $exportable);
89         $file2 = reset($files2);
90         $item = $this->assert_exportable_matches_file($component, $user, $context, $filearea, '', $files2, false, $exportable);
91         $this->assertCount(count($files2), $item->get_all_files());
92         $comparisonurl = new moodle_url('/tokenpluginfile.php/');
93         foreach ($item->get_all_files() as $url) {
94             $this->assertStringStartsWith($comparisonurl->out(false), $url->filepath);
95         }
96     }
98     /**
99      * Ensure that the exportable_filearea returns all stored_file items for only the specified itemid.
100      */
101     public function test_specified_itemid(): void {
102         $this->resetAfterTest(true);
104         // Setup for test.
105         $user = $this->getDataGenerator()->create_user();
106         $context = context_system::instance();
107         $component = 'fake';
108         $filearea = 'myfirstfilearea';
110         $files1 = $this->create_files(context_system::instance(), $component, $filearea, 1);
111         $files2 = $this->create_files(context_system::instance(), $component, $filearea, 2);
112         $files3 = $this->create_files(context_system::instance(), $component, $filearea, 3);
113         $otherfiles2 = $this->create_files(context_system::instance(), $component, "other{$filearea}", 2);
115         $exportable = new exportable_filearea(
116             $context,
117             $component,
118             'Some filearea description',
119             $filearea,
120             2
121         );
123         // There is only one exportable.
124         $this->assertInstanceOf(exportable_filearea::class, $exportable);
126         $file2 = reset($files2);
127         $item = $this->assert_exportable_matches_file($component, $user, $context, $filearea, '', $files2, true, $exportable);
128         $this->assertCount(count($files2), $item->get_all_files());
129     }
131     /**
132      * Ensure that the exportable_filearea returns all stored_files into the correct file location.
133      */
134     public function test_in_subdir(): void {
135         $this->resetAfterTest(true);
137         // Setup for test.
138         $user = $this->getDataGenerator()->create_user();
139         $context = context_system::instance();
140         $component = 'fake';
141         $filearea = 'myfirstfilearea';
142         $subdir = 'a/path/to/my/subdir';
144         $files1 = $this->create_files(context_system::instance(), $component, $filearea, 1);
145         $files2 = $this->create_files(context_system::instance(), $component, $filearea, 2);
146         $files3 = $this->create_files(context_system::instance(), $component, $filearea, 3);
148         $exportable = new exportable_filearea(
149             $context,
150             $component,
151             'Some filearea description',
152             $filearea,
153             2,
154             2,
155             $subdir
156         );
158         // There is only one exportable.
159         $this->assertInstanceOf(exportable_filearea::class, $exportable);
161         $item = $this->assert_exportable_matches_file($component, $user, $context, $filearea, $subdir, $files2, true, $exportable);
162         $this->assertCount(count($files2), $item->get_all_files());
163     }
165     /**
166      * Create files for use in testing.
167      *
168      * @param   context $context
169      * @param   string $component
170      * @param   string $filearea
171      * @param   int $itemid
172      * @param   int $count
173      * @return  filearea[]
174      */
175     protected function create_files(context $context, string $component, string $filearea, int $itemid, int $count = 1): array {
176         $fs = get_file_storage();
178         $files = [];
179         for ($i = 0; $i < $count; $i++) {
181             $filepath = '/';
182             for ($j = 0; $j < $i; $j++) {
183                 $filepath .= "{$j}/";
184             }
186             $files[] = $fs->create_file_from_string(
187                 (object) [
188                     'contextid' => $context->id,
189                     'component' => $component,
190                     'filearea' => $filearea,
191                     'filepath' => $filepath,
192                     'filename' => "file.txt",
193                     'itemid' => $itemid,
194                 ],
195                 "File content: {$i}"
196             );
197         }
199         return $files;
200     }
202     /**
203      * Assert that the supplied expotable matches the supplied file.
204      *
205      * @param   string $component
206      * @param   stdClass $user
207      * @param   context $context
208      * @param   string $filearea
209      * @param   string $subdir
210      * @param   stored_file[] $expectedfiles
211      * @param   bool $addfilestozip Whether to allow files to be added to the archive
212      * @param   exportable_filearea $exportable
213      * @return  exported_item
214      */
215     protected function assert_exportable_matches_file(
216         string $component,
217         stdClass $user,
218         context $context,
219         string $filearea,
220         string $subdir,
221         array $expectedfiles,
222         bool $addfilestozip,
223         exportable_filearea $exportable
224     ): exported_item {
225         $archive = $this->getMockBuilder(zipwriter::class)
226             ->setConstructorArgs([$this->getMockBuilder(\ZipStream\ZipStream::class)->getmock()])
227             ->setMethods([
228                 'add_file_from_stored_file',
229                 'is_file_in_archive',
230             ])
231             ->getMock();
233         $archive->expects($this->any())
234             ->method('is_file_in_archive')
235             ->willReturn($addfilestozip);
237         $storedfileargs = [];
238         foreach ($expectedfiles as $file) {
239             $filepathinzip = $subdir . '/' . $file->get_filearea() . '/' . $file->get_filepath() . $file->get_filename();
240             $filepathinzip = ltrim(preg_replace('#/+#', '/', $filepathinzip), '/');
241             $storedfileargs[] = [
242                 $this->equalTo($context),
243                 $this->equalTo($filepathinzip),
244                 $this->equalTo($file),
245             ];
246         }
248         $archive->expects($this->exactly(count($expectedfiles)))
249             ->method('add_file_from_stored_file')
250             ->withConsecutive(...$storedfileargs);
252         return $exportable->add_to_archive($archive);
253     }