From 21ce3cf71eba3fbad82682c913e9846b21789f41 Mon Sep 17 00:00:00 2001 From: Edie Lemoine Date: Mon, 18 Mar 2024 16:35:12 +0100 Subject: [PATCH] feat(installer): allow differentiating between migration types (#261) - Note: deprecations are now demoted to notice level --------- Co-authored-by: markernst97 --- config/pdk-dependencies.php | 15 +- config/pdk-template.php | 14 +- .../InstallationMigrationInterface.php | 12 + .../Contract/MigrationServiceInterface.php | 8 +- .../Contract/UpgradeMigrationInterface.php | 12 + .../Installer/Service/InstallerService.php | 131 ++++++++-- .../Installer/Service/MigrationService.php | 5 + src/Base/Concern/HasAttributes.php | 5 +- src/Facade/Logger.php | 7 +- src/Logger/AbstractLogger.php | 26 +- src/Logger/Contract/PdkLoggerInterface.php | 23 ++ .../MockInstallationMigration100.php | 42 ++++ .../Bootstrap/MockLegacyMigrationService.php | 22 ++ tests/Bootstrap/MockMigrationService.php | 18 +- tests/Bootstrap/MockPdkConfig.php | 3 + tests/Bootstrap/MockSettingsRepository.php | 9 +- ...ion110.php => MockUpgradeMigration110.php} | 10 +- ...ion120.php => MockUpgradeMigration120.php} | 15 +- tests/Bootstrap/MockUpgradeMigration130.php | 44 ++++ .../Service/InstallerServiceLegacyTest.php | 235 ++++++++++++++++++ .../Service/InstallerServiceTest.php | 152 +++++++---- .../Unit/Base/Model/ModelDeprecationTest.php | 9 +- tests/Unit/Logger/LoggerTest.php | 27 ++ 23 files changed, 748 insertions(+), 96 deletions(-) create mode 100644 src/App/Installer/Contract/InstallationMigrationInterface.php create mode 100644 src/App/Installer/Contract/UpgradeMigrationInterface.php create mode 100644 src/Logger/Contract/PdkLoggerInterface.php create mode 100644 tests/Bootstrap/MockInstallationMigration100.php create mode 100644 tests/Bootstrap/MockLegacyMigrationService.php rename tests/Bootstrap/{MockMigration110.php => MockUpgradeMigration110.php} (76%) rename tests/Bootstrap/{MockMigration120.php => MockUpgradeMigration120.php} (59%) create mode 100644 tests/Bootstrap/MockUpgradeMigration130.php create mode 100644 tests/Unit/App/Installer/Service/InstallerServiceLegacyTest.php diff --git a/config/pdk-dependencies.php b/config/pdk-dependencies.php index a097abdaf..58837d121 100644 --- a/config/pdk-dependencies.php +++ b/config/pdk-dependencies.php @@ -8,12 +8,25 @@ use function DI\value; return [ + /** + * The current version of the pdk according to the composer.json file. + */ 'pdkVersion' => factory(function (FileSystemInterface $fileSystem): string { - $composerJson = json_decode($fileSystem->get(__DIR__ . '/../composer.json'), true); + $rootDir = Pdk::get('rootDir'); + $composerJson = json_decode($fileSystem->get("$rootDir/composer.json"), true); return $composerJson['version']; }), + /** + * The next major version of the pdk. Used for deprecation messages. + */ + 'pdkNextMajorVersion' => factory(function (): string { + $version = Pdk::get('pdkVersion'); + + return (int) explode('.', $version)[0] + 1 . '.0.0'; + }), + /** * The minimum PHP version required to run the app. */ diff --git a/config/pdk-template.php b/config/pdk-template.php index 39c06c6f0..4759ec741 100644 --- a/config/pdk-template.php +++ b/config/pdk-template.php @@ -15,9 +15,11 @@ use MyParcelNL\Pdk\Base\Contract\CronServiceInterface; use MyParcelNL\Pdk\Frontend\Contract\ViewServiceInterface; use MyParcelNL\Pdk\Language\Contract\LanguageServiceInterface; +use MyParcelNL\Pdk\Logger\Contract\PdkLoggerInterface; use MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface; use Psr\Log\LoggerInterface; use function DI\autowire; +use function DI\get; use function DI\value; /** @@ -135,10 +137,18 @@ /** * Handles logging. * - * @see \MyParcelNL\Pdk\Logger\AbstractLogger + * @see \MyParcelNL\Pdk\Logger\AbstractLogger + * @deprecated Will be removed in v3.0.0. Use PdkLoggerInterface instead. */ - LoggerInterface::class => autowire(), + LoggerInterface::class => autowire(), + + /** + * Handles logging. + * + * @see \MyParcelNL\Pdk\Logger\AbstractLogger + */ + PdkLoggerInterface::class => get(LoggerInterface::class), /** * Exposes frontend api url and endpoints. diff --git a/src/App/Installer/Contract/InstallationMigrationInterface.php b/src/App/Installer/Contract/InstallationMigrationInterface.php new file mode 100644 index 000000000..ebe8d02ce --- /dev/null +++ b/src/App/Installer/Contract/InstallationMigrationInterface.php @@ -0,0 +1,12 @@ +> + * @return class-string<\MyParcelNL\Pdk\App\Installer\Contract\MigrationInterface>[] + * @deprecated Will be removed in v3.0.0. Implement getUpgradeMigrations() and getInstallationMigrations() instead + * @todo remove in v3.0.0 */ public function all(): array; } diff --git a/src/App/Installer/Contract/UpgradeMigrationInterface.php b/src/App/Installer/Contract/UpgradeMigrationInterface.php new file mode 100644 index 000000000..80798fe60 --- /dev/null +++ b/src/App/Installer/Contract/UpgradeMigrationInterface.php @@ -0,0 +1,12 @@ +executeUninstallation(...$args); - $this->migrateDown(); $this->updateInstalledVersion(null); } } @@ -92,6 +91,7 @@ public function uninstall(...$args): void protected function executeInstallation(...$args): void { $this->setDefaultSettings(); + $this->migrateInstall(); } /** @@ -101,6 +101,7 @@ protected function executeInstallation(...$args): void */ protected function executeUninstallation(...$args): void { + $this->migrateUninstall(); } /** @@ -116,13 +117,20 @@ protected function getInstalledVersion(): ?string */ protected function migrateDown(): void { - $this->getMigrations() - ->filter(function (MigrationInterface $migration) { - return version_compare($migration->getVersion(), $this->getInstalledVersion(), '<='); - }) - ->each(function (MigrationInterface $migration) { - $migration->down(); - }); + $this->runDownMigrations( + $this->getUpgradeMigrations() + ->filter(function (MigrationInterface $migration) { + return version_compare($migration->getVersion(), $this->getInstalledVersion(), '<='); + }) + ); + } + + /** + * @return void + */ + protected function migrateInstall(): void + { + $this->runUpMigrations($this->getInstallationMigrations()); } /** @@ -132,14 +140,7 @@ protected function migrateDown(): void */ protected function migrateUp(string $version): void { - $this->getMigrations() - ->filter(function (MigrationInterface $migration) use ($version) { - return version_compare($migration->getVersion(), $this->getInstalledVersion(), '>') - && version_compare($migration->getVersion(), $version, '<='); - }) - ->each(function (MigrationInterface $migration) { - $migration->up(); - }); + $this->runUpMigrations($this->getUpgradeMigrations($version)); } /** @@ -163,13 +164,105 @@ protected function updateInstalledVersion(?string $version): void } /** - * @return \MyParcelNL\Pdk\Base\Support\Collection + * @template T of \MyParcelNL\Pdk\App\Installer\Contract\MigrationInterface + * @param array $migrations + * + * @return \MyParcelNL\Pdk\Base\Support\Collection */ - private function getMigrations(): Collection + private function createMigrationCollection(array $migrations): Collection { - return Collection::make($this->migrationService->all()) + return Collection::make($migrations) ->map(function (string $className) { return Pdk::get($className); }); } + + /** + * @return \MyParcelNL\Pdk\Base\Support\Collection<\MyParcelNL\Pdk\App\Installer\Contract\InstallationMigrationInterface> + * @todo v3.0.0 remove legacy support + */ + private function getInstallationMigrations(): Collection + { + if (! method_exists($this->migrationService, 'getInstallationMigrations')) { + Logger::deprecated( + sprintf('Method "%s::all()"', MigrationServiceInterface::class), + 'getUpgradeMigrations and getInstallationMigrations' + ); + + return new Collection(); + } + + return $this->createMigrationCollection($this->migrationService->getInstallationMigrations()); + } + + /** + * @param null|string $version + * + * @return \MyParcelNL\Pdk\Base\Support\Collection<\MyParcelNL\Pdk\App\Installer\Contract\UpgradeMigrationInterface> + * @todo v3.0.0 remove legacy support + */ + private function getUpgradeMigrations(?string $version = null): Collection + { + $useLegacy = ! method_exists($this->migrationService, 'getUpgradeMigrations'); + + if ($useLegacy) { + Logger::deprecated( + sprintf('Method "%s::all()"', MigrationServiceInterface::class), + 'getUpgradeMigrations and getInstallationMigrations' + ); + } + + $migrations = $useLegacy + ? $this->migrationService->all() + : $this->migrationService->getUpgradeMigrations(); + + $collection = $this->createMigrationCollection($migrations); + + if (! $version) { + return $collection; + } + + return $collection->filter(function (MigrationInterface $migration) use ($version) { + return version_compare($migration->getVersion(), $this->getInstalledVersion(), '>') + && version_compare($migration->getVersion(), $version, '<='); + }); + } + + private function migrateUninstall(): void + { + $this->migrateDown(); + $this->runDownMigrations($this->getInstallationMigrations()); + } + + /** + * @param \MyParcelNL\Pdk\Base\Support\Collection $migrations + * + * @return void + */ + private function runDownMigrations(Collection $migrations): void + { + $migrations + ->sort(function (MigrationInterface $a, MigrationInterface $b) { + return version_compare($b->getVersion(), $a->getVersion()); + }) + ->each(function (MigrationInterface $migration) { + $migration->down(); + }); + } + + /** + * @param \MyParcelNL\Pdk\Base\Support\Collection $migrations + * + * @return void + */ + private function runUpMigrations(Collection $migrations): void + { + $migrations + ->sort(function (MigrationInterface $a, MigrationInterface $b) { + return version_compare($a->getVersion(), $b->getVersion()); + }) + ->each(function (MigrationInterface $migration) { + $migration->up(); + }); + } } diff --git a/src/App/Installer/Service/MigrationService.php b/src/App/Installer/Service/MigrationService.php index 830e401c4..79b0724a0 100644 --- a/src/App/Installer/Service/MigrationService.php +++ b/src/App/Installer/Service/MigrationService.php @@ -8,6 +8,11 @@ class MigrationService implements MigrationServiceInterface { + /** + * @return class-string<\MyParcelNL\Pdk\App\Installer\Contract\MigrationInterface>[] + * @deprecated use getUpgradeMigrations() instead + * @todo remove in v3.0.0 + */ public function all(): array { return []; diff --git a/src/Base/Concern/HasAttributes.php b/src/Base/Concern/HasAttributes.php index 840831ba1..fb6d5bdd1 100644 --- a/src/Base/Concern/HasAttributes.php +++ b/src/Base/Concern/HasAttributes.php @@ -744,10 +744,7 @@ protected function isGuarded(string $key): bool */ protected function logDeprecationWarning(string $key, string $newKey): void { - Logger::warning( - "[DEPRECATION] Attribute '$key' is deprecated. Use '$newKey' instead.", - ['class' => static::class] - ); + Logger::deprecated("Attribute '$key'", "'$newKey'", ['class' => static::class]); } /** diff --git a/src/Facade/Logger.php b/src/Facade/Logger.php index 6298cd003..460ed741c 100644 --- a/src/Facade/Logger.php +++ b/src/Facade/Logger.php @@ -6,7 +6,7 @@ namespace MyParcelNL\Pdk\Facade; use MyParcelNL\Pdk\Base\Facade; -use Psr\Log\LoggerInterface; +use MyParcelNL\Pdk\Logger\Contract\PdkLoggerInterface; /** * @method static void log($level, $message, array $context = []) @@ -18,7 +18,8 @@ * @method static void info($message, array $context = []) * @method static void notice($message, array $context = []) * @method static void warning($message, array $context = []) - * @see \Psr\Log\LoggerInterface + * @method static void deprecated(string $subject, string $replacement = null, array $context = []) + * @see \MyParcelNL\Pdk\Logger\Contract\PdkLoggerInterface */ final class Logger extends Facade { @@ -27,6 +28,6 @@ final class Logger extends Facade */ protected static function getFacadeAccessor(): string { - return LoggerInterface::class; + return PdkLoggerInterface::class; } } diff --git a/src/Logger/AbstractLogger.php b/src/Logger/AbstractLogger.php index 4bc9e977d..4dc32571a 100644 --- a/src/Logger/AbstractLogger.php +++ b/src/Logger/AbstractLogger.php @@ -4,10 +4,11 @@ namespace MyParcelNL\Pdk\Logger; -use Psr\Log\LoggerInterface; +use MyParcelNL\Pdk\Facade\Pdk; +use MyParcelNL\Pdk\Logger\Contract\PdkLoggerInterface; use Psr\Log\LogLevel; -abstract class AbstractLogger implements LoggerInterface +abstract class AbstractLogger implements PdkLoggerInterface { /** * @param $level @@ -51,6 +52,27 @@ public function debug($message, array $context = []): void $this->createLog(LogLevel::DEBUG, $message, $context); } + /** + * @param string $subject + * @param null|string $replacement + * @param array $context + * + * @return void + */ + public function deprecated(string $subject, ?string $replacement = null, array $context = []): void + { + $message = "[DEPRECATED] $subject is deprecated."; + + if ($replacement) { + $message .= " Use $replacement instead."; + } + + $version = Pdk::get('pdkNextMajorVersion'); + $message .= " Will be removed in $version."; + + $this->notice($message, $context); + } + /** * @param string $message * @param array $context diff --git a/src/Logger/Contract/PdkLoggerInterface.php b/src/Logger/Contract/PdkLoggerInterface.php new file mode 100644 index 000000000..2fd8cc900 --- /dev/null +++ b/src/Logger/Contract/PdkLoggerInterface.php @@ -0,0 +1,23 @@ +settingsRepository = $settingsRepository; + } + + public function down(): void + { + $this->settingsRepository->store(Pdk::get('createSettingsKey')(self::KEY_PARCEL_WEIGHT), 200); + $this->settingsRepository->store(Pdk::get('createSettingsKey')(self::KEY_MAILBOX_WEIGHT), 100); + } + + public function getVersion(): string + { + return '1.0.0'; + } + + public function up(): void + { + $this->settingsRepository->store(Pdk::get('createSettingsKey')(self::KEY_PARCEL_WEIGHT), 300); + $this->settingsRepository->store(Pdk::get('createSettingsKey')(self::KEY_MAILBOX_WEIGHT), 200); + } +} diff --git a/tests/Bootstrap/MockLegacyMigrationService.php b/tests/Bootstrap/MockLegacyMigrationService.php new file mode 100644 index 000000000..48ae7e582 --- /dev/null +++ b/tests/Bootstrap/MockLegacyMigrationService.php @@ -0,0 +1,22 @@ + get(MockFrontendEndpointService::class), InstallerServiceInterface::class => get(MockInstallerService::class), LanguageServiceInterface::class => get(MockLanguageService::class), + /** + * @todo v3.0.0 use PdkLoggerInterface. Leave it for now to test backwards compatibility. :) + */ LoggerInterface::class => get(MockLogger::class), MigrationServiceInterface::class => get(MockMigrationService::class), OrderStatusServiceInterface::class => get(MockOrderStatusService::class), diff --git a/tests/Bootstrap/MockSettingsRepository.php b/tests/Bootstrap/MockSettingsRepository.php index 8bab3c1d7..867b99b0c 100644 --- a/tests/Bootstrap/MockSettingsRepository.php +++ b/tests/Bootstrap/MockSettingsRepository.php @@ -4,8 +4,8 @@ namespace MyParcelNL\Pdk\Tests\Bootstrap; -use MyParcelNL\Pdk\App\Webhook\Service\AbstractPdkWebhookService; use MyParcelNL\Pdk\Base\Support\Arr; +use MyParcelNL\Pdk\Facade\Pdk; use MyParcelNL\Pdk\Settings\Repository\AbstractPdkSettingsRepository; use MyParcelNL\Pdk\Storage\MemoryCacheStorage; @@ -46,7 +46,12 @@ public function getGroup(string $namespace) */ public function reset(): void { - $this->settings = []; + /** @var string $installedVersionKey */ + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + + $this->settings = [ + $installedVersionKey => null, + ]; } /** diff --git a/tests/Bootstrap/MockMigration110.php b/tests/Bootstrap/MockUpgradeMigration110.php similarity index 76% rename from tests/Bootstrap/MockMigration110.php rename to tests/Bootstrap/MockUpgradeMigration110.php index d0db9aee9..63b0d31e4 100644 --- a/tests/Bootstrap/MockMigration110.php +++ b/tests/Bootstrap/MockUpgradeMigration110.php @@ -4,14 +4,13 @@ namespace MyParcelNL\Pdk\Tests\Bootstrap; -use MyParcelNL\Pdk\App\Installer\Contract\MigrationInterface; +use MyParcelNL\Pdk\App\Installer\Contract\UpgradeMigrationInterface; use MyParcelNL\Pdk\Facade\Pdk; use MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface; -use MyParcelNL\Pdk\Settings\Model\LabelSettings; -class MockMigration110 implements MigrationInterface +class MockUpgradeMigration110 implements UpgradeMigrationInterface { - private const SETTING_KEY = LabelSettings::ID . '.' . LabelSettings::DESCRIPTION; + private const SETTING_KEY = 'label.description'; /** * @var \MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface @@ -38,9 +37,6 @@ public function up(): void $this->settingsRepository->store($this->getSettingKey(), 'new-description'); } - /** - * @return string - */ private function getSettingKey(): string { return Pdk::get('createSettingsKey')(self::SETTING_KEY); diff --git a/tests/Bootstrap/MockMigration120.php b/tests/Bootstrap/MockUpgradeMigration120.php similarity index 59% rename from tests/Bootstrap/MockMigration120.php rename to tests/Bootstrap/MockUpgradeMigration120.php index df611d4b8..42190c5ee 100644 --- a/tests/Bootstrap/MockMigration120.php +++ b/tests/Bootstrap/MockUpgradeMigration120.php @@ -5,12 +5,12 @@ namespace MyParcelNL\Pdk\Tests\Bootstrap; use MyParcelNL\Pdk\App\Installer\Contract\MigrationInterface; +use MyParcelNL\Pdk\Facade\Pdk; use MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface; -use MyParcelNL\Pdk\Settings\Model\AccountSettings; -class MockMigration120 implements MigrationInterface +class MockUpgradeMigration120 implements MigrationInterface { - private const SETTING_KEY = AccountSettings::ID . '.' . AccountSettings::API_KEY; + private const SETTING_KEY = 'order.barcodeInNoteTitle'; /** * @var \MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface @@ -24,7 +24,7 @@ public function __construct(PdkSettingsRepositoryInterface $settingsRepository) public function down(): void { - $this->settingsRepository->store(self::SETTING_KEY, 'old-api-key'); + $this->settingsRepository->store($this->getSettingKey(), 'old-barcode-in-note'); } public function getVersion(): string @@ -34,6 +34,11 @@ public function getVersion(): string public function up(): void { - $this->settingsRepository->store(self::SETTING_KEY, 'new-api-key'); + $this->settingsRepository->store($this->getSettingKey(), 'new-barcode-in-note'); + } + + private function getSettingKey(): string + { + return Pdk::get('createSettingsKey')(self::SETTING_KEY); } } diff --git a/tests/Bootstrap/MockUpgradeMigration130.php b/tests/Bootstrap/MockUpgradeMigration130.php new file mode 100644 index 000000000..6c77a70be --- /dev/null +++ b/tests/Bootstrap/MockUpgradeMigration130.php @@ -0,0 +1,44 @@ +settingsRepository = $settingsRepository; + } + + public function down(): void + { + $this->settingsRepository->store($this->getSettingKey(), 300); + } + + public function getVersion(): string + { + return '1.3.0'; + } + + public function up(): void + { + $this->settingsRepository->store($this->getSettingKey(), 400); + } + + private function getSettingKey(): string + { + return Pdk::get('createSettingsKey')(self::SETTING_KEY); + } +} diff --git a/tests/Unit/App/Installer/Service/InstallerServiceLegacyTest.php b/tests/Unit/App/Installer/Service/InstallerServiceLegacyTest.php new file mode 100644 index 000000000..1cda5dbd2 --- /dev/null +++ b/tests/Unit/App/Installer/Service/InstallerServiceLegacyTest.php @@ -0,0 +1,235 @@ + value(Platform::SENDMYPARCEL_NAME), + 'appInfo' => factory(function (): AppInfo { + return new AppInfo([ + 'name' => 'test', + 'version' => '1.3.0', + ]); + }), + + 'defaultSettings' => value([ + CheckoutSettings::ID => [ + // Default value of 'pickupLocationsDefaultView' comes from the Platform . + CheckoutSettings::DELIVERY_OPTIONS_HEADER => 'default', + ], + ]), + + MigrationServiceInterface::class => get(MockLegacyMigrationService::class), + ]) +); + +afterEach(function () { + /** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockSettingsRepository $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + + $settingsRepository->reset(); +}); + +function expectSettingsToContainLegacy(array $values): void +{ + /** @var \MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $settings = $settingsRepository->all(); + + expect(Arr::dot($settings->toArray()))->toHaveKeysAndValues($values); +} + +it('[legacy] performs a fresh install of the app, filling default values from platform and config', function () { + /** @var PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + + // Remove the installed version from the settings: + $settingsRepository->store($installedVersionKey, null); + + expect($settingsRepository->get($installedVersionKey))->toBe(null); + + Installer::install(); + + expect($settingsRepository->get($installedVersionKey)) + ->toEqual('1.3.0'); + + expectSettingsToContainLegacy([ + /** From default settings */ + 'checkout.deliveryOptionsHeader' => 'default', + 'checkout.pickupLocationsDefaultView' => 'map', + + /** + * Expect 1.2.0 migration to not have run (as it's only in the upgrade migrations) + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration120 + */ + 'order.barcodeInNoteTitle' => null, + ]); +}); + +it('[legacy] upgrades app to new version', function () { + /** @var PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + + // Set the installed version to 1.1.0: + $settingsRepository->store($installedVersionKey, '1.1.0'); + expect($settingsRepository->get($installedVersionKey))->toEqual('1.1.0'); + + Installer::install(); + + expect($settingsRepository->get($installedVersionKey))->toEqual('1.3.0'); + + expectSettingsToContainLegacy([ + /** + * Expect 1.1.0 migration to not have run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration110 + * */ + 'label.description' => null, + + /** + * Expect 1.2.0 migration to have run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration120 + */ + 'order.barcodeInNoteTitle' => 'new-barcode-in-note', + + /** + * Expect 1.3.0 migration to have been run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration130 + */ + 'order.emptyMailboxWeight' => 400, + ]); +}); + +it('[legacy] runs down migrations on uninstall', function () { + /** @var PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + $createSettingsKey = Pdk::get('createSettingsKey'); + + $settingsRepository->store($installedVersionKey, '1.3.0'); + $settingsRepository->store($createSettingsKey('account.apiKey'), '12345'); + + expect($settingsRepository->get($installedVersionKey)) + ->toEqual('1.3.0'); + + Installer::uninstall(); + + expect($settingsRepository->get($installedVersionKey)) + ->toEqual(null); + + expectSettingsToContainLegacy([ + /** + * Expect 1.1.0 migration to have been reversed + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration110 + */ + 'label.description' => 'old-description', + + /** + * Expect 1.2.0 migration to have been reversed + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration120 + */ + 'order.barcodeInNoteTitle' => 'old-barcode-in-note', + ]); +}); + +it('[legacy] passes through arbitrary arguments', function ($_, array $result) { + /** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockLogger $logger */ + $logger = Pdk::get(LoggerInterface::class); + + expect($logger->getLogs())->toContain($result); +})->with([ + 'install' => [ + 'method' => function () { + Installer::install('appelboom', 12345); + }, + 'result' => [ + 'level' => 'debug', + 'message' => '[PDK]: install arguments', + 'context' => ['appelboom', 12345], + ], + ], + + 'uninstall' => [ + 'method' => function () { + /** @var \MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + + $settingsRepository->store($installedVersionKey, '1.3.0'); + + Installer::uninstall(12, 'peer'); + }, + 'result' => [ + 'level' => 'debug', + 'message' => '[PDK]: uninstall arguments', + 'context' => [12, 'peer'], + ], + ], +]); + +it('[legacy] does not install if version is equal', function () { + /** @var PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + $createSettingsKey = Pdk::get('createSettingsKey'); + + $settingsRepository->store($installedVersionKey, '1.3.0'); + $settingsRepository->store($createSettingsKey('label.description'), 'description'); + + Installer::install(); + + expect($settingsRepository->get($installedVersionKey)) + ->toEqual('1.3.0') + ->and($settingsRepository->get($createSettingsKey('label.description'))) + ->toBe('description'); +}); + +it('[legacy] does not uninstall if is not installed', function () { + /** @var PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + $createSettingsKey = Pdk::get('createSettingsKey'); + + // Set the installed version to null: + $settingsRepository->store($installedVersionKey, null); + $settingsRepository->store($createSettingsKey('label.description'), 'description'); + + Installer::uninstall(); + + expect($settingsRepository->get($installedVersionKey)) + ->toEqual(null) + ->and($settingsRepository->get($createSettingsKey('label.description'))) + ->toBe('description'); +}); diff --git a/tests/Unit/App/Installer/Service/InstallerServiceTest.php b/tests/Unit/App/Installer/Service/InstallerServiceTest.php index cad09f0e8..9e55b98b9 100644 --- a/tests/Unit/App/Installer/Service/InstallerServiceTest.php +++ b/tests/Unit/App/Installer/Service/InstallerServiceTest.php @@ -24,7 +24,7 @@ 'appInfo' => factory(function (): AppInfo { return new AppInfo([ 'name' => 'test', - 'version' => '1.2.0', + 'version' => '1.3.0', ]); }), @@ -37,58 +37,101 @@ ]) ); +afterEach(function () { + /** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockSettingsRepository $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + + $settingsRepository->reset(); +}); + +function expectSettingsToContain(array $values): void +{ + /** @var \MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $settings = $settingsRepository->all(); + + expect(Arr::dot($settings->toArray()))->toHaveKeysAndValues($values); +} + it('performs a fresh install of the app, filling default values from platform and config', function () { /** @var PdkSettingsRepositoryInterface $settingsRepository */ $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); - $createSettingsKey = Pdk::get('createSettingsKey'); // Remove the installed version from the settings: $settingsRepository->store($installedVersionKey, null); - expect($settingsRepository->get($installedVersionKey)) - ->toEqual(null) - ->and($settingsRepository->get($createSettingsKey('checkout.deliveryOptionsHeader'))) - ->toBe(null) - ->and($settingsRepository->get($createSettingsKey('checkout.pickupLocationsDefaultView'))) - ->toBe(null); + expect($settingsRepository->get($installedVersionKey))->toBe(null); Installer::install(); expect($settingsRepository->get($installedVersionKey)) - ->toEqual('1.2.0') - ->and($settingsRepository->get($createSettingsKey('checkout.deliveryOptionsHeader'))) - ->toBe('default') - ->and($settingsRepository->get($createSettingsKey('checkout.pickupLocationsDefaultView'))) - ->toBe('map'); + ->toEqual('1.3.0'); + + expectSettingsToContain([ + /** From default settings */ + 'checkout.deliveryOptionsHeader' => 'default', + 'checkout.pickupLocationsDefaultView' => 'map', + + /** + * Expect installation migration to have run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockInstallationMigration100 + */ + 'order.emptyParcelWeight' => 300, + 'order.emptyMailboxWeight' => 200, + + /** + * Expect 1.2.0 migration to not have run (as it's only in the upgrade migrations) + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration120 + */ + 'order.barcodeInNoteTitle' => null, + ]); }); it('upgrades app to new version', function () { /** @var PdkSettingsRepositoryInterface $settingsRepository */ $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); - $createSettingsKey = Pdk::get('createSettingsKey'); // Set the installed version to 1.1.0: $settingsRepository->store($installedVersionKey, '1.1.0'); - - expect($settingsRepository->get($installedVersionKey)) - ->toEqual('1.1.0') - ->and($settingsRepository->get($createSettingsKey('label.description'))) - ->toBe(null) - ->and($settingsRepository->get($createSettingsKey('account.apiKey'))) - ->toBe(null); + expect($settingsRepository->get($installedVersionKey))->toEqual('1.1.0'); Installer::install(); - expect($settingsRepository->get($installedVersionKey)) - ->toEqual('1.2.0') - // Expect 1.1.0 migration to not have run - ->and($settingsRepository->get('label.description')) - ->toBe(null) - // Expect 1.2.0 migration to have run - ->and($settingsRepository->get('account.apiKey')) - ->toBe('new-api-key'); + expect($settingsRepository->get($installedVersionKey))->toEqual('1.3.0'); + + expectSettingsToContain([ + /** + * Expect installation migration not to have run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockInstallationMigration100 + */ + 'order.emptyParcelWeight' => null, + + /** + * Expect 1.1.0 migration to not have run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration110 + * */ + 'label.description' => null, + + /** + * Expect 1.2.0 migration to have run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration120 + */ + 'order.barcodeInNoteTitle' => 'new-barcode-in-note', + + /** + * Expect 1.3.0 migration to have been run + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration130 + */ + 'order.emptyMailboxWeight' => 400, + ]); }); it('runs down migrations on uninstall', function () { @@ -97,30 +140,48 @@ $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); $createSettingsKey = Pdk::get('createSettingsKey'); - // Set the installed version to 1.1.0: - $settingsRepository->store($installedVersionKey, '1.1.0'); + $settingsRepository->store($installedVersionKey, '1.3.0'); $settingsRepository->store($createSettingsKey('account.apiKey'), '12345'); expect($settingsRepository->get($installedVersionKey)) - ->toEqual('1.1.0'); + ->toEqual('1.3.0'); Installer::uninstall(); expect($settingsRepository->get($installedVersionKey)) - ->toEqual(null) - // Expect 1.1.0 migration to have run - ->and($settingsRepository->get($createSettingsKey('label.description'))) - ->toBe('old-description') - // Expect 1.2.0 migration to not have run - ->and($settingsRepository->get($createSettingsKey('account.apiKey'))) - ->toBe('12345'); + ->toEqual(null); + + expectSettingsToContain([ + /** + * Expect installation migration to have been reversed + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockInstallationMigration100 + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration130 + */ + 'order.emptyParcelWeight' => 200, + 'order.emptyMailboxWeight' => 100, + + /** + * Expect 1.1.0 migration to have been reversed + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration110 + */ + 'label.description' => 'old-description', + + /** + * Expect 1.2.0 migration to have been reversed + * + * @see \MyParcelNL\Pdk\Tests\Bootstrap\MockUpgradeMigration120 + */ + 'order.barcodeInNoteTitle' => 'old-barcode-in-note', + ]); }); it('passes through arbitrary arguments', function ($_, array $result) { /** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockLogger $logger */ $logger = Pdk::get(LoggerInterface::class); - expect(Arr::last($logger->getLogs()))->toBe($result); + expect($logger->getLogs())->toContain($result); })->with([ 'install' => [ 'method' => function () { @@ -135,6 +196,12 @@ 'uninstall' => [ 'method' => function () { + /** @var \MyParcelNL\Pdk\Settings\Contract\PdkSettingsRepositoryInterface $settingsRepository */ + $settingsRepository = Pdk::get(PdkSettingsRepositoryInterface::class); + $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); + + $settingsRepository->store($installedVersionKey, '1.3.0'); + Installer::uninstall(12, 'peer'); }, 'result' => [ @@ -151,14 +218,13 @@ $installedVersionKey = Pdk::get('settingKeyInstalledVersion'); $createSettingsKey = Pdk::get('createSettingsKey'); - // Set the installed version to 1.2.0: - $settingsRepository->store($installedVersionKey, '1.2.0'); + $settingsRepository->store($installedVersionKey, '1.3.0'); $settingsRepository->store($createSettingsKey('label.description'), 'description'); Installer::install(); expect($settingsRepository->get($installedVersionKey)) - ->toEqual('1.2.0') + ->toEqual('1.3.0') ->and($settingsRepository->get($createSettingsKey('label.description'))) ->toBe('description'); }); diff --git a/tests/Unit/Base/Model/ModelDeprecationTest.php b/tests/Unit/Base/Model/ModelDeprecationTest.php index 09ed21783..e53c70850 100644 --- a/tests/Unit/Base/Model/ModelDeprecationTest.php +++ b/tests/Unit/Base/Model/ModelDeprecationTest.php @@ -21,6 +21,11 @@ /** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockLogger $logger */ $logger = Pdk::get(LoggerInterface::class); + /** + * @var string $nextVersion + */ + $nextVersion = Pdk::get('pdkNextMajorVersion'); + expect($model->property) ->toEqual('nice') ->and($model->broccoli) @@ -28,8 +33,8 @@ ->and($logger->getLogs()) ->toEqual([ [ - 'level' => 'warning', - 'message' => "[PDK]: [DEPRECATION] Attribute 'broccoli' is deprecated. Use 'property' instead.", + 'level' => 'notice', + 'message' => "[PDK]: [DEPRECATED] Attribute 'broccoli' is deprecated. Use 'property' instead. Will be removed in $nextVersion.", 'context' => [ 'class' => MockCastModel::class, ], diff --git a/tests/Unit/Logger/LoggerTest.php b/tests/Unit/Logger/LoggerTest.php index 6c23e1a2c..5a76dd46c 100644 --- a/tests/Unit/Logger/LoggerTest.php +++ b/tests/Unit/Logger/LoggerTest.php @@ -5,7 +5,9 @@ namespace MyParcelNL\Pdk\Logger; +use MyParcelNL\Pdk\Base\FileSystemInterface; use MyParcelNL\Pdk\Facade\Logger; +use MyParcelNL\Pdk\Facade\Pdk as PdkFacade; use MyParcelNL\Pdk\Tests\Uses\UsesEachMockPdkInstance; use function MyParcelNL\Pdk\Tests\usesShared; @@ -56,3 +58,28 @@ 'message' => 'The world has ended', ], ]); + +it('logs deprecations', function (string $currentVersion, string $nextVersion) { + /** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockFileSystem $fileSystem */ + $fileSystem = PdkFacade::get(FileSystemInterface::class); + $rootDir = PdkFacade::get('rootDir'); + + $fileSystem->put($rootDir . '/composer.json', json_encode([ + 'name' => 'myparcelnl/pdk', + 'version' => $currentVersion, + ])); + + Logger::deprecated('old', 'new', ['additional' => 'information']); + + expect(Logger::getLogs())->toBe([ + [ + 'level' => 'notice', + 'message' => "[PDK]: [DEPRECATED] old is deprecated. Use new instead. Will be removed in $nextVersion.", + 'context' => ['additional' => 'information'], + ], + ]); +})->with([ + ['1.0.0', '2.0.0'], + ['2.1.3', '3.0.0'], + ['5555.33312.1231245392', '5556.0.0'], +]);