diff --git a/.allowed-licenses.php b/.allowed-licenses.php new file mode 100644 index 0000000..3b28e83 --- /dev/null +++ b/.allowed-licenses.php @@ -0,0 +1,14 @@ +addLicenses( + // And other licenses you wish to allow. + 'MIT', + 'Apache-2.0', + 'BSD-3-Clause', + ) + ->build(); \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..f9de590 --- /dev/null +++ b/.env @@ -0,0 +1,18 @@ +# In all environments, the following files are loaded if they exist, +# the latter taking precedence over the former: +# +# * .env contains default values for the environment variables needed by the app +# * .env.local uncommitted file with local overrides +# * .env.$APP_ENV committed environment-specific defaults +# * .env.$APP_ENV.local uncommitted environment-specific overrides +# +# Real environment variables win over .env files. +# +# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. +# +# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). +# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration +DATABASE_HOST= +DATABASE_USER= +DATABASE_PASSWORD= +DATABASE_NAME= \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/config.yml rename to .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml new file mode 100644 index 0000000..f628913 --- /dev/null +++ b/.github/workflows/license-check.yml @@ -0,0 +1,50 @@ +name: "Allowed licenses checks" +on: + push: + pull_request: + +jobs: + static-analysis: + name: "composer-license-checker" + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - "8.3" + dependencies: [ highest ] + operating-system: [ ubuntu-latest] + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + extensions: json, bcmath, curl, intl, mbstring + tools: composer:v2 + + - name: "Install lowest dependencies" + if: ${{ matrix.dependencies == 'lowest' }} + run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest" + + - name: "Install highest dependencies" + if: ${{ matrix.dependencies == 'highest' }} + run: "composer update --no-interaction --no-progress --no-suggest" + + - name: "composer-license-checker" + run: "make lint-allowed-licenses" + + - name: "is allowed licenses check succeeded" + if: ${{ success() }} + run: | + echo '✅ allowed licenses check pass, congratulations!' + + - name: "is allowed licenses check failed" + if: ${{ failure() }} + run: | + echo '::error:: ❗️ allowed licenses check failed (╯°益°)╯彡┻━┻' \ No newline at end of file diff --git a/.github/workflows/lint-cs-fixer.yml b/.github/workflows/lint-cs-fixer.yml new file mode 100644 index 0000000..6d2fd84 --- /dev/null +++ b/.github/workflows/lint-cs-fixer.yml @@ -0,0 +1,51 @@ +on: + push: + pull_request: + +name: Lint CS-Fixer + +jobs: + static-analysis: + name: "CS-Fixer" + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - "8.3" + dependencies: [ highest ] + operating-system: [ ubuntu-latest] + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + extensions: json, bcmath, curl, intl, mbstring + tools: composer:v2 + + - name: "Install lowest dependencies" + if: ${{ matrix.dependencies == 'lowest' }} + run: "composer update --prefer-lowest --no-interaction --no-progress --no-suggest" + + - name: "Install highest dependencies" + if: ${{ matrix.dependencies == 'highest' }} + run: "composer update --no-interaction --no-progress --no-suggest" + + - name: "CS-Fixer" + run: "vendor/bin/php-cs-fixer fix --dry-run --diff --verbose" + + - name: "is CS-Fixer check succeeded" + if: ${{ success() }} + run: | + echo '✅ CS-Fixer check pass, congratulations!' + + - name: "is CS-Fixer check failed" + if: ${{ failure() }} + run: | + echo '::error:: ❗️ CS-Fixer check failed (╯°益°)╯彡┻━┻' \ No newline at end of file diff --git a/.github/workflows/phpstan.yml b/.github/workflows/lint-phpstan.yml similarity index 95% rename from .github/workflows/phpstan.yml rename to .github/workflows/lint-phpstan.yml index 03760bc..9cf8ab2 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/lint-phpstan.yml @@ -38,7 +38,7 @@ jobs: run: "composer update --no-interaction --no-progress --no-suggest" - name: "PHPStan" - run: "make lint-phpstan" + run: "vendor/bin/phpstan --memory-limit=2G analyse" - name: "is PHPStan check succeeded" if: ${{ success() }} diff --git a/.github/workflows/rector.yml b/.github/workflows/lint-rector.yml similarity index 96% rename from .github/workflows/rector.yml rename to .github/workflows/lint-rector.yml index 4a0bf8a..ec35b52 100644 --- a/.github/workflows/rector.yml +++ b/.github/workflows/lint-rector.yml @@ -38,7 +38,7 @@ jobs: run: "composer update --no-interaction --no-progress --no-suggest" - name: "Rector" - run: "make lint-rector" + run: "vendor/bin/rector process --dry-run" - name: "is Rector check succeeded" if: ${{ success() }} diff --git a/.github/workflows/tests-functional.yml b/.github/workflows/tests-functional.yml new file mode 100644 index 0000000..b17a851 --- /dev/null +++ b/.github/workflows/tests-functional.yml @@ -0,0 +1,86 @@ +name: "Functional tests" + +on: + push: + pull_request: + +env: + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress" + DATABASE_HOST: localhost + DATABASE_USER: b24phpLibTest + DATABASE_PASSWORD: b24phpLibTest + DATABASE_NAME: b24phpLibTest + +jobs: + tests: + name: "Functional tests" + + runs-on: ${{ matrix.operating-system }} + + strategy: + fail-fast: false + matrix: + php-version: + - "8.3" + dependencies: [ highest ] + operating-system: [ ubuntu-latest ] + services: + bitrix24-php-lib-test-database: + image: postgres:16-alpine + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -U b24phpLibTest" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + env: + POSTGRES_USER: b24phpLibTest + POSTGRES_PASSWORD: b24phpLibTest + POSTGRES_DB: b24phpLibTest + + steps: + - name: "Checkout code" + uses: "actions/checkout@v2" + + - name: "Setup PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + extensions: json, bcmath, curl, intl, mbstring, pdo_pgsql, pdo + + - name: "Install dependencies with Composer" + run: | + composer update ${{ env.COMPOSER_FLAGS }} + + - name: "Install PostgreSQL client" + run: | + sudo apt-get update + sudo apt-get install -y postgresql-client + + - name: "Wait for PostgreSQL to be ready" + run: | + until pg_isready -h localhost -p 5432 -U b24phpLibTest; do + echo "Waiting for PostgreSQL to start..." + sleep 2 + done + + - name: "Run functional tests" + run: | + php bin/doctrine orm:schema-tool:drop --force + php bin/doctrine orm:schema-tool:create --dump-sql + php bin/doctrine orm:schema-tool:update --force + php bin/doctrine orm:info + # Запуск тестов с очисткой состояния между тестами + php vendor/bin/phpunit --testsuite=functional_tests --display-warnings --testdox --process-isolation + + - name: "is functional tests succeeded" + if: ${{ success() }} + run: | + echo '✅ functional tests pass, congratulations!' + + - name: "is functional tests failed" + if: ${{ failure() }} + run: | + echo '::error:: ❗️ functional tests failed (╯°益°)╯彡┻━┻' diff --git a/.github/workflows/phpunit.yml b/.github/workflows/tests-unit.yml similarity index 86% rename from .github/workflows/phpunit.yml rename to .github/workflows/tests-unit.yml index 32152d5..c714207 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/tests-unit.yml @@ -1,11 +1,11 @@ -name: "PHPUnit tests" +name: "Unit tests" on: push: pull_request: env: - COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist" + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress" jobs: tests: @@ -37,7 +37,7 @@ jobs: composer update ${{ env.COMPOSER_FLAGS }} - name: "run unit tests" - run: "make test-unit" + run: "php vendor/bin/phpunit --testsuite=unit_tests --display-warnings --testdox" - name: "is unit tests tests succeeded" if: ${{ success() }} diff --git a/.gitignore b/.gitignore index 66c3dc1..b5709f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ /.idea* /vendor /.cache +/docker/db composer.phar composer.lock .phpunit.result.cache *.log -.env.local \ No newline at end of file +.env.local +*.cache \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..3508ea9 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,20 @@ +in(__DIR__.'/src'); + +return (new Config()) + ->setParallelConfig(ParallelConfigFactory::detect()) + ->setCacheFile(__DIR__.'/.php-cs-fixer.cache') + ->setFinder($finder) + ->setRules([ + '@Symfony' => true, + '@DoctrineAnnotation' => true, + '@PhpCsFixer' => true, + 'phpdoc_to_comment' => false, + ]); diff --git a/Makefile b/Makefile index a1f253a..5927f0a 100644 --- a/Makefile +++ b/Makefile @@ -4,22 +4,123 @@ # # For the full copyright and license information, please view the MIT-LICENSE.txt # file that was distributed with this source code. +#!/usr/bin/env make + +export COMPOSE_HTTP_TIMEOUT=120 +export DOCKER_CLIENT_TIMEOUT=120 + +# load default and personal env-variables +ENV := $(PWD)/.env +ENV_LOCAL := $(PWD)/.env.local +include $(ENV) +-include $(ENV_LOCAL) + + +start-rector: vendor + vendor/bin/rector process tests --config=rector.php + +coding-standards: vendor + vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --diff --verbose default: @echo "make needs target:" @egrep -e '^\S+' ./Makefile | grep -v default | sed -r 's/://' | sed -r 's/^/ - /' +%: + @: # silence + +# Rule to print all environment variables for debugging +debug-print-env: + @echo "DATABASE_HOST=$(DATABASE_HOST)" + @echo "DATABASE_NAME=$(DATABASE_NAME)" + @echo "DATABASE_USER=$(DATABASE_USER)" + @echo "DATABASE_PASSWORD=$(DATABASE_PASSWORD)" + +init: + @echo "remove all containers" + docker-compose down --remove-orphans + @echo "build containers" + docker-compose build + @echo "install dependencies" + docker-compose run --rm php-cli composer install + @echo "change owner of var folder for access from container" + docker-compose run --rm php-cli chown -R www-data:www-data /var/www/html/var/ + @echo "run application…" + docker-compose up -d + + +clear: + docker-compose run --rm php-cli composer clear-cache + +up: + @echo "run application…" + docker-compose up --build -d + +down: + @echo "stop application and remove containers" + docker-compose down --remove-orphans + +down-clear: + @echo "stop application and remove containers with volumes" + docker-compose down -v --remove-orphans + +restart: down up + +# работа с контейнерами +php-cli-bash: + docker-compose run --rm php-cli sh $(filter-out $@,$(MAKECMDGOALS)) + +# работа с composer +composer-install: + @echo "install dependencies…" + docker-compose run --rm php-cli composer install + +composer-update: + @echo "update dependencies…" + docker-compose run --rm php-cli composer update + +composer-dumpautoload: + docker-compose run --rm php-cli composer dumpautoload +# вызов composer с любыми параметрами +# Примеры: +# make composer install +# make composer "install --no-dev" +composer: + docker-compose run --rm php-cli composer $(filter-out $@,$(MAKECMDGOALS)) + +# check allowed licenses +lint-allowed-licenses: + vendor/bin/composer-license-checker # linters lint-phpstan: - vendor/bin/phpstan --memory-limit=1G analyse + docker-compose run --rm php-cli php vendor/bin/phpstan analyse --memory-limit 2G lint-rector: - vendor/bin/rector process --dry-run + docker-compose run --rm php-cli php vendor/bin/rector process --dry-run lint-rector-fix: - vendor/bin/rector process + docker-compose run --rm php-cli php vendor/bin/rector process +lint-cs-fixer: + docker-compose run --rm php-cli php vendor/bin/php-cs-fixer fix --dry-run --diff --verbose +lint-cs-fixer-fix: + docker-compose run --rm php-cli php vendor/bin/php-cs-fixer fix --diff --verbose + +# unit-tests +test-run-unit: + docker-compose run --rm php-cli php vendor/bin/phpunit --testsuite=unit_tests --display-warnings --testdox + +# functional-tests, work with test database +test-run-functional: debug-print-env + docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:drop --force + docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:create + docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:update --dump-sql + docker-compose run --rm php-cli php vendor/bin/phpunit --testsuite=functional_tests --display-warnings --testdox + +# Запустить один функциональный тест с дебагером +run-one-functional-test: debug-print-env + docker-compose run --rm php-cli php -dxdebug.start_with_request=yes vendor/bin/phpunit --filter 'testChangeDomainUrlWithHappyPath' tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php + +schema-drop: + docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:drop --force -# unit tests -test-unit: - vendor/bin/phpunit --testsuite unit_tests --display-warnings +schema-create: + docker-compose run --rm php-cli php bin/doctrine orm:schema-tool:create -test-integration: - vendor/bin/phpunit --testsuite integration_tests --display-warnings \ No newline at end of file diff --git a/bin/doctrine b/bin/doctrine new file mode 100644 index 0000000..cf20dbe --- /dev/null +++ b/bin/doctrine @@ -0,0 +1,12 @@ +#!/usr/bin/env php + 'pdo_pgsql', + 'memory' => true, + 'dbname' => $_ENV['DATABASE_NAME'], + ]); + +return DependencyFactory::fromConnection($config, new ExistingConnection($conn)); \ No newline at end of file diff --git a/config/xml/Bitrix24.Lib.Bitrix24Accounts.Entity.Bitrix24Account.dcm.xml b/config/xml/Bitrix24.Lib.Bitrix24Accounts.Entity.Bitrix24Account.dcm.xml new file mode 100644 index 0000000..c2e57d9 --- /dev/null +++ b/config/xml/Bitrix24.Lib.Bitrix24Accounts.Entity.Bitrix24Account.dcm.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/xml/Bitrix24.SDK.Core.Credentials.AuthToken.dcm.xml b/config/xml/Bitrix24.SDK.Core.Credentials.AuthToken.dcm.xml new file mode 100644 index 0000000..b229a88 --- /dev/null +++ b/config/xml/Bitrix24.SDK.Core.Credentials.AuthToken.dcm.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/config/xml/Bitrix24.SDK.Core.Credentials.Scope.dcm.xml b/config/xml/Bitrix24.SDK.Core.Credentials.Scope.dcm.xml new file mode 100644 index 0000000..0df218b --- /dev/null +++ b/config/xml/Bitrix24.SDK.Core.Credentials.Scope.dcm.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..fbdff87 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,28 @@ +services: + php-cli: + build: + context: ./docker/php-cli + depends_on: + - database + volumes: + - .:/var/www/html + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + PHP_IDE_CONFIG: "serverName=docker-cli" + working_dir: /var/www/html + database: + image: postgres:${POSTGRES_VERSION:-16}-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-b24phpLibTest} + # You should definitely change the password in production + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-b24phpLibTest} + POSTGRES_USER: ${POSTGRES_USER:-b24phpLibTest} + PGDATA: "/var/lib/postgresql/data/pgdata" + container_name: bitrix24-php-lib-test-database + ports: + - '5438:5432' + volumes: + - ./docker/init_database/:/docker-entrypoint-initdb.d + - ./docker/db:/var/lib/postgresql/data diff --git a/docker/php-cli/Dockerfile b/docker/php-cli/Dockerfile new file mode 100644 index 0000000..a154dfd --- /dev/null +++ b/docker/php-cli/Dockerfile @@ -0,0 +1,17 @@ +FROM php:8.3-cli-alpine + +RUN apk add unzip libpq-dev git icu-dev autoconf build-base linux-headers \ + && docker-php-ext-install bcmath pdo pdo_pgsql intl \ + && docker-php-ext-enable bcmath pdo pdo_pgsql intl + +RUN pecl install xdebug \ + && docker-php-ext-enable xdebug \ + && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo "xdebug.client_host = host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo "xdebug.client_port = 9003" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + +RUN git config --global --add safe.directory /var/www/html + +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/bin --filename=composer --quiet + +ENV COMPOSER_ALLOW_SUPERUSER=1 diff --git a/migrations.php b/migrations.php new file mode 100644 index 0000000..73e85a8 --- /dev/null +++ b/migrations.php @@ -0,0 +1,20 @@ + [ + 'table_name' => 'b24AccountsDoctrineMigrationVersions', + 'version_column_name' => 'version', + 'version_column_length' => 1024, + 'executed_at_column_name' => 'executed_at', + 'execution_time_column_name' => 'execution_time', + ], + 'migrations_paths' => [ + 'B24io\Core\Bitrix24\Account\Migrations' => '/migrations/', + ], + 'all_or_nothing' => true, + 'transactional' => true, + 'check_database_platform' => true, + 'organize_migrations' => 'none', + 'connection' => null, + 'em' => null, +]; \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0f572c3..fee6376 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,8 +10,8 @@ ./tests/Unit - - ./tests/Integration + + ./tests/Functional diff --git a/src/AggregateRoot.php b/src/AggregateRoot.php new file mode 100644 index 0000000..abb2be6 --- /dev/null +++ b/src/AggregateRoot.php @@ -0,0 +1,19 @@ +events; + $this->events = []; + + return $events; + } +} diff --git a/src/Bitrix24Accounts/Entity/Bitrix24Account.php b/src/Bitrix24Accounts/Entity/Bitrix24Account.php index 527f5b1..616f75d 100644 --- a/src/Bitrix24Accounts/Entity/Bitrix24Account.php +++ b/src/Bitrix24Accounts/Entity/Bitrix24Account.php @@ -1,4 +1,5 @@ accessToken = $authToken->getAccessToken(); - $this->refreshToken = $authToken->getRefreshToken(); - $this->expires = $authToken->getExpires(); - $this->applicationScope = $applicationScope->getScopeCodes(); + private CarbonImmutable $updatedAt, + private int $applicationVersion, + private Scope $applicationScope, + bool $isEmitBitrix24AccountCreatedEvent = false, + ) { + $this->addAccountCreatedEventIfNeeded($isEmitBitrix24AccountCreatedEvent); } - #[Override] + #[\Override] public function getId(): Uuid { return $this->id; } - #[Override] + #[\Override] public function getBitrix24UserId(): int { return $this->bitrix24UserId; } - #[Override] + #[\Override] public function isBitrix24UserAdmin(): bool { return $this->isBitrix24UserAdmin; } - #[Override] + #[\Override] public function getMemberId(): string { return $this->memberId; } - #[Override] + #[\Override] public function getDomainUrl(): string { return $this->domainUrl; } - #[Override] + #[\Override] public function getStatus(): Bitrix24AccountStatus { - return $this->accountStatus; + return $this->status; } - #[Override] + /** + * @throws InvalidArgumentException + */ + #[\Override] public function getAuthToken(): AuthToken { - return new AuthToken($this->accessToken, $this->refreshToken, $this->expires); + return $this->authToken; } /** * @throws InvalidArgumentException */ - #[Override] + #[\Override] public function renewAuthToken(RenewedAuthToken $renewedAuthToken): void { if ($this->memberId !== $renewedAuthToken->memberId) { @@ -117,118 +116,114 @@ public function renewAuthToken(RenewedAuthToken $renewedAuthToken): void ); } - $this->accessToken = $renewedAuthToken->authToken->getAccessToken(); - $this->refreshToken = $renewedAuthToken->authToken->getRefreshToken(); - $this->expires = $renewedAuthToken->authToken->getExpires(); + $this->authToken = new AuthToken( + $renewedAuthToken->authToken->accessToken, + $renewedAuthToken->authToken->refreshToken, + $renewedAuthToken->authToken->expires + ); + $this->updatedAt = new CarbonImmutable(); } - #[Override] + #[\Override] public function getApplicationVersion(): int { return $this->applicationVersion; } - /** - * @throws UnknownScopeCodeException - */ - #[Override] + #[\Override] public function getApplicationScope(): Scope { - return new Scope($this->applicationScope); + return $this->applicationScope; } /** * @throws InvalidArgumentException */ - #[Override] + #[\Override] public function changeDomainUrl(string $newDomainUrl): void { - if ($newDomainUrl === '') { + if ('' === $newDomainUrl) { throw new InvalidArgumentException('new domain url cannot be empty'); } - if (Bitrix24AccountStatus::blocked === $this->accountStatus || Bitrix24AccountStatus::deleted === $this->accountStatus) { + if (Bitrix24AccountStatus::blocked === $this->status || Bitrix24AccountStatus::deleted === $this->status) { throw new InvalidArgumentException( sprintf( 'bitrix24 account %s for domain %s must be in active or new state, now account in %s state. domain url cannot be changed', $this->id->toRfc4122(), $this->domainUrl, - $this->accountStatus->name + $this->status->name ) ); } $this->domainUrl = $newDomainUrl; $this->updatedAt = new CarbonImmutable(); + $this->events[] = new Bitrix24AccountDomainUrlChangedEvent( + $this->id, + new CarbonImmutable() + ); } /** * @throws InvalidArgumentException */ - #[Override] + #[\Override] public function applicationInstalled(string $applicationToken): void { - if (Bitrix24AccountStatus::new !== $this->accountStatus) { - throw new InvalidArgumentException(sprintf( - 'for finish installation bitrix24 account must be in status «new», current status - «%s»', - $this->accountStatus->name)); + if (Bitrix24AccountStatus::new !== $this->status) { + throw new InvalidArgumentException( + sprintf( + 'for finish installation bitrix24 account must be in status «new», current status - «%s»', + $this->status->name + ) + ); } - if ($applicationToken === '') { + if ('' === $applicationToken) { throw new InvalidArgumentException('application token cannot be empty'); } - $this->accountStatus = Bitrix24AccountStatus::active; + $this->status = Bitrix24AccountStatus::active; $this->applicationToken = $applicationToken; $this->updatedAt = new CarbonImmutable(); + $this->events[] = new Bitrix24AccountApplicationInstalledEvent( + $this->id, + new CarbonImmutable() + ); } /** * @throws InvalidArgumentException */ - #[Override] + #[\Override] public function applicationUninstalled(string $applicationToken): void { - if ($applicationToken === '') { - throw new InvalidArgumentException('application token cannot be empty'); - } - - if (Bitrix24AccountStatus::active !== $this->accountStatus) { - throw new InvalidArgumentException(sprintf( - 'for uninstall account must be in status «active», current status - «%s»', - $this->accountStatus->name)); - } - - if ($this->applicationToken !== $applicationToken) { - throw new InvalidArgumentException( - sprintf( - 'application token «%s» mismatch with application token «%s» for bitrix24 account %s for domain %s', - $applicationToken, - $this->applicationToken, - $this->id->toRfc4122(), - $this->domainUrl - ) - ); - } - - $this->accountStatus = Bitrix24AccountStatus::deleted; + $this->guardEmptyToken($applicationToken); + $this->guardTokenMismatch($applicationToken); + $this->guardApplicationIsActive(); + $this->status = Bitrix24AccountStatus::deleted; $this->updatedAt = new CarbonImmutable(); + $this->events[] = new Bitrix24AccountApplicationUninstalledEvent( + $this->id, + new CarbonImmutable() + ); } - #[Override] + #[\Override] public function isApplicationTokenValid(string $applicationToken): bool { return $this->applicationToken === $applicationToken; } - #[Override] + #[\Override] public function getCreatedAt(): CarbonImmutable { return $this->createdAt; } - #[Override] + #[\Override] public function getUpdatedAt(): CarbonImmutable { return $this->updatedAt; @@ -237,41 +232,53 @@ public function getUpdatedAt(): CarbonImmutable /** * @throws InvalidArgumentException */ - #[Override] - public function updateApplicationVersion(int $version, ?Scope $newScope): void + #[\Override] + public function updateApplicationVersion(AuthToken $authToken, int $b24UserId, int $version, ?Scope $newScope): void { - if (Bitrix24AccountStatus::active !== $this->accountStatus) { - throw new InvalidArgumentException(sprintf('account must be in status «active», but now account in status «%s»', $this->accountStatus->name)); + if (Bitrix24AccountStatus::active !== $this->status) { + throw new InvalidArgumentException( + sprintf('account must be in status «active», but now account in status «%s»', $this->status->name) + ); } if ($this->applicationVersion >= $version) { throw new InvalidArgumentException( - sprintf('you cannot downgrade application version or set some version, current version «%s», but you try to upgrade to «%s»', + sprintf( + 'you cannot downgrade application version or set some version, current version «%s», but you try to upgrade to «%s»', $this->applicationVersion, - $version)); + $version + ) + ); } $this->applicationVersion = $version; - if ($newScope instanceof \Bitrix24\SDK\Core\Credentials\Scope) { - $this->applicationScope = $newScope->getScopeCodes(); + if ($newScope instanceof Scope) { + $this->applicationScope = $newScope; } $this->updatedAt = new CarbonImmutable(); + $this->events[] = new Bitrix24AccountApplicationVersionUpdatedEvent( + $this->id, + new CarbonImmutable() + ); } /** * @throws InvalidArgumentException */ - #[Override] + #[\Override] public function markAsActive(?string $comment): void { - if (Bitrix24AccountStatus::blocked !== $this->accountStatus) { + if (Bitrix24AccountStatus::blocked !== $this->status) { throw new InvalidArgumentException( - sprintf('you can activate account only in status blocked, now account in status %s', - $this->accountStatus->name)); + sprintf( + 'you can activate account only in status «blocked», now account in status «%s»', + $this->status->name + ) + ); } - $this->accountStatus = Bitrix24AccountStatus::active; + $this->status = Bitrix24AccountStatus::active; $this->comment = $comment; $this->updatedAt = new CarbonImmutable(); } @@ -279,21 +286,71 @@ public function markAsActive(?string $comment): void /** * @throws InvalidArgumentException */ - #[Override] + #[\Override] public function markAsBlocked(?string $comment): void { - if (Bitrix24AccountStatus::deleted === $this->accountStatus) { + if (Bitrix24AccountStatus::deleted === $this->status) { throw new InvalidArgumentException('you cannot block account in status «deleted»'); } - $this->accountStatus = Bitrix24AccountStatus::blocked; + $this->status = Bitrix24AccountStatus::blocked; $this->comment = $comment; $this->updatedAt = new CarbonImmutable(); + $this->events[] = new Bitrix24AccountBlockedEvent( + $this->id, + new CarbonImmutable(), + $this->comment + ); } - #[Override] + #[\Override] public function getComment(): ?string { return $this->comment; } -} \ No newline at end of file + + private function guardEmptyToken(string $applicationToken): void + { + if ('' === $applicationToken) { + throw new InvalidArgumentException('application token cannot be empty'); + } + } + + private function guardTokenMismatch(string $applicationToken): void + { + if ($this->applicationToken !== $applicationToken) { + throw new InvalidArgumentException( + sprintf( + 'application token «%s» mismatch with application token «%s» for bitrix24 account %s for domain %s', + $applicationToken, + $this->applicationToken, + $this->id->toRfc4122(), + $this->domainUrl + ) + ); + } + } + + private function guardApplicationIsActive(): void + { + if (Bitrix24AccountStatus::active !== $this->status) { + throw new InvalidArgumentException( + sprintf( + 'for uninstall account must be in status «active», current status - «%s»', + $this->status->name + ) + ); + } + } + + private function addAccountCreatedEventIfNeeded(bool $isEmitCreatedEvent): void + { + if ($isEmitCreatedEvent) { + // Создание события и добавление его в массив событий + $this->events[] = new Bitrix24AccountCreatedEvent( + $this->id, + $this->createdAt + ); + } + } +} diff --git a/src/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepository.php b/src/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepository.php new file mode 100644 index 0000000..5fb37ad --- /dev/null +++ b/src/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepository.php @@ -0,0 +1,215 @@ +getClassMetadata(Bitrix24Account::class)); + } + + /** + * @phpstan-return Bitrix24AccountInterface&AggregateRootEventsEmitterInterface + * + * @throws Bitrix24AccountNotFoundException + */ + #[\Override] + public function getById(Uuid $uuid): Bitrix24AccountInterface + { + $account = $this->getEntityManager()->getRepository(Bitrix24Account::class) + ->createQueryBuilder('b24') + ->where('b24.id = :id') + ->andWhere('b24.status != :status') + ->setParameter('id', $uuid) + ->setParameter('status', Bitrix24AccountStatus::deleted) + ->getQuery() + ->getOneOrNullResult() + ; + + if (null === $account) { + throw new Bitrix24AccountNotFoundException( + sprintf('bitrix24 account not found by id %s', $uuid->toRfc4122()) + ); + } + + return $account; + } + + public function existsById(Uuid $uuid): bool + { + $account = $this->getEntityManager()->getRepository(Bitrix24Account::class) + ->createQueryBuilder('b24') + ->where('b24.id = :id') + ->andWhere('b24.status != :status') + ->setParameter('id', $uuid) + ->setParameter('status', Bitrix24AccountStatus::deleted) + ->getQuery() + ->getOneOrNullResult() + ; + + return (bool) $account; + } + + #[\Override] + public function save(Bitrix24AccountInterface $bitrix24Account): void + { + $this->getEntityManager()->persist($bitrix24Account); + } + + /** + * @phpstan-return array + * + * @throws InvalidArgumentException + */ + #[\Override] + public function findByMemberId( + string $memberId, + ?Bitrix24AccountStatus $bitrix24AccountStatus = null, + ?int $bitrix24UserId = null, + ?bool $isAdmin = null + ): array { + if ('' === trim($memberId)) { + throw new InvalidArgumentException('memberId cannot be empty'); + } + + $criteria = [ + 'memberId' => $memberId, + ]; + if ($bitrix24AccountStatus instanceof Bitrix24AccountStatus) { + $criteria['status'] = $bitrix24AccountStatus->name; + } + + if (null !== $bitrix24UserId) { + $criteria['bitrix24UserId'] = $bitrix24UserId; + } + + if (null !== $isAdmin) { + $criteria['isBitrix24UserAdmin'] = $isAdmin; + } + + return $this->findBy($criteria); + } + + #[\Override] + public function delete(Uuid $uuid): void + { + $bitrix24Account = $this->getEntityManager()->getRepository(Bitrix24Account::class)->find($uuid); + + if (null === $bitrix24Account) { + throw new Bitrix24AccountNotFoundException( + sprintf('bitrix24 account not found by id %s', $uuid->toRfc4122()) + ); + } + + if (Bitrix24AccountStatus::deleted !== $bitrix24Account->getStatus()) { + throw new InvalidArgumentException( + sprintf( + 'you cannot delete bitrix24account «%s», they must be in status «deleted», current status «%s»', + $bitrix24Account->getId()->toRfc4122(), + $bitrix24Account->getStatus()->name + ) + ); + } + + $this->save($bitrix24Account); + } + + public function findAllActive(?int $limit = null, ?int $offset = null): array + { + return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findBy( + [ + 'status' => Bitrix24AccountStatus::active, + ], + null, + $limit, + $offset + ); + } + + /** + * @param non-empty-string $applicationToken + * + * @phpstan-return array + * + * @throws InvalidArgumentException + */ + #[\Override] + public function findByApplicationToken(string $applicationToken): array + { + if ('' === $applicationToken) { + throw new InvalidArgumentException('application token cannot be an empty string'); + } + + return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findBy( + [ + 'applicationToken' => $applicationToken, + ] + ); + } + + /** + * @throws InvalidArgumentException + * + * @phpstan-return Bitrix24AccountInterface&AggregateRootEventsEmitterInterface + */ + #[\Override] + public function findOneAdminByMemberId(string $memberId): ?Bitrix24AccountInterface + { + if ('' === trim($memberId)) { + throw new InvalidArgumentException('memberId cannot be an empty string'); + } + + return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findOneBy( + [ + 'memberId' => $memberId, + 'isBitrix24UserAdmin' => true, + 'status' => [Bitrix24AccountStatus::active, Bitrix24AccountStatus::new], + ] + ); + } + + /** + * @phpstan-return array + * + * @return array + * + * @throws InvalidArgumentException + */ + #[\Override] + public function findByDomain( + string $domainUrl, + ?Bitrix24AccountStatus $bitrix24AccountStatus = null, + ?bool $isAdmin = null + ): array { + if ('' === trim($domainUrl)) { + throw new InvalidArgumentException('domainUrl cannot be an empty string'); + } + + $criteria = ['domainUrl' => $domainUrl]; + + if ($bitrix24AccountStatus instanceof Bitrix24AccountStatus) { + $criteria['status'] = $bitrix24AccountStatus->name; + } + + if (null !== $isAdmin) { + $criteria['isBitrix24UserAdmin'] = $isAdmin; + } + + return $this->getEntityManager()->getRepository(Bitrix24Account::class)->findBy($criteria); + } +} diff --git a/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php b/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php new file mode 100644 index 0000000..0d640a8 --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/ChangeDomainUrl/Command.php @@ -0,0 +1,12 @@ +logger->info('Bitrix24Accounts.ChangeDomainUrl.start', [ + 'b24_domain_url_old' => $command->oldDomain->value, + 'b24_domain_url_new' => $command->newDomain->value, + ]); + + /** @var AggregateRootEventsEmitterInterface[]|Bitrix24AccountInterface[] $accounts */ + $accounts = $this->bitrix24AccountRepository->findByDomain($command->oldDomain->value); + foreach ($accounts as $account) { + $account->changeDomainUrl($command->newDomain->value); + $this->bitrix24AccountRepository->save($account); + } + + // используется как оператор распаковки (splat operator) для передачи массива как отдельных аргументов: + $this->flusher->flush(...$accounts); + + $this->logger->info( + 'Bitrix24Accounts.ChangeDomainUrl.Finish', + [ + 'b24_domain_url_old' => $command->oldDomain, + 'b24_domain_url_new' => $command->newDomain, + ] + ); + } +} diff --git a/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php b/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php new file mode 100644 index 0000000..979b0e1 --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/InstallFinish/Command.php @@ -0,0 +1,34 @@ +validate(); + } + + private function validate(): void + { + if ('' === $this->applicationToken) { + throw new \InvalidArgumentException('Application token cannot be empty.'); + } + + if ('' === $this->memberId) { + throw new \InvalidArgumentException('Member ID cannot be empty.'); + } + + if ($this->bitrix24UserId <= 0) { + throw new \InvalidArgumentException('Bitrix24 User ID must be a positive integer.'); + } + } +} diff --git a/src/Bitrix24Accounts/UseCase/InstallFinish/Handler.php b/src/Bitrix24Accounts/UseCase/InstallFinish/Handler.php new file mode 100644 index 0000000..e5b805d --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/InstallFinish/Handler.php @@ -0,0 +1,88 @@ +logger->info('Bitrix24Accounts.InstallFinish.start', [ + 'b24_domain_url' => $command->domain, + 'b24_member_id' => $command->memberId, + 'b24_application_id' => $command->applicationToken, + 'b24_user_id' => $command->bitrix24UserId, + ]); + + /** @var AggregateRootEventsEmitterInterface|Bitrix24AccountInterface $bitrix24Account */ + $bitrix24Account = $this->getSingleAccountByMemberId($command->domain->value, $command->memberId, $command->bitrix24UserId); + + $bitrix24Account->applicationInstalled($command->applicationToken); + + $this->bitrix24AccountRepository->save($bitrix24Account); + $this->flusher->flush($bitrix24Account); + + $this->logger->info( + 'Bitrix24Accounts.InstallFinish.Finish', + [ + 'b24_domain_url' => $command->domain, + 'b24_member_id' => $command->memberId, + 'b24_application_id' => $command->applicationToken, + 'b24_user_id' => $command->bitrix24UserId, + ] + ); + } + + private function getSingleAccountByMemberId(string $domainUrl, string $memberId, ?int $bitrix24UserId): Bitrix24AccountInterface + { + $accounts = $this->bitrix24AccountRepository->findByMemberId( + $memberId, + Bitrix24AccountStatus::new, + $bitrix24UserId + ); + + if ([] === $accounts) { + throw new Bitrix24AccountNotFoundException( + sprintf( + 'bitrix24 account for domain %s with member id %s in status «new» not found', + $domainUrl, + $memberId + ) + ); + } + + if (count($accounts) > 1) { + throw new MultipleBitrix24AccountsFoundException( + sprintf( + 'multiple bitrix24 accounts for domain %s with member id %s in status «new» found', + $domainUrl, + $memberId + ) + ); + } + + return $accounts[0]; + } +} diff --git a/src/Bitrix24Accounts/UseCase/InstallStart/Command.php b/src/Bitrix24Accounts/UseCase/InstallStart/Command.php new file mode 100644 index 0000000..a63c792 --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/InstallStart/Command.php @@ -0,0 +1,41 @@ +validate(); + } + + private function validate(): void + { + if ($this->bitrix24UserId <= 0) { + throw new \InvalidArgumentException('Bitrix24 User ID must be a positive integer.'); + } + + if ('' === $this->memberId) { + throw new \InvalidArgumentException('Member ID must be a non-empty string.'); + } + + if ($this->applicationVersion <= 0) { + throw new \InvalidArgumentException('Application version must be a positive integer.'); + } + } +} diff --git a/src/Bitrix24Accounts/UseCase/InstallStart/Handler.php b/src/Bitrix24Accounts/UseCase/InstallStart/Handler.php new file mode 100644 index 0000000..01e7ee0 --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/InstallStart/Handler.php @@ -0,0 +1,75 @@ +logger->info('Bitrix24Accounts.InstallStart.start', [ + 'id' => $command->uuid->toRfc4122(), + 'domain' => $command->domain, + 'member_id' => $command->memberId, + ]); + + $bitrix24Account = new Bitrix24Account( + $command->uuid, + $command->bitrix24UserId, + $command->isBitrix24UserAdmin, + $command->memberId, + $command->domain->value, + Bitrix24AccountStatus::new, + $command->authToken, + new CarbonImmutable(), + new CarbonImmutable(), + $command->applicationVersion, + $command->applicationScope, + true + ); + + $isAccountExists = $this->bitrix24AccountRepository->existsById($bitrix24Account->getId()); + + if (!$isAccountExists) { + $this->bitrix24AccountRepository->save($bitrix24Account); + $this->flusher->flush($bitrix24Account); + + $this->logger->info( + 'Bitrix24Accounts.InstallStart.Finish', + [ + 'id' => $command->uuid->toRfc4122(), + 'domain_url' => $command->domain, + 'member_id' => $command->memberId, + ] + ); + } else { + $this->logger->info( + 'Bitrix24Accounts.InstallStart.AlreadyExists', + [ + 'id' => $command->uuid->toRfc4122(), + 'domain' => $command->domain, + 'member_id' => $command->memberId, + ] + ); + + throw new Bitrix24AccountNotFoundException( + sprintf('bitrix24account with uuid "%s" already exists', $command->uuid->toRfc4122()) + ); + } + } +} diff --git a/src/Bitrix24Accounts/UseCase/RenewAuthToken/Command.php b/src/Bitrix24Accounts/UseCase/RenewAuthToken/Command.php new file mode 100644 index 0000000..e33957d --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/RenewAuthToken/Command.php @@ -0,0 +1,15 @@ +logger->info('Bitrix24Accounts.RenewAuthToken.start', [ + 'domain_url' => $command->renewedAuthToken->domain, + 'member_id' => $command->renewedAuthToken->memberId, + 'bitrix24_user_id' => $command->bitrix24UserId, + ]); + + /** @var AggregateRootEventsEmitterInterface|Bitrix24AccountInterface $bitrix24Account */ + $bitrix24Account = $this->getSingleAccountByMemberId( + $command->renewedAuthToken->domain, + $command->renewedAuthToken->memberId, + $command->bitrix24UserId + ); + + $bitrix24Account->renewAuthToken($command->renewedAuthToken); + + $this->bitrix24AccountRepository->save($bitrix24Account); + + $this->flusher->flush($bitrix24Account); + + $this->logger->info( + 'Bitrix24Accounts.RenewAuthToken.finish', + [ + 'domain_url' => $command->renewedAuthToken->domain, + 'member_id' => $command->renewedAuthToken->memberId, + 'bitrix24_user_id' => $command->bitrix24UserId, + ] + ); + } + + /** + * @throws MultipleBitrix24AccountsFoundException + */ + private function getSingleAccountByMemberId(string $domainUrl, string $memberId, ?int $bitrix24UserId): Bitrix24AccountInterface + { + $accounts = $this->bitrix24AccountRepository->findByMemberId( + $memberId, + Bitrix24AccountStatus::active, + $bitrix24UserId + ); + + if (null === $bitrix24UserId && count($accounts) > 1) { + throw new MultipleBitrix24AccountsFoundException( + sprintf( + 'updating auth token failure - for domain %s with member id %s found multiple active accounts, try pass bitrix24_user_id in command', + $domainUrl, + $memberId + ) + ); + } + + if (null !== $bitrix24UserId && count($accounts) > 1) { + throw new MultipleBitrix24AccountsFoundException( + sprintf( + 'updating auth token failure - for domain %s with member id %s and bitrix24 user id %s found multiple active accounts', + $domainUrl, + $memberId, + $bitrix24UserId + ) + ); + } + + return $accounts[0]; + } +} diff --git a/src/Bitrix24Accounts/UseCase/Uninstall/Command.php b/src/Bitrix24Accounts/UseCase/Uninstall/Command.php new file mode 100644 index 0000000..5a92c6a --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/Uninstall/Command.php @@ -0,0 +1,24 @@ +validate(); + } + + private function validate(): void + { + if ('' === $this->applicationToken) { + throw new \InvalidArgumentException('Application token must be a non-empty string.'); + } + } +} diff --git a/src/Bitrix24Accounts/UseCase/Uninstall/Handler.php b/src/Bitrix24Accounts/UseCase/Uninstall/Handler.php new file mode 100644 index 0000000..516126b --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/Uninstall/Handler.php @@ -0,0 +1,49 @@ +logger->info('Bitrix24Accounts.Uninstall.start', [ + 'b24_application_token' => $command->applicationToken, + ]); + + /** @var AggregateRootEventsEmitterInterface[]|Bitrix24AccountInterface[] $accounts */ + $accounts = $this->bitrix24AccountRepository->findByApplicationToken($command->applicationToken); + $accountsCount = count($accounts); + foreach ($accounts as $account) { + $account->applicationUninstalled($command->applicationToken); + $this->bitrix24AccountRepository->save($account); + } + + $this->flusher->flush(...$accounts); + + $this->logger->info( + 'Bitrix24Accounts.Uninstall.Finish', + [ + 'accountsCount' => $accountsCount, + 'b24_application_token' => $command->applicationToken, + ] + ); + } +} diff --git a/src/Bitrix24Accounts/UseCase/UpdateVersion/Command.php b/src/Bitrix24Accounts/UseCase/UpdateVersion/Command.php new file mode 100644 index 0000000..ed6c4f0 --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/UpdateVersion/Command.php @@ -0,0 +1,39 @@ +validate(); + } + + private function validate(): void + { + if ($this->bitrix24UserId <= 0) { + throw new \InvalidArgumentException('Bitrix24 User ID must be a positive integer.'); + } + + if ('' === $this->memberId) { + throw new \InvalidArgumentException('Member ID must be a non-empty string.'); + } + + if ($this->newApplicationVersion <= 0) { + throw new \InvalidArgumentException('Application version must be a positive integer.'); + } + } +} diff --git a/src/Bitrix24Accounts/UseCase/UpdateVersion/Handler.php b/src/Bitrix24Accounts/UseCase/UpdateVersion/Handler.php new file mode 100644 index 0000000..997e500 --- /dev/null +++ b/src/Bitrix24Accounts/UseCase/UpdateVersion/Handler.php @@ -0,0 +1,92 @@ +logger->info('Bitrix24Accounts.UpdateVersion.start', [ + 'uuid' => $command->uuid, + 'bitrix24_user_id' => $command->bitrix24UserId, + 'is_bitrix24UserAdmin' => $command->isBitrix24UserAdmin, + 'member_id' => $command->memberId, + 'auth_token' => $command->authToken, + 'new_application_version' => $command->newApplicationVersion, + 'new_application_scope' => $command->newApplicationScope, + ]); + + $accounts = $this->bitrix24AccountRepository->findByMemberId( + $command->memberId, + Bitrix24AccountStatus::active, + $command->bitrix24UserId, + ); + + if ([] !== $accounts) { + /** @var AggregateRootEventsEmitterInterface|Bitrix24AccountInterface $bitrix24Account */ + $bitrix24Account = $accounts[0]; + $bitrix24Account->updateApplicationVersion( + $command->authToken, + $command->bitrix24UserId, + $command->newApplicationVersion, + $command->newApplicationScope, + ); + + $this->bitrix24AccountRepository->save($bitrix24Account); + $this->flusher->flush($bitrix24Account); + + $this->logger->info('Bitrix24Accounts.UpdateVersion.finish', [ + 'uuid' => $command->uuid, + 'bitrix24_user_id' => $command->bitrix24UserId, + 'is_bitrix24UserAdmin' => $command->isBitrix24UserAdmin, + 'member_id' => $command->memberId, + 'auth_token' => $command->authToken, + 'new_application_version' => $command->newApplicationVersion, + 'new_application_scope' => $command->newApplicationScope, + ]); + } else { + $this->logger->info( + 'Bitrix24Accounts.UpdateVersion.NotFoundAccount', + [ + 'uuid' => $command->uuid, + 'bitrix24_user_id' => $command->bitrix24UserId, + 'is_bitrix24UserAdmin' => $command->isBitrix24UserAdmin, + 'member_id' => $command->memberId, + 'auth_token' => $command->authToken, + 'new_application_version' => $command->newApplicationVersion, + 'new_application_scope' => $command->newApplicationScope, + ] + ); + + throw new MultipleBitrix24AccountsFoundException( + sprintf( + 'bitrix24account not found by memberId %s, status %s and bitrix24UserId %s ', + $command->memberId, + 'active', + $command->bitrix24UserId + ) + ); + } + } +} diff --git a/src/Bitrix24Accounts/ValueObjects/Domain.php b/src/Bitrix24Accounts/ValueObjects/Domain.php new file mode 100644 index 0000000..a399ee7 --- /dev/null +++ b/src/Bitrix24Accounts/ValueObjects/Domain.php @@ -0,0 +1,34 @@ +validate($domain); + $this->value = $domain; + } + + private function validate(string $domain): void + { + // Регулярное выражение для проверки допустимых символов (латиница и кириллица) + $patternValidChars = '/^((?!-)[A-Za-zА-Яа-яЁё0-9-]{1,63}(?em->flush(); + foreach ($aggregateRootEventsEmitter as $aggregateRootEventEmitter) { + $events = $aggregateRootEventEmitter->emitEvents(); + foreach ($events as $event) { + $this->eventDispatcher->dispatch($event); + } + } + } +} diff --git a/tests/EntityManagerFactory.php b/tests/EntityManagerFactory.php new file mode 100644 index 0000000..e3935cf --- /dev/null +++ b/tests/EntityManagerFactory.php @@ -0,0 +1,77 @@ + 'pdo_pgsql', + 'host' => $_ENV['DATABASE_HOST'], + 'user' => $_ENV['DATABASE_USER'], + 'password' => $_ENV['DATABASE_PASSWORD'], + 'dbname' => $_ENV['DATABASE_NAME'], + ]; + + if (!Type::hasType(UuidType::NAME)) { + Type::addType(UuidType::NAME, UuidType::class); + } + + if (!Type::hasType('carbon_immutable')) { + Type::addType('carbon_immutable', CarbonImmutableType::class); + } + + $configuration = ORMSetup::createXMLMetadataConfiguration($paths, $isDevMode); + + $connection = DriverManager::getConnection($connectionParams, $configuration); + self::$entityManager = new EntityManager($connection, $configuration); + } + + return self::$entityManager; + } +} diff --git a/tests/Functional/Bitrix24Accounts/Builders/Bitrix24AccountBuilder.php b/tests/Functional/Bitrix24Accounts/Builders/Bitrix24AccountBuilder.php new file mode 100644 index 0000000..bff547e --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/Builders/Bitrix24AccountBuilder.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders; + +use Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterface; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus; +use Bitrix24\SDK\Application\Contracts\Events\AggregateRootEventsEmitterInterface; +use Bitrix24\SDK\Core\Credentials\AuthToken; +use Bitrix24\SDK\Core\Credentials\Scope; +use Carbon\CarbonImmutable; +use Symfony\Component\Uid\Uuid; + +class Bitrix24AccountBuilder +{ + private readonly Uuid $id; + + private readonly int $bitrix24UserId; + + private readonly bool $isBitrix24UserAdmin; + + /** bitrix24 portal unique id */ + private string $memberId; + + private string $domainUrl; + + private Bitrix24AccountStatus $status = Bitrix24AccountStatus::active; + + private readonly AuthToken $authToken; + + private readonly CarbonImmutable $createdAt; + + private readonly CarbonImmutable $updatedAt; + + private readonly int $applicationVersion; + + private Scope $applicationScope; + + private ?string $applicationToken = null; + + public function __construct() + { + $this->id = Uuid::v7(); + $this->bitrix24UserId = random_int(1, 1_000_000); + $this->isBitrix24UserAdmin = true; + $this->memberId = Uuid::v4()->toRfc4122(); + $this->domainUrl = Uuid::v4()->toRfc4122().'-example.com'; + $this->authToken = new AuthToken('old_1', 'old_2', 3600); + $this->createdAt = CarbonImmutable::now(); + $this->updatedAt = CarbonImmutable::now(); + $this->applicationVersion = 1; + $this->applicationScope = new Scope(); + } + + public function withMemberId(string $memberId): self + { + $this->memberId = $memberId; + + return $this; + } + + public function withDomainUrl(string $domainUrl): self + { + $this->domainUrl = $domainUrl; + + return $this; + } + + public function withApplicationScope(Scope $applicationScope): self + { + $this->applicationScope = $applicationScope; + + return $this; + } + + public function withApplicationToken(string $applicationToken): self + { + $this->applicationToken = $applicationToken; + + return $this; + } + + public function withStatus(Bitrix24AccountStatus $bitrix24AccountStatus): self + { + $this->status = $bitrix24AccountStatus; + + return $this; + } + + public function build(): Bitrix24Account + { + $bitrix24Account = new Bitrix24Account( + $this->id, + $this->bitrix24UserId, + $this->isBitrix24UserAdmin, + $this->memberId, + $this->domainUrl, + $this->status, + $this->authToken, + $this->createdAt, + $this->updatedAt, + $this->applicationVersion, + $this->applicationScope + ); + + if ($this->applicationToken !== null && Bitrix24AccountStatus::new == $this->status) { + $bitrix24Account->applicationInstalled($this->applicationToken); + } + + return $bitrix24Account; + } +} diff --git a/tests/Functional/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepositoryTest.php b/tests/Functional/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepositoryTest.php new file mode 100644 index 0000000..4736e55 --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/Infrastructure/Doctrine/Bitrix24AccountRepositoryTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Infrastructure\Doctrine; + +use Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account; +use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository; +use Bitrix24\Lib\Services\Flusher; +use Bitrix24\Lib\Tests\EntityManagerFactory; +use Bitrix24\Lib\Tests\Functional\FlusherDecorator; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterface; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface; +use Bitrix24\SDK\Core\Credentials\AuthToken; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Tests\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterfaceTest; +use Bitrix24\SDK\Tests\Application\Contracts\TestRepositoryFlusherInterface; +use Carbon\CarbonImmutable; +use PHPUnit\Framework\Attributes\CoversClass; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Uid\Uuid; + +/** + * @internal + */ +#[CoversClass(Bitrix24AccountRepository::class)] +class Bitrix24AccountRepositoryTest extends Bitrix24AccountRepositoryInterfaceTest +{ + #[\Override] + protected function createBitrix24AccountImplementation( + Uuid $uuid, + int $bitrix24UserId, + bool $isBitrix24UserAdmin, + string $memberId, + string $domainUrl, + Bitrix24AccountStatus $bitrix24AccountStatus, + AuthToken $authToken, + CarbonImmutable $createdAt, + CarbonImmutable $updatedAt, + int $applicationVersion, + Scope $applicationScope + ): Bitrix24AccountInterface { + return new Bitrix24Account( + $uuid, + $bitrix24UserId, + $isBitrix24UserAdmin, + $memberId, + $domainUrl, + $bitrix24AccountStatus, + $authToken, + $createdAt, + $updatedAt, + $applicationVersion, + $applicationScope + ); + } + + #[\Override] + protected function createBitrix24AccountRepositoryImplementation(): Bitrix24AccountRepositoryInterface + { + $entityManager = EntityManagerFactory::get(); + + return new Bitrix24AccountRepository($entityManager); + } + + #[\Override] + protected function createRepositoryFlusherImplementation(): TestRepositoryFlusherInterface + { + $entityManager = EntityManagerFactory::get(); + $eventDispatcher = new EventDispatcher(); + + return new FlusherDecorator(new Flusher($entityManager, $eventDispatcher)); + } +} diff --git a/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php new file mode 100644 index 0000000..01e2d5a --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/UseCase/ChangeDomainUrl/HandlerTest.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\UseCase\ChangeDomainUrl; + +use Bitrix24\Lib\Bitrix24Accounts; +use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository; +use Bitrix24\Lib\Services\Flusher; +use Bitrix24\Lib\Tests\EntityManagerFactory; +use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountDomainUrlChangedEvent; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Uid\Uuid; +use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain; + +/** + * @internal + */ +#[CoversClass(Bitrix24Accounts\UseCase\ChangeDomainUrl\Handler::class)] +class HandlerTest extends TestCase +{ + private Bitrix24Accounts\UseCase\ChangeDomainUrl\Handler $handler; + + private Flusher $flusher; + + private Bitrix24AccountRepositoryInterface $repository; + + private TraceableEventDispatcher $eventDispatcher; + + #[\Override] + protected function setUp(): void + { + $entityManager = EntityManagerFactory::get(); + $eventDispatcher = new EventDispatcher(); + $this->repository = new Bitrix24AccountRepository($entityManager); + $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch()); + $this->flusher = new Flusher($entityManager, $this->eventDispatcher); + $this->handler = new Bitrix24Accounts\UseCase\ChangeDomainUrl\Handler( + $this->repository, + $this->flusher, + new NullLogger() + ); + } + + #[Test] + #[TestDox('Test change domain url for one account')] + public function testChangeDomainUrlForAccount(): void + { + $oldDomainUrl = Uuid::v7()->toRfc4122().'-test.bitrix24.com'; + $newDomainUrl = 'new-'.$oldDomainUrl; + + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withDomainUrl($oldDomainUrl) + ->build(); + $this->repository->save($bitrix24Account); + $this->flusher->flush(); + + $this->handler->handle( + new Bitrix24Accounts\UseCase\ChangeDomainUrl\Command( + new Domain($oldDomainUrl), + new Domain($newDomainUrl) + ) + ); + + $updated = $this->repository->getById($bitrix24Account->getId()); + $this->assertEquals( + $newDomainUrl, + $updated->getDomainUrl(), + sprintf( + 'New domain url %s must be equals domain url %s after update', + $newDomainUrl, + $updated->getDomainUrl() + ) + ); + + $this->assertTrue( + in_array( + Bitrix24AccountDomainUrlChangedEvent::class, + $this->eventDispatcher->getOrphanedEvents(), + ), + sprintf( + 'Event %s was expected to be in the list of orphan events, but it is missing', + Bitrix24AccountDomainUrlChangedEvent::class + ) + ); + } + + #[Test] + #[TestDox('Test change domain url for many accounts')] + public function testChangeDomainUrlForManyAccounts(): void + { + $oldDomainUrl = Uuid::v7()->toRfc4122().'-test.bitrix24.com'; + $newDomainUrl = 'new-'.$oldDomainUrl; + $b24MemberId = Uuid::v7()->toRfc4122(); + + $bitrix24AccountA = (new Bitrix24AccountBuilder()) + ->withDomainUrl($oldDomainUrl) + ->withMemberId($b24MemberId) + ->build(); + $this->repository->save($bitrix24AccountA); + + $bitrix24AccountB = (new Bitrix24AccountBuilder()) + ->withDomainUrl($oldDomainUrl) + ->withMemberId($b24MemberId) + ->build(); + $this->repository->save($bitrix24AccountB); + + $this->flusher->flush(); + + $this->handler->handle( + new Bitrix24Accounts\UseCase\ChangeDomainUrl\Command( + new Domain($oldDomainUrl), + new Domain($newDomainUrl) + ) + ); + + $accounts = $this->repository->findByMemberId($b24MemberId); + foreach ($accounts as $account) { + $this->assertSame( + $account->getDomainUrl(), + $newDomainUrl, + sprintf( + 'domain url «%s» mismatch with expected «%s» for account «%s»', + $account->getDomainUrl(), + $newDomainUrl, + $account->getId()->toRfc4122() + ) + ); + } + + $this->assertTrue( + in_array( + Bitrix24AccountDomainUrlChangedEvent::class, + $this->eventDispatcher->getOrphanedEvents() + ), + sprintf( + 'Event %s was expected to be in the list of orphan events, but it is missing', + Bitrix24AccountDomainUrlChangedEvent::class + ) + ); + } +} diff --git a/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php new file mode 100644 index 0000000..f0d1ac0 --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/UseCase/InstallFinish/HandlerTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\UseCase\InstallFinish; + +use Bitrix24\Lib\Bitrix24Accounts; +use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository; +use Bitrix24\Lib\Services\Flusher; +use Bitrix24\Lib\Tests\EntityManagerFactory; +use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountApplicationInstalledEvent; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Uid\Uuid; +use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain; + +/** + * @internal + */ +#[CoversClass(Bitrix24Accounts\UseCase\InstallFinish\Handler::class)] +class HandlerTest extends TestCase +{ + private Bitrix24Accounts\UseCase\InstallFinish\Handler $handler; + + private Flusher $flusher; + + private Bitrix24AccountRepositoryInterface $repository; + + private TraceableEventDispatcher $eventDispatcher; + + #[\Override] + protected function setUp(): void + { + $entityManager = EntityManagerFactory::get(); + $eventDispatcher = new EventDispatcher(); + $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch()); + + $this->repository = new Bitrix24AccountRepository($entityManager); + $this->flusher = new Flusher($entityManager, $this->eventDispatcher); + + $this->handler = new Bitrix24Accounts\UseCase\InstallFinish\Handler( + $this->repository, + $this->flusher, + new NullLogger() + ); + } + + #[Test] + #[TestDox('test finish installation application')] + public function testFinishInstallationApplication(): void + { + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withStatus(Bitrix24AccountStatus::new) + ->build(); + + $this->repository->save($bitrix24Account); + $this->flusher->flush(); + + $applicationToken = Uuid::v7()->toRfc4122(); + $this->handler->handle( + new Bitrix24Accounts\UseCase\InstallFinish\Command( + $applicationToken, + $bitrix24Account->getMemberId(), + new Domain($bitrix24Account->getDomainUrl()), + $bitrix24Account->getBitrix24UserId() + ) + ); + + $updated = $this->repository->getById($bitrix24Account->getId()); + $this->assertEquals(Bitrix24AccountStatus::active, $updated->getStatus(), 'expected status is active'); + $this->assertTrue( + $updated->isApplicationTokenValid($applicationToken), + sprintf( + 'failed application token «%s» validation for bitrix24 account with id «%s»', + $applicationToken, + $bitrix24Account->getId()->toString() + ) + ); + $this->assertTrue( + in_array(Bitrix24AccountApplicationInstalledEvent::class, $this->eventDispatcher->getOrphanedEvents(), true), + sprintf( + 'emited event «%s» for bitrix24 account wiht id «%s» not found', + Bitrix24AccountApplicationInstalledEvent::class, + $bitrix24Account->getId()->toString() + ) + ); + } +} diff --git a/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php new file mode 100644 index 0000000..0090188 --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/UseCase/InstallStart/HandlerTest.php @@ -0,0 +1,209 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\UseCase\InstallStart; + +use Bitrix24\Lib\Bitrix24Accounts; +use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository; +use Bitrix24\Lib\Services\Flusher; +use Bitrix24\Lib\Tests\EntityManagerFactory; +use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountCreatedEvent; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Exceptions\Bitrix24AccountNotFoundException; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\UnknownScopeCodeException; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Random\RandomException; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Stopwatch\Stopwatch; +use Bitrix24\Lib\Bitrix24Accounts\ValueObjects\Domain; + +/** + * @internal + */ +#[CoversClass(Bitrix24Accounts\UseCase\InstallStart\Handler::class)] +class HandlerTest extends TestCase +{ + private Bitrix24Accounts\UseCase\InstallStart\Handler $handler; + + private Flusher $flusher; + + private Bitrix24AccountRepositoryInterface $repository; + + private TraceableEventDispatcher $eventDispatcher; + + #[\Override] + protected function setUp(): void + { + $entityManager = EntityManagerFactory::get(); + $this->eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $this->repository = new Bitrix24AccountRepository($entityManager); + $this->flusher = new Flusher($entityManager, $this->eventDispatcher); + $this->handler = new Bitrix24Accounts\UseCase\InstallStart\Handler( + $this->repository, + $this->flusher, + new NullLogger() + ); + } + + /** + * @throws InvalidArgumentException + * @throws Bitrix24AccountNotFoundException + * @throws UnknownScopeCodeException + */ + #[Test] + public function testInstallNewApplication(): void + { + $bitrix24AccountBuilder = (new Bitrix24AccountBuilder()) + ->withApplicationScope(new Scope(['crm'])) + ->build(); + + $this->handler->handle( + new Bitrix24Accounts\UseCase\InstallStart\Command( + $bitrix24AccountBuilder->getId(), + $bitrix24AccountBuilder->getBitrix24UserId(), + $bitrix24AccountBuilder->isBitrix24UserAdmin(), + $bitrix24AccountBuilder->getMemberId(), + new Domain($bitrix24AccountBuilder->getDomainUrl()), + $bitrix24AccountBuilder->getAuthToken(), + $bitrix24AccountBuilder->getApplicationVersion(), + $bitrix24AccountBuilder->getApplicationScope() + ) + ); + + $bitrix24Account = $this->repository->getById($bitrix24AccountBuilder->getId()); + + $this->assertEquals( + $bitrix24AccountBuilder->getBitrix24UserId(), + $bitrix24Account->getBitrix24UserId(), + sprintf( + 'Expected the property value to be "%s", but got "%s"', + $bitrix24AccountBuilder->getBitrix24UserId(), + $bitrix24Account->getBitrix24UserId() + ) + ); + + $this->assertEquals( + $bitrix24AccountBuilder->isBitrix24UserAdmin(), + $bitrix24Account->isBitrix24UserAdmin(), + sprintf( + 'Expected the property value to be "%s", but got "%s"', + $bitrix24AccountBuilder->isBitrix24UserAdmin(), + $bitrix24Account->isBitrix24UserAdmin() + ) + ); + + $this->assertEquals( + $bitrix24AccountBuilder->getMemberId(), + $bitrix24Account->getMemberId(), + sprintf( + 'Expected the property value to be "%s", but got "%s"', + $bitrix24AccountBuilder->getMemberId(), + $bitrix24Account->getMemberId() + ) + ); + + $this->assertEquals( + $bitrix24AccountBuilder->getDomainUrl(), + $bitrix24Account->getDomainUrl(), + sprintf( + 'Expected the property value to be "%s", but got "%s"', + $bitrix24AccountBuilder->getDomainUrl(), + $bitrix24Account->getDomainUrl() + ) + ); + + $this->assertEquals( + $bitrix24AccountBuilder->getAuthToken(), + $bitrix24Account->getAuthToken(), + 'Object not equals' + ); + + $this->assertEquals( + $bitrix24AccountBuilder->getApplicationVersion(), + $bitrix24Account->getApplicationVersion(), + sprintf( + 'Expected the property value to be "%s", but got "%s"', + $bitrix24AccountBuilder->getApplicationVersion(), + $bitrix24Account->getApplicationVersion() + ) + ); + $this->assertEquals( + $bitrix24AccountBuilder->getApplicationScope(), + $bitrix24Account->getApplicationScope(), + 'Object not equals' + ); + + $this->assertEquals(Bitrix24AccountStatus::new, $bitrix24Account->getStatus()); + + $this->assertContains( + Bitrix24AccountCreatedEvent::class, + $this->eventDispatcher->getOrphanedEvents(), + sprintf( + 'not found expected domain event «%s»', + Bitrix24AccountCreatedEvent::class + ) + ); + } + + /** + * @throws Bitrix24AccountNotFoundException + * @throws InvalidArgumentException + * @throws UnknownScopeCodeException + */ + #[Test] + public function testInstallExistingAccount(): void + { + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withApplicationScope(new Scope(['crm'])) + ->build(); + + $this->handler->handle( + new Bitrix24Accounts\UseCase\InstallStart\Command( + $bitrix24Account->getId(), + $bitrix24Account->getBitrix24UserId(), + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + new Domain($bitrix24Account->getDomainUrl()), + $bitrix24Account->getAuthToken(), + $bitrix24Account->getApplicationVersion(), + $bitrix24Account->getApplicationScope() + ) + ); + + $this->expectException(Bitrix24AccountNotFoundException::class); + $this->expectExceptionMessage( + sprintf('bitrix24account with uuid "%s" already exists', $bitrix24Account->getId()) + ); + + $this->handler->handle( + new Bitrix24Accounts\UseCase\InstallStart\Command( + $bitrix24Account->getId(), + $bitrix24Account->getBitrix24UserId(), + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + new Domain($bitrix24Account->getDomainUrl()), + $bitrix24Account->getAuthToken(), + $bitrix24Account->getApplicationVersion(), + $bitrix24Account->getApplicationScope() + ) + ); + } +} diff --git a/tests/Functional/Bitrix24Accounts/UseCase/RenewAuthToken/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/RenewAuthToken/HandlerTest.php new file mode 100644 index 0000000..31a5855 --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/UseCase/RenewAuthToken/HandlerTest.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\UseCase\RenewAuthToken; + +use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository; +use Bitrix24\Lib\Bitrix24Accounts\UseCase\RenewAuthToken\Command; +use Bitrix24\Lib\Bitrix24Accounts\UseCase\RenewAuthToken\Handler; +use Bitrix24\Lib\Services\Flusher; +use Bitrix24\Lib\Tests\EntityManagerFactory; +use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder; +use Bitrix24\SDK\Application\ApplicationStatus; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface; +use Bitrix24\SDK\Core\Credentials\AuthToken; +use Bitrix24\SDK\Core\Response\DTO\RenewedAuthToken; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Stopwatch\Stopwatch; + +/** + * @internal + */ +#[CoversClass(Handler::class)] +class HandlerTest extends TestCase +{ + private Handler $handler; + + private Flusher $flusher; + + private Bitrix24AccountRepositoryInterface $repository; + + private TraceableEventDispatcher $eventDispatcher; + + #[\Override] + protected function setUp(): void + { + $entityManager = EntityManagerFactory::get(); + $eventDispatcher = new EventDispatcher(); + $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch()); + + $this->repository = new Bitrix24AccountRepository($entityManager); + $this->flusher = new Flusher($entityManager, $this->eventDispatcher); + $this->handler = new Handler( + $this->repository, + $this->flusher, + new NullLogger() + ); + } + + #[Test] + public function testRenewAuthTokenWithoutBitrix24UserId(): void + { + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->build(); + $this->repository->save($bitrix24Account); + $this->flusher->flush(); + + $newAuthToken = new AuthToken('new_1', 'new_2', 3600); + $this->handler->handle( + new Command( + new RenewedAuthToken( + $newAuthToken, + $bitrix24Account->getMemberId(), + 'https://client-endpoint.com', + 'https://server-endpoint.com', + ApplicationStatus::subscription(), + $bitrix24Account->getDomainUrl() + ), + $bitrix24Account->getBitrix24UserId() + ) + ); + $updated = $this->repository->getById($bitrix24Account->getId()); + $this->assertEquals( + $newAuthToken->accessToken, + $updated->getAuthToken()->accessToken, + sprintf( + 'Expected accessToken %s but got %s', + $newAuthToken->accessToken, + $updated->getAuthToken()->accessToken + ) + ); + + $this->assertEquals( + $newAuthToken->refreshToken, + $updated->getAuthToken()->refreshToken, + sprintf( + 'Expected refreshToken %s but got %s', + $newAuthToken->refreshToken, + $updated->getAuthToken()->refreshToken + ) + ); + } +} diff --git a/tests/Functional/Bitrix24Accounts/UseCase/Uninstall/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/Uninstall/HandlerTest.php new file mode 100644 index 0000000..a902791 --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/UseCase/Uninstall/HandlerTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\UseCase\Uninstall; + +use Bitrix24\Lib\Bitrix24Accounts; +use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository; +use Bitrix24\Lib\Services\Flusher; +use Bitrix24\Lib\Tests\EntityManagerFactory; +use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Events\Bitrix24AccountApplicationUninstalledEvent; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Exceptions\Bitrix24AccountNotFoundException; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Uid\Uuid; + +/** + * @internal + */ +#[CoversClass(Bitrix24Accounts\UseCase\Uninstall\Handler::class)] +class HandlerTest extends TestCase +{ + private Bitrix24Accounts\UseCase\Uninstall\Handler $handler; + + private Flusher $flusher; + + private Bitrix24AccountRepositoryInterface $repository; + + private TraceableEventDispatcher $eventDispatcher; + + #[\Override] + protected function setUp(): void + { + $entityManager = EntityManagerFactory::get(); + $eventDispatcher = new EventDispatcher(); + $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch()); + + $this->repository = new Bitrix24AccountRepository($entityManager); + $this->flusher = new Flusher($entityManager, $this->eventDispatcher); + + $this->handler = new Bitrix24Accounts\UseCase\Uninstall\Handler( + $this->repository, + $this->flusher, + new NullLogger(), + ); + } + + /** + * @throws InvalidArgumentException + * @throws Bitrix24AccountNotFoundException + */ + #[Test] + public function testUninstallApplication(): void + { + $applicationToken = Uuid::v7()->toRfc4122(); + + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withStatus(Bitrix24AccountStatus::new) + ->withApplicationToken($applicationToken) + ->build(); + + $this->repository->save($bitrix24Account); + $this->flusher->flush(); + + $this->handler->handle(new Bitrix24Accounts\UseCase\Uninstall\Command($applicationToken)); + + $this->expectException(Bitrix24AccountNotFoundException::class); + $this->repository->getById($bitrix24Account->getId()); + + $this->assertTrue( + in_array( + Bitrix24AccountApplicationUninstalledEvent::class, + $this->eventDispatcher->getOrphanedEvents() + ), + sprintf( + 'Event %s was expected to be in the list of orphan events, but it is missing', + Bitrix24AccountApplicationUninstalledEvent::class + ) + ); + } +} diff --git a/tests/Functional/Bitrix24Accounts/UseCase/UpdateVersion/HandlerTest.php b/tests/Functional/Bitrix24Accounts/UseCase/UpdateVersion/HandlerTest.php new file mode 100644 index 0000000..8147815 --- /dev/null +++ b/tests/Functional/Bitrix24Accounts/UseCase/UpdateVersion/HandlerTest.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\UseCase\UpdateVersion; + +use Bitrix24\Lib\Bitrix24Accounts; +use Bitrix24\Lib\Bitrix24Accounts\Infrastructure\Doctrine\Bitrix24AccountRepository; +use Bitrix24\Lib\Services\Flusher; +use Bitrix24\Lib\Tests\EntityManagerFactory; +use Bitrix24\Lib\Tests\Functional\Bitrix24Accounts\Builders\Bitrix24AccountBuilder; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Exceptions\MultipleBitrix24AccountsFoundException; +use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Repository\Bitrix24AccountRepositoryInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Uid\Uuid; + +/** + * @internal + */ +#[CoversClass(Bitrix24Accounts\UseCase\UpdateVersion\Handler::class)] +class HandlerTest extends TestCase +{ + private Bitrix24Accounts\UseCase\UpdateVersion\Handler $handler; + + private Flusher $flusher; + + private Bitrix24AccountRepositoryInterface $repository; + + private TraceableEventDispatcher $eventDispatcher; + + #[\Override] + protected function setUp(): void + { + $entityManager = EntityManagerFactory::get(); + $eventDispatcher = new EventDispatcher(); + $this->eventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch()); + + $this->repository = new Bitrix24AccountRepository($entityManager); + $this->flusher = new Flusher($entityManager, $this->eventDispatcher); + $this->handler = new Bitrix24Accounts\UseCase\UpdateVersion\Handler( + $this->repository, + $this->flusher, + new NullLogger(),); + + } + + #[Test] + public function testSuccessUpdateVersion(): void + { + $applicationToken = Uuid::v7()->toRfc4122(); + + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withStatus(Bitrix24AccountStatus::active) + ->withApplicationToken($applicationToken) + ->build(); + + $this->repository->save($bitrix24Account); + $this->flusher->flush(); + + $this->handler->handle(new Bitrix24Accounts\UseCase\UpdateVersion\Command( + $bitrix24Account->getId(), + $bitrix24Account->getBitrix24UserId(), + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + $bitrix24Account->getAuthToken(), + 2, + new Scope(['crm','log']) + )); + + $updated = $this->repository->getById($bitrix24Account->getId()); + + $this->assertEquals(2,$updated->getApplicationVersion(), 'expected application version is 2'); + $this->assertEquals(new Scope(['crm','log']),$updated->getApplicationScope(), 'application Scope is not equal'); + } + + #[Test] + public function testNotFoundBitrix24AccountForUpdateVersion(): void + { + $applicationToken = Uuid::v7()->toRfc4122(); + + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withStatus(Bitrix24AccountStatus::active) + ->withApplicationToken($applicationToken) + ->build(); + + $this->repository->save($bitrix24Account); + $this->flusher->flush(); + + $this->expectException(MultipleBitrix24AccountsFoundException::class); + $this->expectExceptionMessage(sprintf('bitrix24account not found by memberId %s, status %s and bitrix24UserId %s ', + $bitrix24Account->getMemberId(), + 'active', + 3558) + ); + + $this->handler->handle(new Bitrix24Accounts\UseCase\UpdateVersion\Command( + $bitrix24Account->getId(), + 3558, + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + $bitrix24Account->getAuthToken(), + 2, + new Scope(['crm','log']) + )); + } + + #[Test] + public function testNotValidVersionForUpdateVersion(): void + { + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withStatus(Bitrix24AccountStatus::active) + ->build(); + + + $this->repository->save($bitrix24Account); + $this->flusher->flush(); + + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + sprintf( + 'you cannot downgrade application version or set some version, current version «%s», but you try to upgrade to «%s»', + $bitrix24Account->getApplicationVersion(), + '1' + ) + ); + + $this->handler->handle(new Bitrix24Accounts\UseCase\UpdateVersion\Command( + $bitrix24Account->getId(), + $bitrix24Account->getBitrix24UserId(), + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + $bitrix24Account->getAuthToken(), + 1, + new Scope(['crm','log']) + )); + } +} diff --git a/tests/Functional/FlusherDecorator.php b/tests/Functional/FlusherDecorator.php new file mode 100644 index 0000000..f68b903 --- /dev/null +++ b/tests/Functional/FlusherDecorator.php @@ -0,0 +1,21 @@ +flusher->flush(); + } +} diff --git a/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php b/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php index d8b83f7..3671674 100644 --- a/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php +++ b/tests/Unit/Bitrix24Accounts/Entity/Bitrix24AccountTest.php @@ -11,37 +11,38 @@ declare(strict_types=1); -namespace Bitrix24\SDK\Lib\Tests\Unit\Bitrix24Accounts\Entity; +namespace Bitrix24\Lib\Tests\Unit\Bitrix24Accounts\Entity; +use Bitrix24\Lib\Bitrix24Accounts\Entity\Bitrix24Account; use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterface; use Bitrix24\SDK\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountStatus; -use Bitrix24\SDK\Lib\Bitrix24Accounts\Entity\Bitrix24Account; use Bitrix24\SDK\Core\Credentials\AuthToken; use Bitrix24\SDK\Core\Credentials\Scope; use Bitrix24\SDK\Tests\Application\Contracts\Bitrix24Accounts\Entity\Bitrix24AccountInterfaceTest; use Carbon\CarbonImmutable; -use Override; use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Component\Uid\Uuid; +/** + * @internal + */ #[CoversClass(Bitrix24Account::class)] class Bitrix24AccountTest extends Bitrix24AccountInterfaceTest { - #[Override] + #[\Override] protected function createBitrix24AccountImplementation( - Uuid $uuid, - int $bitrix24UserId, - bool $isBitrix24UserAdmin, - string $memberId, - string $domainUrl, + Uuid $uuid, + int $bitrix24UserId, + bool $isBitrix24UserAdmin, + string $memberId, + string $domainUrl, Bitrix24AccountStatus $bitrix24AccountStatus, - AuthToken $authToken, - CarbonImmutable $createdAt, - CarbonImmutable $updatedAt, - int $applicationVersion, - Scope $applicationScope - ): Bitrix24AccountInterface - { + AuthToken $authToken, + CarbonImmutable $createdAt, + CarbonImmutable $updatedAt, + int $applicationVersion, + Scope $applicationScope + ): Bitrix24AccountInterface { return new Bitrix24Account( $uuid, $bitrix24UserId, @@ -56,4 +57,4 @@ protected function createBitrix24AccountImplementation( $applicationScope ); } -} \ No newline at end of file +} diff --git a/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php new file mode 100644 index 0000000..bb3646f --- /dev/null +++ b/tests/Unit/Bitrix24Accounts/UseCase/ChangeDomainUrl/CommandTest.php @@ -0,0 +1,142 @@ +assertTrue(true); + } + + #[Test] + #[DataProvider('dataForValidateInvalidDomain')] + public function testValidateInvalidDomain( + string $oldDomain, + string $newDomain, + ?string $expectedException, + ?string $expectedExceptionMessage + ): void { + + if ($expectedException !== null) { + $this->expectException($expectedException); + } + + if ($expectedExceptionMessage !== null) { + $this->expectExceptionMessage($expectedExceptionMessage); + } + + new Domain($oldDomain); + new Domain($newDomain); + + } + + public static function dataForValidateValidDomain(): \Generator + { + + // Примеры допустимых доменов + /*$arrValidDomains = [ + ['oldDomain' => 'example.com', 'newDomain' => 'example.org'], + ['oldDomain' => 'пример.рф', 'newDomain' => 'пример.рус'], + ['oldDomain' => 'test-site.org', 'newDomain' => 'test-site.ru'], + ['oldDomain' => 'valid-domain.co.uk', 'newDomain' => 'valid-domain.net'], + ['oldDomain' => 'subdomain.example.com', 'newDomain' => 'subdomain2.example.com'], + ['oldDomain' => 'тест.рус', 'newDomain' => 'тест2.рус'], // Пример с кириллицей + ];*/ + + yield 'validDomain1' => [ + 'example.com', + 'example.org', + ]; + + yield 'validDomain2' => [ + 'пример.рф', + 'пример.рус', + ]; + + yield 'validDomain3' => [ + 'test-site.org', + 'test-site.ru', + ]; + + yield 'validDomain4' => [ + 'valid-domain.co.uk', + 'valid-domain.net', + ]; + + yield 'validDomain5' => [ + 'subdomain.example.com', + 'subdomain2.example.com', + ]; + + yield 'validDomain6' => [ + 'тест.рус', + 'тест2.рус', + ]; + } + + public static function dataForValidateInvalidDomain(): \Generator + { + yield 'invalidDomain1' => [ + 'invalid_domain.com', // Неправильный формат (подчеркивание) + 'valid.com', + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', 'invalid_domain.com') + ]; + + yield 'invalidDomain2' => [ + '-invalid.com', // Домен не может начинаться с дефиса + 'valid.com', + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', '-invalid.com') + ]; + + yield 'invalidDomain3' => [ + 'invalid-.com', // Домен не может заканчиваться на дефис + 'valid.com', + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', 'invalid-.com') + ]; + + yield 'invalidDomain4' => [ + '123.456.789.0', // Неправильный формат (IP-адрес) + 'valid.com', + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', '123.456.789.0') + ]; + + yield 'invalidDomain5' => [ + 'example..com', // Два подряд идущих точки + 'valid.com', + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', 'example..com') + ]; + + yield 'invalidDomain6' => [ + 'example.c', // Слишком короткая доменная зона + 'valid.com', + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', 'example.c') + ]; + } +} diff --git a/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php new file mode 100644 index 0000000..44559ef --- /dev/null +++ b/tests/Unit/Bitrix24Accounts/UseCase/InstallFinish/CommandTest.php @@ -0,0 +1,87 @@ +expectException($expectedException); + } + + if (null !== $expectedExceptionMessage) { + $this->expectExceptionMessage($expectedExceptionMessage); + } + + $domain = new Domain($domainUrl); + new Command( + $applicationToken, + $memberId, + $domain, + $bitrix24UserId + ); + } + + public static function dataForCommand(): \Generator + { + $applicationToken = Uuid::v7()->toRfc4122(); + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withStatus(Bitrix24AccountStatus::new) + ->build(); + + yield 'emptyApplicationToken' => [ + '', + $bitrix24Account->getMemberId(), + $bitrix24Account->getDomainUrl(), + $bitrix24Account->getBitrix24UserId(), + \InvalidArgumentException::class, + 'Application token cannot be empty.' + ]; + + yield 'emptyMemberId' => [ + $applicationToken, + '', + $bitrix24Account->getDomainUrl(), + $bitrix24Account->getBitrix24UserId(), + \InvalidArgumentException::class, + 'Member ID cannot be empty.' + ]; + + yield 'invalidDomain' => [ + $applicationToken, + $bitrix24Account->getMemberId(), + '', + $bitrix24Account->getBitrix24UserId(), + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', '') + ]; + } +} diff --git a/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php new file mode 100644 index 0000000..3369550 --- /dev/null +++ b/tests/Unit/Bitrix24Accounts/UseCase/InstallStart/CommandTest.php @@ -0,0 +1,122 @@ +expectException($expectedException); + } + + if (null !== $expectedExceptionMessage) { + $this->expectExceptionMessage($expectedExceptionMessage); + } + + $domain = new Domain($domainUrl); + + new Command( + $uuid, + $bitrix24UserId, + $isBitrix24UserAdmin, + $memberId, + $domain, + $authToken, + $applicationVersion, + $applicationScope + ); + + } + + public static function dataForCommand(): \Generator + { + $bitrix24Account = (new Bitrix24AccountBuilder()) + ->withStatus(Bitrix24AccountStatus::new) + ->build(); + + yield 'emptyMemberId' => [ + $bitrix24Account->getId(), + $bitrix24Account->getBitrix24UserId(), + $bitrix24Account->isBitrix24UserAdmin(), + '', + $bitrix24Account->getDomainUrl(), + $bitrix24Account->getAuthToken(), + $bitrix24Account->getApplicationVersion(), + $bitrix24Account->getApplicationScope(), + \InvalidArgumentException::class, + 'Member ID must be a non-empty string.' + ]; + + yield 'validDomainUrl' => [ + $bitrix24Account->getId(), + $bitrix24Account->getBitrix24UserId(), + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + '', + $bitrix24Account->getAuthToken(), + $bitrix24Account->getApplicationVersion(), + $bitrix24Account->getApplicationScope(), + \InvalidArgumentException::class, + sprintf('Invalid domain: %s', '') + ]; + + yield 'validBitrix24UserId' => [ + $bitrix24Account->getId(), + 0, + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + $bitrix24Account->getDomainUrl(), + $bitrix24Account->getAuthToken(), + $bitrix24Account->getApplicationVersion(), + $bitrix24Account->getApplicationScope(), + \InvalidArgumentException::class, + 'Bitrix24 User ID must be a positive integer.' + ]; + + yield 'validApplicationToken' => [ + $bitrix24Account->getId(), + $bitrix24Account->getBitrix24UserId(), + $bitrix24Account->isBitrix24UserAdmin(), + $bitrix24Account->getMemberId(), + $bitrix24Account->getDomainUrl(), + $bitrix24Account->getAuthToken(), + 0, + $bitrix24Account->getApplicationScope(), + \InvalidArgumentException::class, + 'Application version must be a positive integer.' + ]; + } +} diff --git a/tests/Unit/Bitrix24Accounts/UseCase/Uninstall/CommandTest.php b/tests/Unit/Bitrix24Accounts/UseCase/Uninstall/CommandTest.php new file mode 100644 index 0000000..257a99d --- /dev/null +++ b/tests/Unit/Bitrix24Accounts/UseCase/Uninstall/CommandTest.php @@ -0,0 +1,62 @@ +assertEquals($applicationToken,$command->applicationToken); + } + + #[Test] + #[DataProvider('dataForCommandEmptyToken')] + public function testEmptyTokenForCommand( + string $applicationToken, + ?string $expectedException, + ?string $expectedExceptionMessage, + ): void { + if (null !== $expectedException) { + $this->expectException($expectedException); + } + + if (null !== $expectedExceptionMessage) { + $this->expectExceptionMessage($expectedExceptionMessage); + } + + new Command($applicationToken); + } + + public static function dataForCommandValidToken(): \Generator + { + yield 'validApplicationToken' => [ + Uuid::v7()->toRfc4122() + ]; + } + + public static function dataForCommandEmptyToken(): \Generator + { + yield 'emptyApplicationToken' => [ + '', + \InvalidArgumentException::class, + 'Application token must be a non-empty string.', + ]; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 94def9c..2f9c358 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -11,4 +11,24 @@ declare(strict_types=1); -require_once dirname(__DIR__) . '/vendor/autoload.php'; \ No newline at end of file +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Dotenv\Dotenv; + +require_once dirname(__DIR__).'/vendor/autoload.php'; + +require_once dirname(__DIR__).'/tests/EntityManagerFactory.php'; + +if (!class_exists(Dotenv::class)) { + throw new LogicException('You need to add "symfony/dotenv" as Composer dependencies.'); +} + +$input = new ArgvInput(); +if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) { + putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); +} + +if ($input->hasParameterOption('--no-debug', true)) { + putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); +} + +(new Dotenv())->loadEnv(dirname(__DIR__).'/.env');