Skip to content

Commit bd024d1

Browse files
committed
Add tests, resolve #1
1 parent 219838c commit bd024d1

8 files changed

Lines changed: 168 additions & 8 deletions

File tree

artisan

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
// We call 'php artisan lean:install' in a test, so we need *some* artisan file in here.
4+
// We don't test the actual behavior, but the file needs to exist for the test to pass.

composer.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
"Lean\\Installer\\": "src/"
1515
}
1616
},
17+
"autoload-dev": {
18+
"psr-4": {
19+
"Lean\\Installer\\Tests\\": "tests/"
20+
}
21+
},
1722
"require": {
1823
"php": "^7.2|^8.0",
1924
"illuminate/console": "^8.24",
@@ -31,6 +36,13 @@
3136
"require-dev": {
3237
"orchestra/testbench": "^6.9",
3338
"vimeo/psalm": "^4.2",
34-
"nunomaduro/larastan": "^0.6.10"
39+
"nunomaduro/larastan": "^0.6.10",
40+
"guzzlehttp/guzzle": "^7.2"
41+
},
42+
"repositories": {
43+
"lean": {
44+
"type": "composer",
45+
"url": "https://lean-admin.dev/releases"
46+
}
3547
}
3648
}

phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ includes:
44
parameters:
55
paths:
66
- src
7-
- tests
87

98
level: 8
109

src/Commands/Setup.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
use Illuminate\Console\Command;
88
use Illuminate\Contracts\Filesystem\Filesystem;
9-
use Illuminate\Http\Client\Factory;
109
use Illuminate\Http\Client\Response;
10+
use Illuminate\Support\Facades\Http;
1111
use Lean\Installer\Concerns;
1212

1313
class Setup extends Command
@@ -21,16 +21,14 @@ class Setup extends Command
2121
public static string $repository = 'https://lean-admin.dev/releases';
2222

2323
protected Filesystem $files;
24-
protected Factory $http;
2524

2625
public $signature = 'lean:setup';
2726

28-
public function __construct(Filesystem $files, Factory $http)
27+
public function __construct(Filesystem $files)
2928
{
3029
parent::__construct();
3130

3231
$this->files = $files;
33-
$this->http = $http;
3432
}
3533

3634
public function handle(): int
@@ -85,7 +83,7 @@ protected function getComputerName(): ?string
8583

8684
protected function api(string $path, string $email, string $password, array $extra = []): Response
8785
{
88-
return $this->http->acceptJson()->withoutRedirecting()->put(static::$endpoint . '/' . $path, array_merge([
86+
return Http::acceptJson()->withoutRedirecting()->put(static::$endpoint . '/' . $path, array_merge([
8987
'email' => $email,
9088
'password' => $password,
9189
], $extra));

src/Concerns/AcceptsCredentials.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
trait AcceptsCredentials
1414
{
15+
/* Used for testing. */
16+
public static ?string $emailOverride = null;
17+
1518
protected function getCredentials(): array
1619
{
1720
$email = null;
@@ -53,7 +56,7 @@ protected function getCredentials(): array
5356
$auth = $this->checkCredentials($email, $password);
5457

5558
if ($auth->successful()) {
56-
$this->info('Successfuly logged in.');
59+
$this->info('Successfully logged in.');
5760

5861
return [$email, $password];
5962
} else {
@@ -79,6 +82,10 @@ protected function getCredentials(): array
7982

8083
protected function getEmailFromGit(): ?string
8184
{
85+
if (static::$emailOverride) {
86+
return static::$emailOverride;
87+
}
88+
8289
try {
8390
$process = $this->getProcess(['git', 'config', 'user.email']);
8491
$process->mustRun();

src/Concerns/RunsInstallCommand.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Lean\Installer\Concerns;
66

77
use Exception;
8+
use Symfony\Component\Process\Process;
89

910
trait RunsInstallCommand
1011
{
@@ -17,11 +18,14 @@ protected function phpArtisanLeanInstall(): void
1718
if (! $install->isSuccessful()) {
1819
$this->error('The command `php artisan lean:install` failed.');
1920

21+
$this->output->write(dd($install->getOutput()));
2022
$this->output->write($install->getErrorOutput());
2123

2224
throw new Exception('Install command failed.');
2325
} else {
2426
$this->output->write($install->getOutput());
2527
}
2628
}
29+
30+
abstract protected function getProcess(array $command): Process;
2731
}

tests/CredentialsTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Lean\Installer\Tests;
4+
5+
use Lean\Installer\Concerns\AcceptsCredentials;
6+
7+
class CredentialsTest extends TestCase
8+
{
9+
/** @test */
10+
public function credentials_are_validated_locally()
11+
{
12+
$this->expectExceptionMessage('Interrupted by user');
13+
14+
$this->artisan('lean:setup')
15+
->expectsQuestion("What's your email?", 'samuel.stanclgmail.com')
16+
->expectsOutput('Invalid email') // <--
17+
->expectsQuestion("What's your email?", 'samuel.stancl@gmail.com')
18+
->expectsQuestion("What's your password?", 'foo')
19+
->expectsOutput('Invalid credentials')
20+
->expectsConfirmation('Retry?', 'no')
21+
->assertExitCode(1);
22+
}
23+
24+
/** @test */
25+
public function if_the_server_returns_an_error_it_will_be_shown()
26+
{
27+
$this->expectExceptionMessage('Interrupted by user');
28+
29+
$this->artisan('lean:setup')
30+
->expectsQuestion("What's your email?", 'samuel.stancl@gmail.com')
31+
->expectsQuestion("What's your password?", 'foo')
32+
->expectsOutput('Invalid credentials') // <--
33+
->expectsConfirmation('Retry?', 'no')
34+
->assertExitCode(1);
35+
}
36+
37+
// Right now there's not a scenario when this can happen, because all validation happens on both
38+
// the frontend and the backend. Keeping this test here since the behavior itself is in place
39+
// and I may want to test this sometime, when the backend returns other validation errors.
40+
// /** @test */
41+
// public function if_the_server_returns_errors_for_specific_fields_they_will_be_shown()
42+
// {
43+
// $this->expectExceptionMessage('Interrupted by user');
44+
45+
// $this->artisan('lean:setup')
46+
// ->expectsQuestion("What's your email?", 'samuel.stancl@gmail.com')
47+
// ->expectsQuestion("What's your password?", 'foo')
48+
// ->expectsOutput('Invalid credentials')
49+
// ->expectsConfirmation('Retry?', 'no')
50+
// ->assertExitCode(1);
51+
// }
52+
53+
/** @test */
54+
public function email_defaults_to_git_email()
55+
{
56+
AcceptsCredentials::$emailOverride = 'samuel.stancl+test@gmail.com';
57+
58+
$this->expectExceptionMessage('Interrupted by user');
59+
60+
$this->artisan('lean:setup')
61+
->expectsChoice("What's your email?", 'samuel.stancl+test@gmail.com', ['samuel.stancl+test@gmail.com']) // <--
62+
->expectsQuestion("What's your password?", 'foo')
63+
->expectsOutput('Invalid credentials')
64+
->expectsConfirmation('Retry?', 'no')
65+
->assertExitCode(1);
66+
}
67+
}

tests/TokenTest.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Lean\Installer\Tests;
4+
5+
use Illuminate\Support\Facades\Http;
6+
7+
class TokenTest extends TestCase
8+
{
9+
/** @test */
10+
public function token_name_is_required()
11+
{
12+
Http::fake([
13+
'lean-admin.dev/install/auth' => Http::response(['message' => 'OK'], 200, ['Content-Type' => 'application/json']),
14+
]);
15+
16+
$this->artisan('lean:setup')
17+
->expectsQuestion("What's your email?", 'samuel.stancl@gmail.com')
18+
->expectsQuestion("What's your password?", 'foo')
19+
->expectsOutput('Successfully logged in.')
20+
->expectsQuestion('Token name', '')
21+
->expectsOutput('Invalid token') // <--
22+
->expectsQuestion('Token name', 'foo')
23+
->expectsOutput('Generating token...')
24+
->assertExitCode(0);
25+
}
26+
27+
/** @test */
28+
public function token_name_defaults_to_the_computer_hostname()
29+
{
30+
Http::fake([
31+
'lean-admin.dev/install/auth' => Http::response(['message' => 'OK'], 200, ['Content-Type' => 'application/json']),
32+
]);
33+
34+
$this->artisan('lean:setup')
35+
->expectsQuestion("What's your email?", 'samuel.stancl@gmail.com')
36+
->expectsQuestion("What's your password?", 'foo')
37+
->expectsChoice('Token name', gethostname(), [gethostname()]) // <--
38+
->assertExitCode(0);
39+
}
40+
41+
/** @test */
42+
public function other_content_in_auth_json_is_not_overridden()
43+
{
44+
file_put_contents(base_path('auth.json'), json_encode(['my' => 'preexisting content']));
45+
46+
Http::fake([
47+
'lean-admin.dev/install/auth' => Http::response(['message' => 'OK'], 200, ['Content-Type' => 'application/json']),
48+
'lean-admin.dev/install/token' => Http::response(['token' => 'bar'], 200, ['Content-Type' => 'application/json']),
49+
]);
50+
51+
$this->artisan('lean:setup')
52+
->expectsQuestion("What's your email?", 'samuel.stancl@gmail.com')
53+
->expectsQuestion("What's your password?", 'foo')
54+
->expectsQuestion('Token name', 'foo')
55+
->expectsOutput('Generating token...')
56+
->expectsOutput('✅ Everything succeeded ✅')
57+
->expectsQuestion('Remove installer now?', false)
58+
->assertExitCode(0);
59+
60+
$content = json_decode(file_get_contents(base_path('auth.json')), true);
61+
62+
$this->assertSame([
63+
'my' => 'preexisting content',
64+
'bearer' => [
65+
'lean-admin.dev' => 'bar',
66+
],
67+
], $content);
68+
}
69+
}

0 commit comments

Comments
 (0)