feat(cloud): P1-B scaffold — composer autoload + Config
This commit is contained in:
parent
65344f6ea6
commit
50cb2cfe82
6 changed files with 70 additions and 0 deletions
4
cloud/.gitignore
vendored
Normal file
4
cloud/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
/vendor/
|
||||||
|
/config.php
|
||||||
|
/storage/snapshots/*
|
||||||
|
!/storage/snapshots/.gitkeep
|
||||||
8
cloud/composer.json
Normal file
8
cloud/composer.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "move-detect/cloud",
|
||||||
|
"type": "project",
|
||||||
|
"require": { "php": ">=8.2" },
|
||||||
|
"require-dev": { "phpunit/phpunit": "^11" },
|
||||||
|
"autoload": { "psr-4": { "App\\": "src/" } },
|
||||||
|
"autoload-dev": { "psr-4": { "App\\Tests\\": "tests/" } }
|
||||||
|
}
|
||||||
9
cloud/config.example.php
Normal file
9
cloud/config.example.php
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'db_dsn' => 'mysql:host=localhost;dbname=move_detect;charset=utf8mb4',
|
||||||
|
'db_user' => 'CHANGEME',
|
||||||
|
'db_pass' => 'CHANGEME',
|
||||||
|
'snapshot_dir' => __DIR__ . '/storage/snapshots',
|
||||||
|
'timestamp_window' => 300,
|
||||||
|
'rate_limit_per_min' => 60,
|
||||||
|
];
|
||||||
7
cloud/phpunit.xml
Normal file
7
cloud/phpunit.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
bootstrap="vendor/autoload.php" colors="true">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="unit"><directory>tests</directory></testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
||||||
18
cloud/src/Config.php
Normal file
18
cloud/src/Config.php
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
final class Config
|
||||||
|
{
|
||||||
|
private function __construct(private array $a) {}
|
||||||
|
|
||||||
|
public static function fromArray(array $a): self { return new self($a); }
|
||||||
|
|
||||||
|
public function dbDsn(): string { return (string)($this->a['db_dsn'] ?? ''); }
|
||||||
|
public function dbUser(): string { return (string)($this->a['db_user'] ?? ''); }
|
||||||
|
public function dbPass(): string { return (string)($this->a['db_pass'] ?? ''); }
|
||||||
|
public function snapshotDir(): string { return (string)($this->a['snapshot_dir'] ?? __DIR__ . '/../storage/snapshots'); }
|
||||||
|
public function nonceTtl(): int { return (int)($this->a['nonce_ttl'] ?? 86400); }
|
||||||
|
public function timestampWindow(): int { return (int)($this->a['timestamp_window'] ?? 300); }
|
||||||
|
public function rateLimitPerMin(): int { return (int)($this->a['rate_limit_per_min'] ?? 60); }
|
||||||
|
}
|
||||||
24
cloud/tests/ConfigTest.php
Normal file
24
cloud/tests/ConfigTest.php
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace App\Tests;
|
||||||
|
use App\Config;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class ConfigTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testTypedGettersWithDefaults(): void
|
||||||
|
{
|
||||||
|
$c = Config::fromArray(['db_dsn' => 'sqlite::memory:']);
|
||||||
|
$this->assertSame('sqlite::memory:', $c->dbDsn());
|
||||||
|
$this->assertSame(300, $c->timestampWindow());
|
||||||
|
$this->assertSame(60, $c->rateLimitPerMin());
|
||||||
|
$this->assertSame(86400, $c->nonceTtl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOverrides(): void
|
||||||
|
{
|
||||||
|
$c = Config::fromArray(['db_dsn' => 'x', 'timestamp_window' => 120, 'rate_limit_per_min' => 30]);
|
||||||
|
$this->assertSame(120, $c->timestampWindow());
|
||||||
|
$this->assertSame(30, $c->rateLimitPerMin());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue