feat(cloud): P1-B scaffold — composer autoload + Config

This commit is contained in:
유창욱 2026-07-11 23:16:13 +09:00
parent 65344f6ea6
commit 50cb2cfe82
6 changed files with 70 additions and 0 deletions

4
cloud/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/vendor/
/config.php
/storage/snapshots/*
!/storage/snapshots/.gitkeep

8
cloud/composer.json Normal file
View 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
View 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
View 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
View 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); }
}

View 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());
}
}