3 namespace Tests\Commands;
5 use BookStack\Uploads\Image;
6 use BookStack\Users\Models\User;
7 use GuzzleHttp\Psr7\Response;
8 use Illuminate\Database\Eloquent\Collection;
11 class RefreshAvatarCommandTest extends TestCase
13 public function setUp(): void
18 'services.disable_services' => false,
19 'services.avatar_url' => 'https://avatars.example.com?a=b',
23 public function test_command_errors_if_avatar_fetch_disabled()
25 config()->set(['services.avatar_url' => false]);
27 $this->artisan('bookstack:refresh-avatar')
28 ->expectsOutputToContain("Avatar fetching is disabled on this instance")
32 public function test_command_requires_email_or_id_option()
34 $this->artisan('bookstack:refresh-avatar')
35 ->expectsOutputToContain("Either a --id=<number> or --email=<email> option must be provided")
39 public function test_command_runs_with_provided_email()
41 $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
43 $user = $this->users->viewer();
44 $this->assertFalse($user->avatar()->exists());
46 $this->artisan("bookstack:refresh-avatar --email={$user->email} -f")
47 ->expectsQuestion('Are you sure you want to proceed?', true)
48 ->expectsOutput("[ID: {$user->id}] {$user->email} - Updated")
49 ->expectsOutputToContain('This will destroy any existing avatar images these users have, and attempt to fetch new avatar images from avatars.example.com')
52 $this->assertEquals('https://avatars.example.com?a=b', $requests->latestRequest()->getUri());
55 $this->assertTrue($user->avatar()->exists());
58 public function test_command_runs_with_provided_id()
60 $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
62 $user = $this->users->viewer();
63 $this->assertFalse($user->avatar()->exists());
65 $this->artisan("bookstack:refresh-avatar --id={$user->id} -f")
66 ->expectsQuestion('Are you sure you want to proceed?', true)
67 ->expectsOutput("[ID: {$user->id}] {$user->email} - Updated")
70 $this->assertEquals('https://avatars.example.com?a=b', $requests->latestRequest()->getUri());
73 $this->assertTrue($user->avatar()->exists());
76 public function test_command_runs_with_provided_id_error_upstream()
78 $requests = $this->mockHttpClient([new Response(404)]);
80 $user = $this->users->viewer();
81 $this->assertFalse($user->avatar()->exists());
83 $this->artisan("bookstack:refresh-avatar --id={$user->id} -f")
84 ->expectsQuestion('Are you sure you want to proceed?', true)
85 ->expectsOutput("[ID: {$user->id}] {$user->email} - Not updated")
88 $this->assertEquals(1, $requests->requestCount());
89 $this->assertFalse($user->avatar()->exists());
92 public function test_saying_no_to_confirmation_does_not_refresh_avatar()
94 $user = $this->users->viewer();
96 $this->assertFalse($user->avatar()->exists());
97 $this->artisan("bookstack:refresh-avatar --id={$user->id} -f")
98 ->expectsQuestion('Are you sure you want to proceed?', false)
100 $this->assertFalse($user->avatar()->exists());
103 public function test_giving_non_existing_user_shows_error_message()
110 public function test_command_runs_all_users_without_avatars_dry_run()
112 $users = User::query()->where('image_id', '=', 0)->get();
114 $this->artisan('bookstack:refresh-avatar --users-without-avatars')
115 ->expectsOutput(count($users) . ' user(s) found to update avatars for.')
116 ->expectsOutput("[ID: {$users[0]->id}] {$users[0]->email} - Not updated")
117 ->expectsOutput('Dry run, no avatars were updated.')
121 public function test_command_runs_all_users_without_avatars_with_none_to_update()
123 $requests = $this->mockHttpClient();
124 $image = Image::factory()->create();
125 User::query()->update(['image_id' => $image->id]);
127 $this->artisan('bookstack:refresh-avatar --users-without-avatars -f')
128 ->expectsOutput('0 user(s) found to update avatars for.')
131 $this->assertEquals(0, $requests->requestCount());
134 public function test_command_runs_all_users_without_avatars()
136 /** @var Collection|User[] $users */
137 $users = User::query()->where('image_id', '=', 0)->get();
139 $pendingCommand = $this->artisan('bookstack:refresh-avatar --users-without-avatars -f');
141 ->expectsOutput($users->count() . ' user(s) found to update avatars for.')
142 ->expectsQuestion('Are you sure you want to proceed?', true);
145 foreach ($users as $user) {
146 $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Updated");
147 $responses[] = new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData());
149 $requests = $this->mockHttpClient($responses);
151 $pendingCommand->assertExitCode(0);
152 $pendingCommand->run();
154 $this->assertEquals(0, User::query()->where('image_id', '=', 0)->count());
155 $this->assertEquals($users->count(), $requests->requestCount());
158 public function test_saying_no_to_confirmation_all_users_without_avatars()
160 $requests = $this->mockHttpClient();
162 $this->artisan('bookstack:refresh-avatar --users-without-avatars -f')
163 ->expectsQuestion('Are you sure you want to proceed?', false)
166 $this->assertEquals(0, $requests->requestCount());
169 public function test_command_runs_all_users_dry_run()
171 $users = User::query()->where('image_id', '=', 0)->get();
173 $this->artisan('bookstack:refresh-avatar --all')
174 ->expectsOutput(count($users) . ' user(s) found to update avatars for.')
175 ->expectsOutput("[ID: {$users[0]->id}] {$users[0]->email} - Not updated")
176 ->expectsOutput('Dry run, no avatars were updated.')
180 public function test_command_runs_update_all_users_avatar()
182 /** @var Collection|User[] $users */
183 $users = User::query()->get();
185 $pendingCommand = $this->artisan('bookstack:refresh-avatar --all -f');
187 ->expectsOutput($users->count() . ' user(s) found to update avatars for.')
188 ->expectsQuestion('Are you sure you want to proceed?', true);
191 foreach ($users as $user) {
192 $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Updated");
193 $responses[] = new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData());
195 $requests = $this->mockHttpClient($responses);
197 $pendingCommand->assertExitCode(0);
198 $pendingCommand->run();
200 $this->assertEquals(0, User::query()->where('image_id', '=', 0)->count());
201 $this->assertEquals($users->count(), $requests->requestCount());
204 public function test_command_runs_update_all_users_avatar_errors()
206 /** @var Collection|User[] $users */
207 $users = array_values(User::query()->get()->all());
209 $pendingCommand = $this->artisan('bookstack:refresh-avatar --all -f');
211 ->expectsOutput(count($users) . ' user(s) found to update avatars for.')
212 ->expectsQuestion('Are you sure you want to proceed?', true);
215 foreach ($users as $index => $user) {
217 $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Not updated");
218 $responses[] = new Response(404);
222 $pendingCommand->expectsOutput("[ID: {$user->id}] {$user->email} - Updated");
223 $responses[] = new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData());
226 $requests = $this->mockHttpClient($responses);
228 $pendingCommand->assertExitCode(1);
229 $pendingCommand->run();
231 $userWithAvatars = User::query()->where('image_id', '!=', 0)->count();
232 $this->assertEquals(count($users) - 1, $userWithAvatars);
233 $this->assertEquals(count($users), $requests->requestCount());
236 public function test_saying_no_to_confirmation_update_all_users_avatar()
238 $requests = $this->mockHttpClient([new Response(200, ['Content-Type' => 'image/png'], $this->files->pngImageData())]);
240 $this->artisan('bookstack:refresh-avatar --all -f')
241 ->expectsQuestion('Are you sure you want to proceed?', false)
244 $this->assertEquals(0, $requests->requestCount());