diff --git a/README.md b/README.md index 0e9f449..862da24 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,18 @@ Redis data is stored in `~/.porter/data/redis`. We leave it up to you to run this command at the moment, it is a temporary setting and resets on a machine restart. Not everyone will need it, and we'd rather not dig much deeper into your system to make it permanent. +## Ngrok + + - `porter ngrok {site?} {--region=eu} {--no-inspection}` + +It's sometimes handy to share your progress on a site without deploying it to a server out there in the world. [ngrok](https://ngrok.com) provides a decent solution for this. Porter provides an ngrok container which is able to forward your local site to an external url. + +You can optionally specify a site to ngrok (e.g. `konsulting.test` would be `konsulting`). You can also specify a region and optionally disable inspections of responses by ngrok. + +The ngrok UI interface will be available here: [http://0.0.0.0:4040](http://0.0.0.0:4040) + +In order to use ngrok, you need to use an alternative loopback address to 127.0.0.1, since this resolves to the container that a request is sent from otherwise. This can be done by following [these instructions](#dns). + ## Email We have a [MailHog](https://github.com/mailhog/MailHog) container; all emails are routed to this container from PHP when using the `mail()` function. diff --git a/app/Commands/Dns/Flush.php b/app/Commands/Dns/Flush.php index ac340e2..8d03cc6 100644 --- a/app/Commands/Dns/Flush.php +++ b/app/Commands/Dns/Flush.php @@ -3,7 +3,6 @@ namespace App\Commands\Dns; use App\Commands\BaseCommand; -use App\Support\Mechanics\Mechanic; class Flush extends BaseCommand { @@ -28,6 +27,6 @@ class Flush extends BaseCommand */ public function handle(): void { - app(Mechanic::class)->flushDns(); + $this->porterLibrary->getMechanic()->flushDns(); } } diff --git a/app/Commands/Dns/SetHost.php b/app/Commands/Dns/SetHost.php index a69a21f..2071766 100644 --- a/app/Commands/Dns/SetHost.php +++ b/app/Commands/Dns/SetHost.php @@ -4,7 +4,6 @@ use App\Commands\BaseCommand; use App\Support\Dnsmasq\Config; -use App\Support\Mechanics\Mechanic; class SetHost extends BaseCommand { @@ -29,16 +28,16 @@ class SetHost extends BaseCommand */ public function handle(): void { - $mechanic = app(Mechanic::class); - if ($this->option('restore')) { - $mechanic->restoreNetworking(); - app(Config::class)->updateIp('127.0.0.1'); + $this->porterLibrary->getMechanic()->removeAlternativeLoopbackAddress(); + app(Config::class)->updateIp($this->porterLibrary->getMechanic()->getStandardLoopback()); + $this->porter->restart('dns'); return; } - $mechanic->setupNetworking(); - app(Config::class)->updateIp($mechanic->getHostAddress()); + $this->porterLibrary->getMechanic()->addAlternativeLoopbackAddress(); + app(Config::class)->updateIp($this->porterLibrary->getMechanic()->getAlternativeLoopback()); + $this->porter->restart('dns'); } } diff --git a/app/Commands/Ngrok/Open.php b/app/Commands/Ngrok/Open.php new file mode 100644 index 0000000..bc8f136 --- /dev/null +++ b/app/Commands/Ngrok/Open.php @@ -0,0 +1,95 @@ +argument('site')); + $wasSecure = false; + + if (!$site) { + $this->error('No site at this location, and no site path provided.'); + + return; + } + + if (!$this->checkItWillResolveProperly()) { + return; + } + + if ($site->secure) { + $this->info('Removing SSL for site (required for free ngrok version)'); + $site->unsecure(); + $wasSecure = true; + } + + $this->porter->stop('ngrok'); + + $tls = ' -bind-tls='.($wasSecure ? 'true' : 'false'); + $region = ' -region='.$this->option('region'); + $inspect = ' -inspect='.($this->option('no-inspection') ? 'false' : 'true'); + + $this->dockerCompose + ->runContainer('ngrok') + ->append("ngrok http -host-header=rewrite{$region}{$tls}{$inspect} {$site->url}:80") + ->interactive() + ->perform(); + + if ($wasSecure) { + $this->info('Restoring SSL for site'); + $site->secure(); + } + + $this->porter->stop('ngrok'); + } + + /** + * Checking that Porter is using the dns:set-host IP. If we don't ngrok + * requests will only resolve to 127.0.0.1 which is internal to the + * ngrok container, and results in a useless 502 error. + * + * @return bool + */ + public function checkItWillResolveProperly() + { + try { + if ($this->porterLibrary->getMechanic()->isUsingStandardLoopback()) { + $this->info('You need to use an alternative loopback address.'); + $this->info('Please run porter dns:set-host and review the documentation here: https://github.com/konsulting/porter#dns'); + + return false; + } + } catch (UnableToRetrieveIP $e) { + $this->info('Please run porter dns:flush and try again. You may need to give it a little while.'); + + return false; + } + + return true; + } +} diff --git a/app/Models/Site.php b/app/Models/Site.php index d82dcb2..1d7e294 100644 --- a/app/Models/Site.php +++ b/app/Models/Site.php @@ -12,6 +12,7 @@ class Site extends Model { protected $guarded = []; + protected $casts = ['secure' => 'boolean']; /** * PHP Version. diff --git a/app/PorterLibrary.php b/app/PorterLibrary.php index 3a3859c..85ed86f 100644 --- a/app/PorterLibrary.php +++ b/app/PorterLibrary.php @@ -46,6 +46,20 @@ public function __construct(FilePublisher $filePublisher, Mechanic $mechanic, $p $this->mechanic = $mechanic; } + /** + * Set the Mechanic instance. + * + * @param Mechanic $mechanic + * + * @return $this + */ + public function setMechanic(Mechanic $mechanic) + { + $this->mechanic = $mechanic; + + return $this; + } + /** * Return the path for storing container config files. * @@ -272,4 +286,14 @@ protected function createDirectoryStructure() } } } + + /** + * Return the Mechanic instance. + * + * @return Mechanic + */ + public function getMechanic() + { + return $this->mechanic; + } } diff --git a/app/Support/Console/DockerCompose/NullCliCommand.php b/app/Support/Console/DockerCompose/NullCliCommand.php new file mode 100644 index 0000000..d008b04 --- /dev/null +++ b/app/Support/Console/DockerCompose/NullCliCommand.php @@ -0,0 +1,15 @@ +consoleWriter->info("Adding loopback alias to {$this->hostAddress}/24. Please provide your sudo password."); + $this->consoleWriter->info("Adding loopback alias to {$this->alternativeLoopback}/24. Please provide your sudo password."); - $command = "sudo ifconfig lo0 alias {$this->hostAddress}/24"; + $command = "sudo ifconfig lo0 alias {$this->alternativeLoopback}/24"; $this->cli->passthru($command); } /** - * Restore networking on Mac. + * Remove the alternative loopback address from the system. * * @return void */ - public function restoreNetworking() + public function removeAlternativeLoopbackAddress() { - $this->consoleWriter->info("Removing loopback alias to {$this->hostAddress}. Please provide your sudo password."); + $this->consoleWriter->info("Removing loopback alias to {$this->alternativeLoopback}. Please provide your sudo password."); - $command = "sudo ifconfig lo0 -alias {$this->hostAddress}"; + $command = "sudo ifconfig lo0 -alias {$this->alternativeLoopback}"; $this->cli->passthru($command); } /** - * Return the host IP address in use. + * Determine the working IP for Porter. + * + * @throws UnableToRetrieveIP * * @return string */ - public function getHostAddress() + public function getPorterDomainIp() { - return $this->hostAddress; + if (($records = dns_get_record('www.unlikely-domain-name.'.setting('domain'))) === []) { + throw new UnableToRetrieveIP(); + } + + return $records[0]['ip']; } } diff --git a/app/Support/Mechanics/Mechanic.php b/app/Support/Mechanics/Mechanic.php index 159efb4..91993c8 100644 --- a/app/Support/Mechanics/Mechanic.php +++ b/app/Support/Mechanics/Mechanic.php @@ -37,21 +37,51 @@ public function getUserHomePath(); public function flushDns(); /** - * Setup networking for Porter. + * Add the alternative loopback address to the system. * * @return void */ - public function setupNetworking(); + public function addAlternativeLoopbackAddress(); /** - * Restore networking. + * Remove the alternative loopback address from the system. * * @return void */ - public function restoreNetworking(); + public function removeAlternativeLoopbackAddress(); /** - * Get Host IP address. + * Get standard loopback address. + * + * @return string + */ + public function getStandardLoopback(); + + /** + * Get alternative loopback address. + * + * @return string + */ + public function getAlternativeLoopback(); + + /** + * Does a Porter domain resolve to the standard loopback address. + * + * @return bool + */ + public function isUsingAlternativeLoopback(); + + /** + * Does a Porter domain resolve to the standard loopback address? + * + * @return bool + */ + public function isUsingStandardLoopback(); + + /** + * Determine the working IP for Porter. + * + * @return string */ - public function getHostAddress(); + public function getPorterDomainIp(); } diff --git a/app/Support/Mechanics/Untrained.php b/app/Support/Mechanics/Untrained.php index 47a7b8c..afa53b3 100644 --- a/app/Support/Mechanics/Untrained.php +++ b/app/Support/Mechanics/Untrained.php @@ -17,8 +17,11 @@ class Untrained implements Mechanic /** @var ServerBag */ protected $serverBag; - /** @var string $hostAddress Address for Host */ - protected $hostAddress = '127.0.0.1'; + /** @var string Standard Loopback Address */ + protected $standardLoopback = '127.0.0.1'; + + /** @var string Alternative Loopback Address */ + protected $alternativeLoopback = '127.0.0.1'; /** * Untrained constructor. @@ -94,7 +97,7 @@ protected function iAmNotTrainedTo($activity) * * @return void */ - public function setupNetworking() + public function addAlternativeLoopbackAddress() { $this->iAmNotTrainedTo('set up special networking for Porter'); } @@ -104,18 +107,58 @@ public function setupNetworking() * * @return void */ - public function restoreNetworking() + public function removeAlternativeLoopbackAddress() { $this->iAmNotTrainedTo('restore special networking for Porter'); } /** - * Get Host IP for Porter. + * Get standard loopback address. + * + * @return string + */ + public function getStandardLoopback() + { + return $this->standardLoopback; + } + + /** + * Get alternative loopback address. + * + * @return string + */ + public function getAlternativeLoopback() + { + return $this->alternativeLoopback; + } + + /** + * Does a Porter domain resolve to the standard loopback address. + * + * @return bool + */ + public function isUsingAlternativeLoopback() + { + return $this->getPorterDomainIp() === $this->getAlternativeLoopback(); + } + + /** + * Does a Porter domain resolve to the standard loopback address? + * + * @return bool + */ + public function isUsingStandardLoopback() + { + return $this->getPorterDomainIp() === $this->getStandardLoopback(); + } + + /** + * Determine the working IP for Porter. * * @return string */ - public function getHostAddress() + public function getPorterDomainIp() { - return $this->hostAddress; + $this->iAmNotTrainedTo('obtain the current IP address for Porter'); } } diff --git a/resources/image_sets/konsulting/porter-ubuntu/config.json b/resources/image_sets/konsulting/porter-ubuntu/config.json index 85747fd..659fbb4 100644 --- a/resources/image_sets/konsulting/porter-ubuntu/config.json +++ b/resources/image_sets/konsulting/porter-ubuntu/config.json @@ -29,6 +29,7 @@ "mysql": "mysql:5.7", "redis": "redis:alpine", "dns": "andyshinn\/dnsmasq", - "mailhog": "mailhog\/mailhog:v1.0.0" + "mailhog": "mailhog\/mailhog:v1.0.0", + "ngrok": "wernight\/ngrok" } } \ No newline at end of file diff --git a/resources/image_sets/konsulting/porter-ubuntu/docker_compose/base.blade.php b/resources/image_sets/konsulting/porter-ubuntu/docker_compose/base.blade.php index 4bef1f5..bfddd3d 100644 --- a/resources/image_sets/konsulting/porter-ubuntu/docker_compose/base.blade.php +++ b/resources/image_sets/konsulting/porter-ubuntu/docker_compose/base.blade.php @@ -14,6 +14,8 @@ @include("{$imageSet->getName()}::node") + @include("{$imageSet->getName()}::ngrok") + @foreach($activePhpVersions as $key => $version) # PHP version {!! $version->version_number !!} diff --git a/resources/image_sets/konsulting/porter-ubuntu/docker_compose/ngrok.blade.php b/resources/image_sets/konsulting/porter-ubuntu/docker_compose/ngrok.blade.php new file mode 100644 index 0000000..51cae5d --- /dev/null +++ b/resources/image_sets/konsulting/porter-ubuntu/docker_compose/ngrok.blade.php @@ -0,0 +1,7 @@ + ngrok: + image: {{ $imageSet->firstByServiceName('ngrok')->getName() }} + ports: + - 4040:4040 + networks: + - porter + diff --git a/tests/Unit/Commands/Browser/OffTest.php b/tests/Unit/Commands/Browser/OffTest.php index 0ff729a..e1b9413 100644 --- a/tests/Unit/Commands/Browser/OffTest.php +++ b/tests/Unit/Commands/Browser/OffTest.php @@ -12,7 +12,7 @@ class OffTest extends BaseTestCase /** @test */ public function it_turns_the_browser_on() { - $this->porter->shouldReceive('turnOffService')->with('browser'); + $this->porter->shouldReceive('turnOffService')->with('browser')->once(); $this->artisan('browser:off'); } diff --git a/tests/Unit/Commands/Browser/OnTest.php b/tests/Unit/Commands/Browser/OnTest.php index c67136d..3cedcc6 100644 --- a/tests/Unit/Commands/Browser/OnTest.php +++ b/tests/Unit/Commands/Browser/OnTest.php @@ -12,7 +12,7 @@ class OnTest extends BaseTestCase /** @test */ public function it_turns_the_browser_on() { - $this->porter->shouldReceive('turnOnService')->with('browser'); + $this->porter->shouldReceive('turnOnService')->with('browser')->once(); $this->artisan('browser:on'); } diff --git a/tests/Unit/Commands/MakeFilesTest.php b/tests/Unit/Commands/MakeFilesTest.php index 6cc5287..76c0bd9 100644 --- a/tests/Unit/Commands/MakeFilesTest.php +++ b/tests/Unit/Commands/MakeFilesTest.php @@ -27,8 +27,8 @@ public function it_will_remake_the_files() factory(Site::class, 2)->create([]); - $this->porter->shouldReceive('isUp')->andReturn(false); - $this->porter->shouldReceive('compose'); + $this->porter->shouldReceive('isUp')->once()->andReturn(false); + $this->porter->shouldReceive('compose')->once(); $conf->shouldReceive('build')->twice(); $cert->shouldReceive('build')->with('porter_default')->once(); @@ -47,8 +47,8 @@ public function it_will_remake_the_files_and_restart_porter() factory(Site::class, 2)->create([]); - $this->porter->shouldReceive('isUp')->andReturn(true); - $this->porter->shouldReceive('compose'); + $this->porter->shouldReceive('isUp')->once()->andReturn(true); + $this->porter->shouldReceive('compose')->once(); $conf->shouldReceive('build')->twice(); $cert->shouldReceive('build')->with('porter_default')->once(); diff --git a/tests/Unit/Commands/MySql/OffTest.php b/tests/Unit/Commands/MySql/OffTest.php index db64a4e..cdf5b83 100644 --- a/tests/Unit/Commands/MySql/OffTest.php +++ b/tests/Unit/Commands/MySql/OffTest.php @@ -12,7 +12,7 @@ class OffTest extends BaseTestCase /** @test */ public function it_turns_the_browser_on() { - $this->porter->shouldReceive('turnOffService')->with('mysql'); + $this->porter->shouldReceive('turnOffService')->with('mysql')->once(); $this->artisan('mysql:off'); } diff --git a/tests/Unit/Commands/MySql/OnTest.php b/tests/Unit/Commands/MySql/OnTest.php index 1309641..33be069 100644 --- a/tests/Unit/Commands/MySql/OnTest.php +++ b/tests/Unit/Commands/MySql/OnTest.php @@ -12,7 +12,7 @@ class OnTest extends BaseTestCase /** @test */ public function it_turns_the_browser_on() { - $this->porter->shouldReceive('turnOnService')->with('mysql'); + $this->porter->shouldReceive('turnOnService')->with('mysql')->once(); $this->artisan('mysql:on'); } diff --git a/tests/Unit/Commands/MySql/OpenTest.php b/tests/Unit/Commands/MySql/OpenTest.php index f9e8a08..ad89b69 100644 --- a/tests/Unit/Commands/MySql/OpenTest.php +++ b/tests/Unit/Commands/MySql/OpenTest.php @@ -27,9 +27,9 @@ public function it_runs_mysql() $this->dockerCompose->shouldReceive('runContainer') ->andReturn($command = \Mockery::mock(CliCommand::class)); - $command->shouldReceive('append')->andReturn($command) - ->shouldReceive('interactive')->andReturn($command) - ->shouldReceive('perform'); + $command->shouldReceive('append')->andReturn($command)->once(); + $command->shouldReceive('interactive')->andReturn($command)->once(); + $command->shouldReceive('perform')->once(); $this->artisan('mysql:open'); } diff --git a/tests/Unit/Commands/Ngrok/OpenTest.php b/tests/Unit/Commands/Ngrok/OpenTest.php new file mode 100644 index 0000000..da02bce --- /dev/null +++ b/tests/Unit/Commands/Ngrok/OpenTest.php @@ -0,0 +1,150 @@ +mockDockerCompose(); + + parent::remakePorter(); + } + + /** @test */ + public function it_checks_if_the_site_exists() + { + Setting::updateOrCreate('home', __DIR__); + + $this->dockerCompose->shouldNotReceive('runContainer'); + + $this->artisan('ngrok', ['doesntexist']); + } + + /** @test */ + public function it_checks_that_the_alternative_loopback_is_being_used() + { + Setting::updateOrCreate('home', __DIR__); + $site = factory(Site::class)->create(); + + $this->mockUsingStandardLoopback(); + + $this->dockerCompose->shouldNotReceive('runContainer'); + + $this->artisan('ngrok', ['site' => $site->name]); + } + + /** @test */ + public function it_will_remove_ssl_and_replace_it() + { + Setting::updateOrCreate('home', __DIR__); + $site = factory(Site::class)->create(['secure' => true]); + + $this->mockUsingAlternativeLoopback(); + + $this->dockerCompose + // We are not bothered about all the functionality being passed through DockerCompose. + // So here we build a null command for all the other calls, this doesn't actually call DockerCompose. + ->shouldIgnoreMissing(new NullCliCommand(app(Cli::class), 'ps')) + + // Here we check that we do try to run a container, and when doing so, we want secure to be false. + ->shouldReceive('runContainer') + ->withArgs(function () use ($site) { + $this->assertSame(false, $site->fresh()->secure); + + return true; + }) + ->once() + ->andReturnSelf(); + + $this->artisan('ngrok', ['site' => $site->name]); + $this->assertSame(true, $site->secure); + } + + /** @test */ + public function it_will_run_ngrok() + { + Setting::updateOrCreate('home', __DIR__); + $site = factory(Site::class)->create(['secure' => false]); + + $this->mockUsingAlternativeLoopback(); + + // Check that the `stop ngrok` command is being run *before* the container run + $this->dockerCompose->shouldReceive('command')->with('stop ngrok') + ->andReturn($this->getNgrokStopCommandMock())->once()->ordered(); + + // Check that the ngrok container is being run + $this->dockerCompose + // We are not bothered about all the functionality being passed through DockerCompose. + // So here we build a null command for all the other calls, this doesn't actually call DockerCompose. + ->shouldIgnoreMissing(new NullCliCommand(app(Cli::class), 'ps')) + ->shouldReceive('runContainer') + ->with('ngrok') + ->once() + ->andReturn($command = \Mockery::mock(CliCommand::class)) + ->ordered(); + + $command->shouldReceive('append') + ->with("ngrok http -host-header=rewrite -region=eu -bind-tls=false -inspect=true {$site->url}:80") + ->andReturnSelf() + ->once(); + $command->shouldReceive('interactive')->withNoArgs()->andReturnSelf()->once(); + $command->shouldReceive('perform')->withNoArgs()->once(); + + // Check that the `stop ngrok` command is being run *after* the container run + $this->dockerCompose->shouldReceive('command')->with('stop ngrok') + ->andReturn($this->getNgrokStopCommandMock())->once()->ordered(); + + // Execute the command under test + $this->artisan('ngrok', ['site' => $site->name]); + } + + /** + * Get the mock for the `stop ngrok` CLI command. + * + * @return Mockery\MockInterface + */ + private function getNgrokStopCommandMock() + { + $stopCommand = \Mockery::mock(CliCommand::class); + $stopCommand->shouldReceive('realTime')->withNoArgs()->andReturnSelf()->once(); + $stopCommand->shouldReceive('perform')->withNoArgs()->once(); + + return $stopCommand; + } + + protected function mockUsingAlternativeLoopback() + { + $mechanicMock = Mockery::mock(Mechanic::class); + $mechanicMock + ->shouldIgnoreMissing() + ->shouldReceive('isUsingStandardLoopback')->withNoArgs()->andReturn(false)->once(); + + $this->app->instance(Mechanic::class, $mechanicMock); + $this->app->get(PorterLibrary::class)->setMechanic($mechanicMock); + } + + protected function mockUsingStandardLoopback() + { + $mechanicMock = Mockery::mock(Mechanic::class); + $mechanicMock + ->shouldIgnoreMissing() + ->shouldReceive('isUsingStandardLoopback')->withNoArgs()->andReturn(true)->once(); + + $this->app->instance(Mechanic::class, $mechanicMock); + $this->app->get(PorterLibrary::class)->setMechanic($mechanicMock); + } +} diff --git a/tests/Unit/Commands/SetHostTest.php b/tests/Unit/Commands/SetHostTest.php index 11dc859..4e3cd02 100644 --- a/tests/Unit/Commands/SetHostTest.php +++ b/tests/Unit/Commands/SetHostTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit\Commands; +use App\PorterLibrary; use App\Support\Dnsmasq\Config; use App\Support\Mechanics\Mechanic; use Mockery; @@ -13,12 +14,13 @@ class SetHostTest extends BaseTestCase public function it_sets_the_host() { $mechanicMock = Mockery::mock(Mechanic::class); - $mechanicMock->shouldReceive('setupNetworking')->withNoArgs()->once(); - $mechanicMock->shouldReceive('getHostAddress')->andReturn('1.1.1.1')->once(); + $mechanicMock->shouldReceive('addAlternativeLoopbackAddress')->withNoArgs()->once(); + $mechanicMock->shouldReceive('getAlternativeLoopback')->andReturn('1.1.1.1')->once(); // Mechanic is being fetched from the container more than once, so we need to bind a specific instance rather // than a closure $this->app->instance(Mechanic::class, $mechanicMock); + $this->app->get(PorterLibrary::class)->setMechanic($mechanicMock); $this->app->extend(Config::class, function () { return Mockery::mock(Config::class) @@ -33,9 +35,11 @@ public function it_sets_the_host() public function it_restores_the_host() { $mechanicMock = Mockery::mock(Mechanic::class); - $mechanicMock->shouldReceive('restoreNetworking')->withNoArgs()->once(); + $mechanicMock->shouldReceive('removeAlternativeLoopbackAddress')->withNoArgs()->once(); + $mechanicMock->shouldReceive('getStandardLoopback')->withNoArgs()->andReturn('127.0.0.1')->once(); $this->app->instance(Mechanic::class, $mechanicMock); + $this->app->get(PorterLibrary::class)->setMechanic($mechanicMock); $this->app->extend(Config::class, function () { return Mockery::mock(Config::class) diff --git a/tests/Unit/PorterLibraryTest.php b/tests/Unit/PorterLibraryTest.php index e95cce0..e1df8d1 100644 --- a/tests/Unit/PorterLibraryTest.php +++ b/tests/Unit/PorterLibraryTest.php @@ -164,4 +164,10 @@ public function it_wont_migrate_when_asked_not_to() $this->assertEquals($this->app[PorterLibrary::class], $lib); } + + /** @test */ + public function it_will_return_the_mechanic() + { + $this->assertInstanceOf(Mechanic::class, $this->makeLibrary('/Users/test/.porter')->getMechanic()); + } } diff --git a/tests/Unit/Support/Mechanics/MacOsTest.php b/tests/Unit/Support/Mechanics/MacOsTest.php index 3b5ce72..1a1c484 100644 --- a/tests/Unit/Support/Mechanics/MacOsTest.php +++ b/tests/Unit/Support/Mechanics/MacOsTest.php @@ -38,7 +38,7 @@ public function it_can_setup_networking() ->with('sudo ifconfig lo0 alias 10.200.10.1/24') ->once(); - $this->getMechanic()->setupNetworking(); + $this->getMechanic()->addAlternativeLoopbackAddress(); } /** @test */ @@ -50,12 +50,18 @@ public function it_can_restore_networking() ->with('sudo ifconfig lo0 -alias 10.200.10.1') ->once(); - $this->getMechanic()->restoreNetworking(); + $this->getMechanic()->removeAlternativeLoopbackAddress(); } /** @test */ - public function it_returns_the_host_address() + public function it_returns_the_alternative_loopback_address() { - $this->assertEquals('10.200.10.1', $this->getMechanic()->getHostAddress()); + $this->assertEquals('10.200.10.1', $this->getMechanic()->getAlternativeLoopback()); + } + + /** @test */ + public function it_returns_the_standard_loopback_address() + { + $this->assertEquals('127.0.0.1', $this->getMechanic()->getStandardLoopback()); } } diff --git a/tests/Unit/Support/Mechanics/MechanicTestCase.php b/tests/Unit/Support/Mechanics/MechanicTestCase.php index 55e56f3..429b75d 100644 --- a/tests/Unit/Support/Mechanics/MechanicTestCase.php +++ b/tests/Unit/Support/Mechanics/MechanicTestCase.php @@ -6,6 +6,7 @@ use App\Support\Console\ConsoleWriter; use App\Support\Console\ServerBag; use App\Support\Mechanics\MacOs; +use App\Support\Mechanics\Mechanic; use Tests\BaseTestCase; abstract class MechanicTestCase extends BaseTestCase @@ -22,6 +23,13 @@ public function setUp(): void $this->consoleWriter = \Mockery::mock(ConsoleWriter::class); } + /** + * Get a Mechanic. + * + * @param array $serverOverrides + * + * @return Mechanic + */ protected function getMechanic($serverOverrides = []) { return new $this->mechanicClass($this->cli, $this->consoleWriter, new ServerBag($serverOverrides)); diff --git a/tests/Unit/Support/Mechanics/UntrainedTest.php b/tests/Unit/Support/Mechanics/UntrainedTest.php index a2674b5..aefea9c 100644 --- a/tests/Unit/Support/Mechanics/UntrainedTest.php +++ b/tests/Unit/Support/Mechanics/UntrainedTest.php @@ -26,14 +26,14 @@ public function commandProvider() ['trustCertificate', ['']], ['getUserHomePath'], ['flushDns'], - ['setupNetworking'], - ['restoreNetworking'], + ['addAlternativeLoopbackAddress'], + ['removeAlternativeLoopbackAddress'], ]; } /** @test */ - public function it_returns_the_host_address() + public function it_returns_the_standard_loopback_address() { - $this->assertEquals('127.0.0.1', $this->getMechanic()->getHostAddress()); + $this->assertEquals('127.0.0.1', $this->getMechanic()->getStandardLoopback()); } }