From dd3490608d9687797cfc1a5281362315b02f188f Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 9 May 2024 10:50:19 +0200 Subject: [PATCH] Import InMemoryStore from Cirrus Identity --- .github/workflows/php.yml | 160 ++++++++++++++++++++++++++-- composer.json | 13 ++- lib/InMemoryStore.php | 79 ++++++++++++++ phpunit.xml | 21 ++++ tests/bootstrap.php | 6 ++ tests/src/InMemoryStoreTest.php | 58 ++++++++++ tools/composer-require-checker.json | 4 + 7 files changed, 328 insertions(+), 13 deletions(-) create mode 100644 lib/InMemoryStore.php create mode 100644 phpunit.xml create mode 100644 tests/bootstrap.php create mode 100644 tests/src/InMemoryStoreTest.php create mode 100644 tools/composer-require-checker.json diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 308fdfb..3b3a49b 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -74,12 +74,11 @@ jobs: - name: Install Composer dependencies run: composer install --no-progress --prefer-dist --optimize-autoloader - #- name: Check code for hard dependencies missing in composer.json - # run: composer-require-checker check composer.json + - name: Check code for hard dependencies missing in composer.json + run: composer-require-checker check --config-file tools/composer-require-checker.json composer.json - name: Check code for unused dependencies in composer.json - run: | - composer-unused \ + run: composer-unused \ --excludePackage=slevomat/coding-standard --excludePackage=symfony/phpunit-bridge @@ -88,15 +87,13 @@ jobs: - name: Psalm continue-on-error: true - run: | - psalm -c psalm.xml \ + run: psalm -c psalm.xml \ --show-info=true \ --shepherd \ --php-version=${{ steps.setup-php.outputs.php-version }} - name: Psalter - run: | - psalm --alter \ + run: psalm --alter \ --issues=UnnecessaryVarAnnotation \ --dry-run \ --php-version=${{ steps.setup-php.outputs.php-version }} @@ -141,3 +138,150 @@ jobs: - name: Security check for updated dependencies run: composer audit + + unit-tests-linux: + name: "Unit tests, PHP ${{ matrix.php-versions }}, ${{ matrix.operating-system }}" + runs-on: ${{ matrix.operating-system }} + needs: [linter, quality, security] + strategy: + fail-fast: false + matrix: + operating-system: [ubuntu-latest] + php-versions: ['8.1', '8.2', '8.3'] + + steps: + - name: Setup PHP, with composer and extensions + # https://github.com/shivammathur/setup-php + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: ctype, date, dom, fileinfo, filter, hash, json, mbstring, mysql, openssl, pcre,\ + pdo, pdo_sqlite, posix, soap, spl, xdebug, xml + tools: composer + ini-values: error_reporting=E_ALL, pcov.directory=. + coverage: pcov + + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v4 + + - name: Get composer cache directory + run: echo COMPOSER_CACHE="$(composer config cache-files-dir)" >> "$GITHUB_ENV" + + - name: Cache composer dependencies + uses: actions/cache@v4 + with: + path: $COMPOSER_CACHE + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Composer dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Run unit tests with coverage + if: ${{ matrix.php-versions == '8.3' }} + run: vendor/bin/phpunit + + - name: Run unit tests (no coverage) + if: ${{ matrix.php-versions != '8.3' }} + run: vendor/bin/phpunit --no-coverage + + - name: Save coverage data + if: ${{ matrix.php-versions == '8.3' }} + uses: actions/upload-artifact@v4 + with: + name: coverage-data + path: ${{ github.workspace }}/build + + unit-tests-windows: + name: "Unit tests, PHP ${{ matrix.php-versions }}, ${{ matrix.operating-system }}" + runs-on: ${{ matrix.operating-system }} + needs: [linter, quality, security] + strategy: + fail-fast: true + matrix: + operating-system: [windows-latest] + php-versions: ['8.1', '8.2', '8.3'] + + steps: + - name: Setup PHP, with composer and extensions + # https://github.com/shivammathur/setup-php + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: ctype, dom, date, fileinfo, filter, hash, json, mbstring, mysql, openssl, pcre, \ + pdo, pdo_sqlite, posix, soap, spl, xdebug, xml + tools: composer + ini-values: error_reporting=E_ALL + coverage: none + + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v4 + + - name: Get composer cache directory + run: echo COMPOSER_CACHE="$(composer config cache-files-dir)" >> "$env:GITHUB_ENV" + + - name: Cache composer dependencies + uses: actions/cache@v4 + with: + path: $COMPOSER_CACHE + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install Composer dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader --ignore-platform-req=ext-posix + + - name: Run unit tests + run: vendor/bin/phpunit --no-coverage + + coverage: + name: Code coverage + runs-on: [ubuntu-latest] + needs: [unit-tests-linux] + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + name: coverage-data + path: ${{ github.workspace }}/build + + - name: Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + verbose: true + + cleanup: + name: Cleanup artifacts + needs: [unit-tests-linux, coverage] + runs-on: [ubuntu-latest] + if: | + always() && + needs.coverage.result == 'success' || + (needs.unit-tests-linux == 'success' && needs.coverage == 'skipped') + + steps: + - uses: geekyeggo/delete-artifact@v5 + with: + name: coverage-data diff --git a/composer.json b/composer.json index d4231c4..1dc90e2 100644 --- a/composer.json +++ b/composer.json @@ -15,16 +15,19 @@ "SimpleSAML\\TestUtils\\": "lib/" } }, - "require": { - "phpunit/phpunit": "^10.0 || ^11.0", - "slevomat/coding-standard": "^8.15", - "symfony/phpunit-bridge": "^6.4 || ^7.0" + "autoload-dev": { + "psr-4": { + "SimpleSAML\\Test\\TestUtils\\": "tests/src/" + } }, - "require-dev": { + "require": { "php": "^8.1", "ext-curl": "*", + "phpunit/phpunit": "^10.0 || ^11.0", "simplesamlphp/simplesamlphp": "^2.2.0" + "slevomat/coding-standard": "^8.15", + "symfony/phpunit-bridge": "^6.4 || ^7.0" }, "support": { "issues": "https://github.com/simplesamlphp/simplesamlphp-test-framework/issues", diff --git a/lib/InMemoryStore.php b/lib/InMemoryStore.php new file mode 100644 index 0000000..b5c4e7b --- /dev/null +++ b/lib/InMemoryStore.php @@ -0,0 +1,79 @@ +delete($type, $key); + return null; + } + return $item['value']; + } + return null; + } + + + /** + * Save a value to the data store. + * + * @param string $type The data type. + * @param string $key The key. + * @param mixed $value The value. + * @param int|null $expire The expiration time (unix timestamp), or null if it never expires. + */ + public function set(string $type, string $key, mixed $value, ?int $expire = null): void + { + self::$store[$key] = [ + 'type' => $type, + 'value' => $value, + 'expire' => $expire + ]; + } + + + /** + * Delete a value from the data store. + * + * @param string $type The data type. + * @param string $key The key. + */ + public function delete(string $type, string $key): void + { + unset(self::$store[$key]); + } + + + /** + * Clear any cached internal state. + */ + public static function clearInternalState(): void + { + self::$store = []; + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..8da00da --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + ./tests + + + + + + ./src + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..28c2413 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,6 @@ + 'SimpleSAML\TestUtils\InMemoryStore', + ]; + Configuration::loadFromArray($config, '[ARRAY]', 'simplesaml'); + + // when: getting the store + $store = Store::getInstance('SimpleSAML\TestUtils\InMemoryStore'); + + // then: will give us the right type of store + $this->assertInstanceOf(InMemoryStore::class, $store); + + // and: we can store stuff + $this->assertNull($store->get('string', 'key'), 'Key does not exist yet'); + $store->set('string', 'key', 'value'); + $this->assertEquals('value', $store->get('string', 'key')); + $store->delete('string', 'key'); + $this->assertNull($store->get('string', 'key'), 'Key was removed'); + } + + + public function testExpiration(): void + { + $store = new InMemoryStore(); + $store->set('string', 'key', 'value', time() + 1); + $this->assertEquals('value', $store->get('string', 'key')); + $this->assertEquals('value', $store->get('string', 'key')); + sleep(2); + $this->assertNull($store->get('string', 'key')); + } +} diff --git a/tools/composer-require-checker.json b/tools/composer-require-checker.json new file mode 100644 index 0000000..eed71aa --- /dev/null +++ b/tools/composer-require-checker.json @@ -0,0 +1,4 @@ +{ + "symbol-whitelist": [ + ] +}