Skip to content

Commit

Permalink
Import InMemoryStore from Cirrus Identity
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdijen committed May 9, 2024
1 parent c5453fe commit 9d3433c
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 10 deletions.
157 changes: 153 additions & 4 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ jobs:
# To report GitHub Actions status checks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LINTER_RULES_PATH: 'tools/linters'
LOG_LEVEL: NOTICE
VALIDATE_ALL_CODEBASE: true
VALIDATE_CSS: true
VALIDATE_JAVASCRIPT_ES: true
VALIDATE_JSON: true
VALIDATE_PHP_BUILTIN: true
VALIDATE_YAML: true
VALIDATE_XML: true
VALIDATE_GITHUB_ACTIONS: true

quality:
Expand Down Expand Up @@ -74,13 +76,13 @@ 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 \
--excludePackage=slevomat/coding-standard
--excludePackage=slevomat/coding-standard \
--excludePackage=symfony/phpunit-bridge
- name: PHP Code Sniffer
Expand Down Expand Up @@ -141,3 +143,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
16 changes: 10 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@
"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": "*",

"simplesamlphp/simplesamlphp": "^2.2.0"
"phpunit/phpunit": "^10.0 || ^11.0",
"psr/log": "^2.0 || ^3.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",
Expand Down
79 changes: 79 additions & 0 deletions lib/InMemoryStore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\TestUtils;

use SimpleSAML\Store\StoreInterface;
use SimpleSAML\Utils\ClearableState;

/**
* Class InMemoryStore. Used for testing SSP code.
*
* Use it by setting `'store.type'` in config.php to this clas
*/
class InMemoryStore implements ClearableState, StoreInterface
{
private static array $store = [];


/**
* Retrieve a value from the data store.
*
* @param string $type The data type.
* @param string $key The key.
*
* @return mixed|null The value.
*/
public function get(string $type, string $key): mixed
{
if (array_key_exists($key, self::$store)) {
$item = self::$store[$key];

Check warning on line 31 in lib/InMemoryStore.php

View workflow job for this annotation

GitHub Actions / Quality control

MixedAssignment

lib/InMemoryStore.php:31:13: MixedAssignment: Unable to determine the type that $item is being assigned to (see https://psalm.dev/032)
if (isset($item['expire']) && $item['expire'] < time()) {
$this->delete($type, $key);
return null;
}
return $item['value'];

Check warning on line 36 in lib/InMemoryStore.php

View workflow job for this annotation

GitHub Actions / Quality control

MixedArrayAccess

lib/InMemoryStore.php:36:20: MixedArrayAccess: Cannot access array value on mixed variable $item (see https://psalm.dev/051)
}
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 = [];
}
}
21 changes: 21 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="tests/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache">
<coverage>
<report>
<clover outputFile="build/logs/clover.xml"/>
<html outputDirectory="build/coverage" lowUpperBound="35" highLowerBound="70"/>
<text outputFile="php://stdout" showUncoveredFiles="true"/>
</report>
</coverage>
<testsuites>
<testsuite name="The project's test suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<logging/>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
6 changes: 6 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

declare(strict_types=1);

$projectRoot = dirname(__DIR__);
require_once($projectRoot . '/vendor/autoload.php');
58 changes: 58 additions & 0 deletions tests/src/InMemoryStoreTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\Test\TestUtils;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use SimpleSAML\Configuration;
use SimpleSAML\Store\StoreFactory;
use SimpleSAML\TestUtils\InMemoryStore;

use function sleep;
use function time;

#[CoversClass(InMemoryStore::class)]
class InMemoryStoreTest extends TestCase
{
protected function tearDown(): void
{
InMemoryStore::clearInternalState();
Configuration::clearInternalState();
}


public function testState(): void
{
// given: an SSP config that overrides the store type
$config = [
'store.type' => 'SimpleSAML\TestUtils\InMemoryStore',
];
Configuration::loadFromArray($config, '[ARRAY]', 'simplesaml');

// when: getting the store
$store = StoreFactory::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'));
}
}
4 changes: 4 additions & 0 deletions tools/composer-require-checker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"symbol-whitelist": [
]
}

0 comments on commit 9d3433c

Please sign in to comment.