]> BookStack Code Mirror - bookstack/blob - tests/Helpers/EntityProvider.php
Quick run through of applying new test entity helper class
[bookstack] / tests / Helpers / EntityProvider.php
1 <?php
2
3 namespace Tests\Helpers;
4
5 use BookStack\Auth\Role;
6 use BookStack\Auth\User;
7 use BookStack\Entities\Models\Book;
8 use BookStack\Entities\Models\Bookshelf;
9 use BookStack\Entities\Models\Chapter;
10 use BookStack\Entities\Models\Entity;
11 use BookStack\Entities\Models\Page;
12 use BookStack\Entities\Repos\BookRepo;
13 use BookStack\Entities\Repos\BookshelfRepo;
14 use BookStack\Entities\Repos\ChapterRepo;
15 use BookStack\Entities\Repos\PageRepo;
16 use Illuminate\Database\Eloquent\Builder;
17
18 /**
19  * Class to provider and action entity models for common test case
20  * operations. Tracks handled models and only returns fresh models.
21  * Does not dedupe against nested/child/parent models.
22  */
23 class EntityProvider
24 {
25     /**
26      * @var array<string, int[]>
27      */
28     protected array $fetchCache = [
29         'book' => [],
30         'page' => [],
31         'bookshelf' => [],
32         'chapter' => [],
33     ];
34
35     /**
36      * Get an un-fetched page from the system.
37      */
38     public function page(callable $queryFilter = null): Page
39     {
40         /** @var Page $page */
41         $page = Page::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['page'])->first();
42         $this->addToCache($page);
43         return $page;
44     }
45
46     public function pageWithinChapter(): Page
47     {
48         return $this->page(fn(Builder $query) => $query->whereHas('chapter')->with('chapter'));
49     }
50
51     public function pageNotWithinChapter(): Page
52     {
53         return $this->page(fn(Builder $query) => $query->where('chapter_id', '=', 0));
54     }
55
56     /**
57      * Get an un-fetched chapter from the system.
58      */
59     public function chapter(callable $queryFilter = null): Chapter
60     {
61         /** @var Chapter $chapter */
62         $chapter = Chapter::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['chapter'])->first();
63         $this->addToCache($chapter);
64         return $chapter;
65     }
66
67     public function chapterHasPages(): Chapter
68     {
69         return $this->chapter(fn(Builder $query) => $query->whereHas('pages'));
70     }
71
72     /**
73      * Get an un-fetched book from the system.
74      */
75     public function book(callable $queryFilter = null): Book
76     {
77         /** @var Book $book */
78         $book = Book::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['book'])->first();
79         $this->addToCache($book);
80         return $book;
81     }
82
83     /**
84      * Get a book that has chapters and pages assigned.
85      */
86     public function bookHasChaptersAndPages(): Book
87     {
88         return $this->book(function (Builder $query) {
89             $query->has('chapters')->has('pages')->with(['chapters', 'pages']);
90         });
91     }
92
93     /**
94      * Get an un-fetched shelf from the system.
95      */
96     public function shelf(callable $queryFilter = null): Bookshelf
97     {
98         /** @var Bookshelf $shelf */
99         $shelf = Bookshelf::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['bookshelf'])->first();
100         $this->addToCache($shelf);
101         return $shelf;
102     }
103
104     /**
105      * Get all entity types from the system.
106      * @return array{page: Page, chapter: Chapter, book: Book, bookshelf: Bookshelf}
107      */
108     public function all(): array
109     {
110         return [
111             'page'      => $this->page(),
112             'chapter'   => $this->chapter(),
113             'book'      => $this->book(),
114             'bookshelf' => $this->shelf(),
115         ];
116     }
117
118     public function updatePage(Page $page, array $data): Page
119     {
120         $this->addToCache($page);
121         return app()->make(PageRepo::class)->update($page, $data);
122     }
123
124     /**
125      * Create a book to page chain of entities that belong to a specific user.
126      * @return array{book: Book, chapter: Chapter, page: Page}
127      */
128     public function createChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
129     {
130         if (empty($updaterUser)) {
131             $updaterUser = $creatorUser;
132         }
133
134         $userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
135         /** @var Book $book */
136         $book = Book::factory()->create($userAttrs);
137         $chapter = Chapter::factory()->create(array_merge(['book_id' => $book->id], $userAttrs));
138         $page = Page::factory()->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
139
140         $book->rebuildPermissions();
141         $this->addToCache([$page, $chapter, $book]);
142
143         return compact('book', 'chapter', 'page');
144     }
145
146     /**
147      * Create and return a new bookshelf.
148      */
149     public function newShelf(array $input = ['name' => 'test shelf', 'description' => 'My new test shelf']): Bookshelf
150     {
151         $shelf = app(BookshelfRepo::class)->create($input, []);
152         $this->addToCache($shelf);
153         return $shelf;
154     }
155
156     /**
157      * Create and return a new book.
158      */
159     public function newBook(array $input = ['name' => 'test book', 'description' => 'My new test book']): Book
160     {
161         $book = app(BookRepo::class)->create($input);
162         $this->addToCache($book);
163         return $book;
164     }
165
166     /**
167      * Create and return a new test chapter.
168      */
169     public function newChapter(array $input, Book $book): Chapter
170     {
171         $chapter = app(ChapterRepo::class)->create($input, $book);
172         $this->addToCache($chapter);
173         return $chapter;
174     }
175
176     /**
177      * Create and return a new test page.
178      */
179     public function newPage(array $input = ['name' => 'test page', 'html' => 'My new test page']): Page
180     {
181         $book = $this->book();
182         $pageRepo = app(PageRepo::class);
183         $draftPage = $pageRepo->getNewDraftPage($book);
184         $this->addToCache($draftPage);
185         return $pageRepo->publishDraft($draftPage, $input);
186     }
187
188     /**
189      * Regenerate the permission for an entity.
190      * Centralised to manage clearing of cached elements between requests.
191      */
192     public function regenPermissions(Entity $entity): void
193     {
194         $entity->rebuildPermissions();
195         $entity->load('jointPermissions');
196     }
197
198     /**
199      * Set the given entity as having restricted permissions, and apply the given
200      * permissions for the given roles.
201      * @param string[] $actions
202      * @param Role[] $roles
203      */
204     public function setPermissions(Entity $entity, array $actions = [], array $roles = []): void
205     {
206         $entity->restricted = true;
207         $entity->permissions()->delete();
208
209         $permissions = [];
210         foreach ($actions as $action) {
211             foreach ($roles as $role) {
212                 $permissions[] = [
213                     'role_id' => $role->id,
214                     'action'  => strtolower($action),
215                 ];
216             }
217         }
218
219         $entity->permissions()->createMany($permissions);
220         $entity->save();
221         $entity->load('permissions');
222         $this->regenPermissions($entity);
223     }
224
225     /**
226      * @param Entity|Entity[] $entities
227      */
228     protected function addToCache($entities): void
229     {
230         if (!is_array($entities)) {
231             $entities = [$entities];
232         }
233
234         foreach ($entities as $entity) {
235             $this->fetchCache[$entity->getType()][] = $entity->id;
236         }
237     }
238 }