Skip to content

Commit

Permalink
feat: aligned behavior using build
Browse files Browse the repository at this point in the history
  • Loading branch information
luislard committed Apr 28, 2024
1 parent 7538809 commit 99bc6c0
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/Container/PackageProxyContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ private function tryContainer(): bool

/** TODO: We need a better way to deal with status checking besides equality */
if (
$this->package->statusIs(Package::STATUS_READY)
$this->package->statusIs(Package::STATUS_INITIALIZED)
|| $this->package->statusIs(Package::STATUS_READY)
|| $this->package->statusIs(Package::STATUS_BOOTED)
) {
$this->container = $this->package->container();
Expand All @@ -88,7 +89,6 @@ private function assertPackageBooted(string $id): void
if ($this->tryContainer()) {
return;
}

$name = $this->package->name();
$status = $this->package->statusIs(Package::STATUS_FAILED)
? 'is errored'
Expand Down
18 changes: 13 additions & 5 deletions src/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,14 @@ public function connect(Package $package): bool
throw new \Exception($error, 0, $this->lastError);
}

// Don't connect, if already booted or boot failed
// Don't connect, if status is failed
$failed = $this->statusIs(self::STATUS_FAILED);
if ($failed || $this->statusIs(self::STATUS_BOOTED)) {
$status = $failed ? 'errored' : 'booted';
// Don't allow connect if current package is already built
if ($failed
|| $this->statusIs(self::STATUS_INITIALIZED) // built
|| $this->statusIs(self::STATUS_BOOTED) // booted
) {
$status = $failed ? 'errored' : 'built_or_booted';
$error = "{$errorMessage} to a {$status} package.";
do_action(
$this->hookName(self::ACTION_FAILED_CONNECTION),
Expand All @@ -330,14 +334,15 @@ static function () use ($package): Properties {
}
);

// If the other package is booted, we can obtain a container, otherwise
// If the other package is built or booted, we can obtain a container, otherwise
// we build a proxy container
$container = $package->statusIs(self::STATUS_BOOTED)
|| $package->statusIs(self::STATUS_INITIALIZED)
// || $package->statusIs(self::STATUS_READY)
? $package->container()
: new PackageProxyContainer($package);

$this->containerConfigurator->addContainer($container);

do_action(
$this->hookName(self::ACTION_PACKAGE_CONNECTED),
$packageName,
Expand All @@ -361,6 +366,9 @@ static function () use ($package): Properties {
*/
public function build(): Package
{
if ($this->built) {
return $this;
}
try {
// Don't allow building the application multiple times.
$this->assertStatus(self::STATUS_IDLE, 'build package');
Expand Down
205 changes: 205 additions & 0 deletions tests/unit/PackageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ public function testBasic(): void
static::assertEmpty($package->modulesStatus()[Package::MODULES_ALL]);
}

/**
* @test
*/
public function testBasicUsingBuild(): void
{
$expectedName = 'foo';
$propertiesStub = $this->mockProperties($expectedName);

$package = Package::new($propertiesStub);

static::assertTrue($package->statusIs(Package::STATUS_IDLE));
static::assertInstanceOf(Package::class, $package->build());
static::assertTrue($package->statusIs(Package::STATUS_INITIALIZED));
static::assertSame($expectedName, $package->name());
static::assertInstanceOf(Properties::class, $package->properties());
static::assertInstanceOf(ContainerInterface::class, $package->container());
static::assertEmpty($package->modulesStatus()[Package::MODULES_ALL]);
}

/**
* @param string $suffix
* @param string $baseName
Expand Down Expand Up @@ -105,6 +124,32 @@ public function testBootWithEmptyModule(): void
static::assertFalse($package->boot());
}

/**
* @test
*/
public function testBuildWithEmptyModule(): void
{
$expectedId = 'my-module';

$moduleStub = $this->mockModule($expectedId);
$propertiesStub = $this->mockProperties('name', false);

$package = Package::new($propertiesStub)->addModule($moduleStub);

static::assertInstanceOf(Package::class, $package->build());
static::assertTrue($package->moduleIs($expectedId, Package::MODULE_NOT_ADDED));
static::assertFalse($package->moduleIs($expectedId, Package::MODULE_REGISTERED));
static::assertFalse($package->moduleIs($expectedId, Package::MODULE_REGISTERED_FACTORIES));
static::assertFalse($package->moduleIs($expectedId, Package::MODULE_EXTENDED));
static::assertFalse($package->moduleIs($expectedId, Package::MODULE_ADDED));

// build again should keep the status of the package.
static::assertTrue($package->statusIs( Package::STATUS_INITIALIZED));
$package->build();
static::assertTrue($package->statusIs( Package::STATUS_INITIALIZED));

}

/**
* @test
*/
Expand All @@ -127,6 +172,28 @@ public function testBootWithServiceModule(): void
static::assertTrue($package->container()->has($serviceId));
}

/**
* @test
*/
public function testBootWithServiceModuleUsingBuild(): void
{
$moduleId = 'my-service-module';
$serviceId = 'service-id';

$module = $this->mockModule($moduleId, ServiceModule::class);
$module->expects('services')->andReturn($this->stubServices($serviceId));

$package = Package::new($this->mockProperties())->addModule($module);

static::assertInstanceOf(Package::class, $package->build());
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_NOT_ADDED));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_REGISTERED));
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_REGISTERED_FACTORIES));
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_EXTENDED));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_ADDED));
static::assertTrue($package->container()->has($serviceId));
}

/**
* @test
*/
Expand All @@ -149,6 +216,28 @@ public function testBootWithFactoryModule(): void
static::assertTrue($package->container()->has($factoryId));
}

/**
* @test
*/
public function testBootWithFactoryModuleUsingBuild(): void
{
$moduleId = 'my-factory-module';
$factoryId = 'factory-id';

$module = $this->mockModule($moduleId, FactoryModule::class);
$module->expects('factories')->andReturn($this->stubServices($factoryId));

$package = Package::new($this->mockProperties())->addModule($module);

static::assertInstanceOf(Package::class, $package->build());
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_NOT_ADDED));
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_REGISTERED));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_REGISTERED_FACTORIES));
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_EXTENDED));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_ADDED));
static::assertTrue($package->container()->has($factoryId));
}

/**
* @test
*/
Expand All @@ -172,6 +261,29 @@ public function testBootWithExtendingModuleWithNonExistingService(): void
static::assertFalse($package->container()->has($extensionId));
}

/**
* @test
*/
public function testBuildWithExtendingModuleWithNonExistingService(): void
{
$moduleId = 'my-extension-module';
$extensionId = 'extension-id';

$module = $this->mockModule($moduleId, ExtendingModule::class);
$module->expects('extensions')->andReturn($this->stubServices($extensionId));

$package = Package::new($this->mockProperties())->addModule($module);

static::assertInstanceOf(Package::class, $package->build());
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_NOT_ADDED));
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_REGISTERED));
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_REGISTERED_FACTORIES));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_EXTENDED));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_ADDED));
// false because extending a service not in container
static::assertFalse($package->container()->has($extensionId));
}

/**
* @test
*/
Expand All @@ -195,6 +307,29 @@ public function testBootWithExtendingModuleWithExistingService(): void
static::assertTrue($package->container()->has($serviceId));
}

/**
* @test
*/
public function testBuildWithExtendingModuleWithExistingService(): void
{
$moduleId = 'my-extension-module';
$serviceId = 'service-id';

$module = $this->mockModule($moduleId, ServiceModule::class, ExtendingModule::class);
$module->expects('services')->andReturn($this->stubServices($serviceId));
$module->expects('extensions')->andReturn($this->stubServices($serviceId));

$package = Package::new($this->mockProperties())->addModule($module);

static::assertInstanceOf(Package::class, $package->build());
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_NOT_ADDED));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_REGISTERED));
static::assertFalse($package->moduleIs($moduleId, Package::MODULE_REGISTERED_FACTORIES));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_EXTENDED));
static::assertTrue($package->moduleIs($moduleId, Package::MODULE_ADDED));
static::assertTrue($package->container()->has($serviceId));
}

/**
* @test
*/
Expand Down Expand Up @@ -497,6 +632,38 @@ public function testPackageConnection(): void
static::assertInstanceOf(\ArrayObject::class, $package2->container()->get('service_1'));
}

/**
* Test we can connect services across packages.
*
* @test
*/
public function testPackageConnectionWhenOnePackageIsBuiltAndTheOtherDont(): void
{
$module1 = $this->mockModule('module_1', ServiceModule::class);
$module1->expects('services')->andReturn($this->stubServices('service_1'));
$package1 = Package::new($this->mockProperties('package_1', false))
->addModule($module1);

$module2 = $this->mockModule('module_2', ServiceModule::class);
$module2->expects('services')->andReturn($this->stubServices('service_2'));
$package2 = Package::new($this->mockProperties('package_2', false))
->addModule($module2);

Monkey\Actions\expectDone($package2->hookName(Package::ACTION_PACKAGE_CONNECTED))
->once()
->with($package1->name(), Package::STATUS_IDLE, false);

$package1->build();

$connected = $package2->connect($package1);
$package2->build();

static::assertTrue($connected);
static::assertSame(['package_1' => true], $package2->connectedPackages());
// retrieve a Package 1's service from Package 2's container.
static::assertInstanceOf(\ArrayObject::class, $package2->container()->get('service_1'));
}

/**
* Test we can not connect services when the package how call connect is booted.
*
Expand Down Expand Up @@ -535,6 +702,44 @@ function (\Throwable $throwable): void {
static::assertSame(['package_1' => false], $package2->connectedPackages());
}

/**
* Test we can not connect services when the package how call connect is built.
*
* @test
*/
public function testPackageConnectionFailsIfBuiltWithDebugOff(): void
{
$module1 = $this->mockModule('module_1', ServiceModule::class);
$module1->expects('services')->andReturn($this->stubServices('service_1'));
$package1 = Package::new($this->mockProperties('package_1', false))
->addModule($module1);

$module2 = $this->mockModule('module_2', ServiceModule::class);
$module2->expects('services')->andReturn($this->stubServices('service_2'));
$package2 = Package::new($this->mockProperties('package_2', false))
->addModule($module2);

$package1->build();
$package2->build();

Monkey\Actions\expectDone($package2->hookName(Package::ACTION_FAILED_CONNECTION))
->once()
->with($package1->name(), \Mockery::type(\WP_Error::class));

Monkey\Actions\expectDone($package2->hookName(Package::ACTION_FAILED_BUILD))
->once()
->whenHappen(
function (\Throwable $throwable): void {
$this->assertThrowableMessageMatches($throwable, 'failed connect.+?built');
}
);

$connected = $package2->connect($package1);

static::assertFalse($connected);
static::assertSame(['package_1' => false], $package2->connectedPackages());
}

/**
* Test we can not connect services when the package how call connect is booted.
*
Expand Down

0 comments on commit 99bc6c0

Please sign in to comment.