From 81cd355df66273efffc43427c136f1141a897597 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 | 151 +++++++++++++++++++++++++++- composer.json | 5 + 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, 322 insertions(+), 2 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..0f40565 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -74,8 +74,8 @@ 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 composer.json - name: Check code for unused dependencies in composer.json run: | @@ -141,3 +141,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..2e7e937 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,11 @@ "SimpleSAML\\TestUtils\\": "lib/" } }, + "autoload-dev": { + "psr-4": { + "SimpleSAML\\Test\\TestUtils\\": "tests/src/" + } + }, "require": { "phpunit/phpunit": "^10.0 || ^11.0", "slevomat/coding-standard": "^8.15", 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": [ + ] +}