mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-06-01 20:31:38 -04:00
* feat(cli): add reconfigure-user.php to read/write per-user config attributes Closes #8869. Adds `cli/reconfigure-user.php`, a first-class CLI for per-user configuration attributes — the user-level equivalent of the existing `reconfigure.php` (system config). ### Usage ```sh # List all attributes (sensitive keys redacted by default) ./cli/reconfigure-user.php --user alice --list ./cli/reconfigure-user.php --user alice --list --show-secrets # Read a single attribute (exit 2 if key not found) ./cli/reconfigure-user.php --user alice --key language # Set an attribute (type inferred from existing value: bool, int, string) ./cli/reconfigure-user.php --user alice --key language --set --value fr # Set from stdin (recommended for secrets — keeps value out of shell history / ps) ./cli/reconfigure-user.php --user alice --key some_token --set --value-stdin < token.txt # Create a new key, e.g. for an extension (unknown keys rejected by default) ./cli/reconfigure-user.php --user alice --key my_ext_setting --set --value hello --force # Delete an attribute (exit 2 if key not found) ./cli/reconfigure-user.php --user alice --key some_token --unset ``` ### Changes - `cli/reconfigure-user.php` — new command - `lib/Minz/Configuration::toArray()` — exposes the full config array (used by `--list`) - `cli/README.md` — documents the new command - `tests/cli/UserConfigOptionsParserTest.php` — PHPUnit tests for the options parser, following the existing `CliOptionsParserTest` pattern (shared `cli-parser-test.php` helper) ### Test plan - `make test-all` passes - Tested manually against a local FreshRSS instance: `--list`, `--key` (get), `--set` (bool/int/string inference), `--value-stdin`, `--unset`, `--force`, error paths (unknown key without `--force`, wrong type) * unserialize allowed_classes --------- Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
114 lines
3.8 KiB
PHP
114 lines
3.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
require_once dirname(__DIR__, 2) . '/cli/CliOption.php';
|
|
require_once dirname(__DIR__, 2) . '/cli/CliOptionsParser.php';
|
|
|
|
final class UserConfigCliOptionsTest extends CliOptionsParser {
|
|
public string $user;
|
|
public string $key;
|
|
public string $value;
|
|
public bool $list;
|
|
public bool $set;
|
|
public bool $unset;
|
|
public bool $valueStdin;
|
|
public bool $force;
|
|
public bool $showSecrets;
|
|
|
|
public function __construct() {
|
|
$this->addRequiredOption('user', (new CliOption('user')));
|
|
$this->addOption('key', (new CliOption('key')));
|
|
$this->addOption('value', (new CliOption('value')));
|
|
$this->addOption('list', (new CliOption('list'))->withValueNone());
|
|
$this->addOption('set', (new CliOption('set'))->withValueNone());
|
|
$this->addOption('unset', (new CliOption('unset'))->withValueNone());
|
|
$this->addOption('valueStdin', (new CliOption('value-stdin'))->withValueNone());
|
|
$this->addOption('force', (new CliOption('force'))->withValueNone());
|
|
$this->addOption('showSecrets', (new CliOption('show-secrets'))->withValueNone());
|
|
parent::__construct();
|
|
}
|
|
}
|
|
|
|
class UserConfigOptionsParserTest extends TestCase {
|
|
|
|
public static function testUserIsRequired(): void {
|
|
$result = self::runOptions('');
|
|
self::assertArrayHasKey('user', $result->errors);
|
|
}
|
|
|
|
public static function testUserProvided(): void {
|
|
$result = self::runOptions('--user=alice');
|
|
self::assertEmpty($result->errors);
|
|
self::assertSame('alice', $result->user);
|
|
}
|
|
|
|
public static function testListFlag(): void {
|
|
$result = self::runOptions('--user=alice --list');
|
|
self::assertTrue($result->list);
|
|
self::assertFalse($result->set);
|
|
self::assertFalse($result->unset);
|
|
}
|
|
|
|
public static function testShowSecretsFlag(): void {
|
|
$result = self::runOptions('--user=alice --list --show-secrets');
|
|
self::assertTrue($result->list);
|
|
self::assertTrue($result->showSecrets);
|
|
}
|
|
|
|
public static function testSetFlagWithValue(): void {
|
|
$result = self::runOptions('--user=alice --key=language --set --value=fr');
|
|
self::assertTrue($result->set);
|
|
self::assertFalse($result->list);
|
|
self::assertFalse($result->unset);
|
|
self::assertSame('language', $result->key);
|
|
self::assertSame('fr', $result->value);
|
|
}
|
|
|
|
public static function testUnsetFlag(): void {
|
|
$result = self::runOptions('--user=alice --key=language --unset');
|
|
self::assertTrue($result->unset);
|
|
self::assertFalse($result->set);
|
|
}
|
|
|
|
public static function testValueStdinFlag(): void {
|
|
$result = self::runOptions('--user=alice --key=token --set --value-stdin');
|
|
self::assertTrue($result->set);
|
|
self::assertTrue($result->valueStdin);
|
|
}
|
|
|
|
public static function testForceFlag(): void {
|
|
$result = self::runOptions('--user=alice --key=custom --set --value=hello --force');
|
|
self::assertTrue($result->set);
|
|
self::assertTrue($result->force);
|
|
}
|
|
|
|
public static function testGetKey(): void {
|
|
$result = self::runOptions('--user=alice --key=language');
|
|
self::assertEmpty($result->errors);
|
|
self::assertSame('language', $result->key);
|
|
self::assertFalse($result->set);
|
|
self::assertFalse($result->unset);
|
|
self::assertFalse($result->list);
|
|
}
|
|
|
|
public static function testUnknownOptionReturnsError(): void {
|
|
$result = self::runOptions('--user=alice --unknown');
|
|
self::assertArrayHasKey('unknown', $result->errors);
|
|
}
|
|
|
|
private static function runOptions(string $cliOptions = ''): UserConfigCliOptionsTest {
|
|
$command = __DIR__ . '/cli-parser-test.php';
|
|
$className = UserConfigCliOptionsTest::class;
|
|
|
|
$result = shell_exec("CLI_PARSER_TEST_OPTIONS_CLASS='$className' $command $cliOptions 2>/dev/null");
|
|
$result = is_string($result) ?
|
|
unserialize($result, ['allowed_classes' => [UserConfigCliOptionsTest::class]]) :
|
|
new UserConfigCliOptionsTest();
|
|
|
|
/** @var UserConfigCliOptionsTest $result */
|
|
return $result;
|
|
}
|
|
}
|