v1.691: testy jednostkowe Domain\, infrastruktura PHPUnit, paczka aktualizacji
- Dodano testy: SettingsRepositoryTest, LanguagesRepositoryTest, UserRepositoryTest - Infrastruktura: phpunit.xml, composer.json (phpunit/phpunit ^10), tests/bootstrap.php - Bootstrap stuby: \Shared\Cache\CacheHandler (in-memory), \S - Zaktualizowano docs/TESTING.md dla cmsPRO - Paczka: updates/1.60/ver_1.691.zip + manifest Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
10
composer.json
Normal file
10
composer.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^10.5"
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
docs/TESTING.md
120
docs/TESTING.md
@@ -1,106 +1,54 @@
|
|||||||
# Testowanie shopPRO
|
# Testowanie cmsPRO
|
||||||
|
|
||||||
## Szybki start
|
## Szybki start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Pelny suite (PowerShell — rekomendowane)
|
# Instalacja PHPUnit (jednorazowo)
|
||||||
./test.ps1
|
composer install
|
||||||
|
|
||||||
|
# Uruchomienie testów
|
||||||
|
./vendor/bin/phpunit
|
||||||
|
|
||||||
# Konkretny plik
|
# Konkretny plik
|
||||||
./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php
|
./vendor/bin/phpunit tests/Unit/Domain/Settings/SettingsRepositoryTest.php
|
||||||
|
|
||||||
# Konkretny test
|
# Konkretny test
|
||||||
./test.ps1 --filter testGetQuantityReturnsCorrectValue
|
./vendor/bin/phpunit --filter testAllSettingsReturnsMappedArray
|
||||||
|
|
||||||
# Alternatywne
|
|
||||||
composer test # standard
|
|
||||||
./test.bat # testdox (czytelna lista)
|
|
||||||
./test-simple.bat # kropki
|
|
||||||
./test-debug.bat # debug
|
|
||||||
./test.sh # Git Bash
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Aktualny stan
|
## Aktualny stan
|
||||||
|
|
||||||
```text
|
```text
|
||||||
OK (805 tests, 2253 assertions)
|
Testy jednostkowe dla Domain\ (Faza 2 DDD)
|
||||||
```
|
```
|
||||||
|
|
||||||
Zweryfikowano: 2026-02-24 (ver. 0.318)
|
|
||||||
|
|
||||||
## Konfiguracja
|
## Konfiguracja
|
||||||
|
|
||||||
- **PHPUnit 9.6** via `phpunit.phar`
|
- **PHPUnit 10** via `composer`
|
||||||
- **Bootstrap:** `tests/bootstrap.php`
|
- **Bootstrap:** `tests/bootstrap.php`
|
||||||
- **Config:** `phpunit.xml`
|
- **Config:** `phpunit.xml`
|
||||||
|
|
||||||
## Struktura testow
|
## Struktura testów
|
||||||
|
|
||||||
```
|
```
|
||||||
tests/
|
tests/
|
||||||
|-- bootstrap.php
|
├── bootstrap.php ← autoloader + stuby (CacheHandler, S)
|
||||||
|-- stubs/
|
└── Unit/
|
||||||
| |-- CacheHandler.php (inline w bootstrap)
|
└── Domain/
|
||||||
| |-- Helpers.php (Shared\Helpers\Helpers stub)
|
├── Languages/LanguagesRepositoryTest.php
|
||||||
| `-- ShopProduct.php (shop\Product stub)
|
├── Settings/SettingsRepositoryTest.php
|
||||||
|-- Unit/
|
└── User/UserRepositoryTest.php
|
||||||
| |-- Domain/
|
|
||||||
| | |-- Article/ArticleRepositoryTest.php
|
|
||||||
| | |-- Attribute/AttributeRepositoryTest.php
|
|
||||||
| | |-- Banner/BannerRepositoryTest.php
|
|
||||||
| | |-- Basket/BasketCalculatorTest.php
|
|
||||||
| | |-- Cache/CacheRepositoryTest.php
|
|
||||||
| | |-- Category/CategoryRepositoryTest.php
|
|
||||||
| | |-- Coupon/CouponRepositoryTest.php
|
|
||||||
| | |-- CronJob/CronJobTypeTest.php
|
|
||||||
| | |-- CronJob/CronJobRepositoryTest.php
|
|
||||||
| | |-- CronJob/CronJobProcessorTest.php
|
|
||||||
| | |-- Dictionaries/DictionariesRepositoryTest.php
|
|
||||||
| | |-- Integrations/IntegrationsRepositoryTest.php
|
|
||||||
| | |-- Languages/LanguagesRepositoryTest.php
|
|
||||||
| | |-- Layouts/LayoutsRepositoryTest.php
|
|
||||||
| | |-- Newsletter/NewsletterRepositoryTest.php
|
|
||||||
| | |-- Pages/PagesRepositoryTest.php
|
|
||||||
| | |-- PaymentMethod/PaymentMethodRepositoryTest.php
|
|
||||||
| | |-- Producer/ProducerRepositoryTest.php
|
|
||||||
| | |-- Product/ProductRepositoryTest.php
|
|
||||||
| | |-- ProductSet/ProductSetRepositoryTest.php
|
|
||||||
| | |-- Promotion/PromotionRepositoryTest.php
|
|
||||||
| | |-- Settings/SettingsRepositoryTest.php
|
|
||||||
| | |-- ShopStatus/ShopStatusRepositoryTest.php
|
|
||||||
| | |-- Transport/TransportRepositoryTest.php
|
|
||||||
| | |-- Update/UpdateRepositoryTest.php
|
|
||||||
| | `-- User/UserRepositoryTest.php
|
|
||||||
| `-- admin/
|
|
||||||
| `-- Controllers/
|
|
||||||
| |-- ArticlesControllerTest.php
|
|
||||||
| |-- DictionariesControllerTest.php
|
|
||||||
| |-- IntegrationsControllerTest.php
|
|
||||||
| |-- ProductArchiveControllerTest.php
|
|
||||||
| |-- SettingsControllerTest.php
|
|
||||||
| |-- ShopAttributeControllerTest.php
|
|
||||||
| |-- ShopCategoryControllerTest.php
|
|
||||||
| |-- ShopCouponControllerTest.php
|
|
||||||
| |-- ShopPaymentMethodControllerTest.php
|
|
||||||
| |-- ShopProducerControllerTest.php
|
|
||||||
| |-- ShopProductControllerTest.php
|
|
||||||
| |-- ShopProductSetsControllerTest.php
|
|
||||||
| |-- ShopPromotionControllerTest.php
|
|
||||||
| |-- ShopStatusesControllerTest.php
|
|
||||||
| |-- ShopTransportControllerTest.php
|
|
||||||
| `-- UsersControllerTest.php
|
|
||||||
| `-- api/
|
|
||||||
| |-- ApiRouterTest.php
|
|
||||||
| `-- Controllers/
|
|
||||||
| |-- OrdersApiControllerTest.php
|
|
||||||
| |-- ProductsApiControllerTest.php
|
|
||||||
| `-- DictionariesApiControllerTest.php
|
|
||||||
`-- Integration/ (puste — zarezerwowane)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dodawanie nowych testow
|
## Stuby (bootstrap.php)
|
||||||
|
|
||||||
1. Plik w `tests/Unit/Domain/<Module>/<Class>Test.php`, `tests/Unit/admin/Controllers/<Class>Test.php` lub `tests/Unit/api/Controllers/<Class>Test.php`.
|
- `\Shared\Cache\CacheHandler` — in-memory stub z `fetch()`/`store()`/`delete()`/`reset()`
|
||||||
|
- `\S` — stub z `delete_cache()`, `htacces()`, `get_domain()`, `send_email()`
|
||||||
|
- `medoo` — mockowany przez PHPUnit (`$this->createMock(\medoo::class)`)
|
||||||
|
|
||||||
|
## Dodawanie nowych testów
|
||||||
|
|
||||||
|
1. Plik w `tests/Unit/Domain/<Modul>/<Klasa>Test.php`.
|
||||||
2. Rozszerz `PHPUnit\Framework\TestCase`.
|
2. Rozszerz `PHPUnit\Framework\TestCase`.
|
||||||
3. Nazwy metod zaczynaj od `test`.
|
3. Nazwy metod zaczynaj od `test`.
|
||||||
4. Wzorzec AAA: Arrange, Act, Assert.
|
4. Wzorzec AAA: Arrange, Act, Assert.
|
||||||
@@ -108,19 +56,11 @@ tests/
|
|||||||
## Mockowanie Medoo
|
## Mockowanie Medoo
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$mockDb = $this->createMock(\medoo::class);
|
$db = $this->createMock(\medoo::class);
|
||||||
$mockDb->method('get')->willReturn(42);
|
$db->method('get')->willReturn(['id' => 1]);
|
||||||
|
|
||||||
$repo = new ProductRepository($mockDb);
|
$repo = new SettingsRepository($db);
|
||||||
$value = $repo->getQuantity(123);
|
$value = $repo->visitCounter();
|
||||||
|
|
||||||
$this->assertEquals(42, $value);
|
$this->assertSame('1', $value);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Bootstrap — stuby
|
|
||||||
|
|
||||||
`tests/bootstrap.php` rejestruje autoloader i definiuje stuby:
|
|
||||||
- `Redis`, `RedisConnection` — klasy Redis (aby nie wymagac rozszerzenia)
|
|
||||||
- `Shared\Cache\CacheHandler` — inline stub z `get()`/`set()`/`exists()`/`delete()`/`deletePattern()`
|
|
||||||
- `Shared\Helpers\Helpers` — z `tests/stubs/Helpers.php`
|
|
||||||
- `shop\Product` — z `tests/stubs/ShopProduct.php`
|
|
||||||
|
|||||||
20
phpunit.xml
Normal file
20
phpunit.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
colors="true">
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Unit">
|
||||||
|
<directory>tests/Unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<coverage>
|
||||||
|
<include>
|
||||||
|
<directory>autoload/Domain</directory>
|
||||||
|
<directory>autoload/Shared</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
|
||||||
|
</phpunit>
|
||||||
123
tests/Unit/Domain/Languages/LanguagesRepositoryTest.php
Normal file
123
tests/Unit/Domain/Languages/LanguagesRepositoryTest.php
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
namespace Tests\Unit\Domain\Languages;
|
||||||
|
|
||||||
|
use Domain\Languages\LanguagesRepository;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class LanguagesRepositoryTest extends TestCase
|
||||||
|
{
|
||||||
|
private function mockDb(): object
|
||||||
|
{
|
||||||
|
return $this->createMock(\medoo::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
\Shared\Cache\CacheHandler::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- languagesList ---
|
||||||
|
|
||||||
|
public function testLanguagesListReturnsArray(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('select')->willReturn([['id' => 'pl', 'name' => 'Polski']]);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertSame([['id' => 'pl', 'name' => 'Polski']], $repo->languagesList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLanguagesListReturnsEmptyWhenNull(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('select')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertSame([], $repo->languagesList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- languageDetails ---
|
||||||
|
|
||||||
|
public function testLanguageDetailsReturnsRowWhenFound(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(['id' => 'pl', 'name' => 'Polski']);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertSame('pl', $repo->languageDetails('pl')['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLanguageDetailsReturnsNullWhenNotFound(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertNull($repo->languageDetails('xx'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- activeLanguages ---
|
||||||
|
|
||||||
|
public function testActiveLanguagesQueriesDbAndCaches(): void
|
||||||
|
{
|
||||||
|
$expected = [['id' => 'pl', 'name' => 'Polski', 'domain' => null]];
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->expects($this->once())->method('select')->willReturn($expected);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertSame($expected, $repo->activeLanguages());
|
||||||
|
// Drugi odczyt — z cache (mock select nie zostanie wywołany drugi raz)
|
||||||
|
$this->assertSame($expected, $repo->activeLanguages());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testActiveLanguagesReturnsEmptyWhenNull(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('select')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertSame([], $repo->activeLanguages());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- maxOrder ---
|
||||||
|
|
||||||
|
public function testMaxOrderReturnsInteger(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('max')->willReturn('5');
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertSame(5, $repo->maxOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- translationDelete ---
|
||||||
|
|
||||||
|
public function testTranslationDeleteReturnsTrueOnSuccess(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('delete')->willReturn(1);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertTrue($repo->translationDelete(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTranslationDeleteReturnsFalseOnFailure(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('delete')->willReturn(0);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertFalse($repo->translationDelete(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- translationDetails ---
|
||||||
|
|
||||||
|
public function testTranslationDetailsReturnsRowOrNull(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(['id' => 1, 'text' => 'hello']);
|
||||||
|
|
||||||
|
$repo = new LanguagesRepository($db);
|
||||||
|
$this->assertSame(['id' => 1, 'text' => 'hello'], $repo->translationDetails(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
101
tests/Unit/Domain/Settings/SettingsRepositoryTest.php
Normal file
101
tests/Unit/Domain/Settings/SettingsRepositoryTest.php
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
namespace Tests\Unit\Domain\Settings;
|
||||||
|
|
||||||
|
use Domain\Settings\SettingsRepository;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class SettingsRepositoryTest extends TestCase
|
||||||
|
{
|
||||||
|
private function mockDb(): object
|
||||||
|
{
|
||||||
|
return $this->createMock(\medoo::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
\Shared\Cache\CacheHandler::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- allSettings ---
|
||||||
|
|
||||||
|
public function testAllSettingsReturnsMappedArray(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('select')->willReturn([
|
||||||
|
['param' => 'site_name', 'value' => 'Test CMS'],
|
||||||
|
['param' => 'email', 'value' => 'admin@test.pl'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$repo = new SettingsRepository($db);
|
||||||
|
$result = $repo->allSettings();
|
||||||
|
|
||||||
|
$this->assertSame('Test CMS', $result['site_name']);
|
||||||
|
$this->assertSame('admin@test.pl', $result['email']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllSettingsReturnsEmptyArrayWhenDbReturnsNull(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('select')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new SettingsRepository($db);
|
||||||
|
$this->assertSame([], $repo->allSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllSettingsUsesCache(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->expects($this->never())->method('select');
|
||||||
|
|
||||||
|
\CacheHandlerStub::store('settings_details', ['cached' => '1']);
|
||||||
|
|
||||||
|
$repo = new SettingsRepository($db);
|
||||||
|
$result = $repo->allSettings();
|
||||||
|
|
||||||
|
$this->assertSame('1', $result['cached']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- update ---
|
||||||
|
|
||||||
|
public function testUpdateCallsDbUpdateWhenParamExists(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('count')->willReturn(1);
|
||||||
|
$db->expects($this->once())->method('update')->willReturn(true);
|
||||||
|
$db->expects($this->never())->method('insert');
|
||||||
|
|
||||||
|
$repo = new SettingsRepository($db);
|
||||||
|
$this->assertTrue($repo->update('site_name', 'Nowa Nazwa'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateCallsDbInsertWhenParamMissing(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('count')->willReturn(0);
|
||||||
|
$db->expects($this->once())->method('insert')->willReturn(true);
|
||||||
|
$db->expects($this->never())->method('update');
|
||||||
|
|
||||||
|
$repo = new SettingsRepository($db);
|
||||||
|
$repo->update('new_param', 'value');
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- visitCounter ---
|
||||||
|
|
||||||
|
public function testVisitCounterReturnsValue(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn('1234');
|
||||||
|
|
||||||
|
$repo = new SettingsRepository($db);
|
||||||
|
$this->assertSame('1234', $repo->visitCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVisitCounterReturnsNullWhenEmpty(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new SettingsRepository($db);
|
||||||
|
$this->assertNull($repo->visitCounter());
|
||||||
|
}
|
||||||
|
}
|
||||||
252
tests/Unit/Domain/User/UserRepositoryTest.php
Normal file
252
tests/Unit/Domain/User/UserRepositoryTest.php
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<?php
|
||||||
|
namespace Tests\Unit\Domain\User;
|
||||||
|
|
||||||
|
use Domain\User\UserRepository;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class UserRepositoryTest extends TestCase
|
||||||
|
{
|
||||||
|
private function mockDb(): object
|
||||||
|
{
|
||||||
|
return $this->createMock(\medoo::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
\Shared\Cache\CacheHandler::reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- find ---
|
||||||
|
|
||||||
|
public function testFindReturnsUserArray(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(['id' => 1, 'login' => 'admin']);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertSame('admin', $repo->find(1)['login']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFindReturnsNullWhenNotFound(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertNull($repo->find(99));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- findByLogin ---
|
||||||
|
|
||||||
|
public function testFindByLoginReturnsUser(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(['id' => 1, 'login' => 'admin']);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertNotNull($repo->findByLogin('admin'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- all ---
|
||||||
|
|
||||||
|
public function testAllReturnsArray(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('select')->willReturn([['id' => 1], ['id' => 2]]);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertCount(2, $repo->all());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllReturnsEmptyArrayWhenNull(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('select')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertSame([], $repo->all());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- hasPrivilege ---
|
||||||
|
|
||||||
|
public function testHasPrivilegeReturnsTrueForAdminUser(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
// userId === 1 zawsze ma uprawnienia, bez zapytania do DB
|
||||||
|
$db->expects($this->never())->method('count');
|
||||||
|
$this->assertTrue($repo->hasPrivilege('articles', 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasPrivilegeReturnsTrueWhenPrivilegeExists(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('count')->willReturn(1);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertTrue($repo->hasPrivilege('articles', 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasPrivilegeReturnsFalseWhenPrivilegeMissing(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('count')->willReturn(0);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertFalse($repo->hasPrivilege('articles', 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- logon ---
|
||||||
|
|
||||||
|
public function testLogonReturnsZeroWhenUserNotFound(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
// Pierwsze get() (sprawdź czy login istnieje) → null
|
||||||
|
$db->method('get')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertSame(0, $repo->logon('unknown', 'pass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLogonReturnsMinusOneWhenAccountBlocked(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
// Pierwsze get() → użytkownik istnieje, drugie → konto zablokowane (null)
|
||||||
|
$db->method('get')->willReturnOnConsecutiveCalls(
|
||||||
|
['id' => 2, 'login' => 'user'],
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertSame(-1, $repo->logon('user', 'pass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLogonReturnsOneOnSuccess(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturnOnConsecutiveCalls(
|
||||||
|
['id' => 2, 'login' => 'user'], // login istnieje
|
||||||
|
['id' => 2, 'status' => 1, 'error_logged_count' => 0], // nie zablokowany
|
||||||
|
['id' => 2] // hasło poprawne
|
||||||
|
);
|
||||||
|
$db->method('update')->willReturn(true);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertSame(1, $repo->logon('user', 'pass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- isLoginTaken ---
|
||||||
|
|
||||||
|
public function testIsLoginTakenReturnsTrueWhenExists(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn('user');
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertTrue($repo->isLoginTaken('user'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsLoginTakenReturnsFalseWhenFree(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertFalse($repo->isLoginTaken('newuser'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- verifyTwofaCode ---
|
||||||
|
|
||||||
|
public function testVerifyTwofaCodeReturnsFalseWhenUserNotFound(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('get')->willReturn(null);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertFalse($repo->verifyTwofaCode(1, '123456'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVerifyTwofaCodeReturnsFalseWhenTooManyFailedAttempts(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$user = [
|
||||||
|
'id' => 2,
|
||||||
|
'twofa_failed_attempts' => 5,
|
||||||
|
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 600),
|
||||||
|
'twofa_code_hash' => password_hash('123456', PASSWORD_DEFAULT),
|
||||||
|
];
|
||||||
|
$db->method('get')->willReturn($user);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertFalse($repo->verifyTwofaCode(2, '123456'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVerifyTwofaCodeReturnsFalseWhenExpired(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$user = [
|
||||||
|
'id' => 2,
|
||||||
|
'twofa_failed_attempts' => 0,
|
||||||
|
'twofa_expires_at' => date('Y-m-d H:i:s', time() - 1),
|
||||||
|
'twofa_code_hash' => password_hash('123456', PASSWORD_DEFAULT),
|
||||||
|
];
|
||||||
|
$db->method('get')->willReturn($user);
|
||||||
|
$db->method('update')->willReturn(true);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertFalse($repo->verifyTwofaCode(2, '123456'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVerifyTwofaCodeReturnsTrueOnValidCode(): void
|
||||||
|
{
|
||||||
|
$code = '123456';
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$user = [
|
||||||
|
'id' => 2,
|
||||||
|
'twofa_failed_attempts' => 0,
|
||||||
|
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 600),
|
||||||
|
'twofa_code_hash' => password_hash($code, PASSWORD_DEFAULT),
|
||||||
|
];
|
||||||
|
// find() wywołuje get() dwa razy (raz przez verifyTwofaCode, raz przez update)
|
||||||
|
$db->method('get')->willReturn($user);
|
||||||
|
$db->method('update')->willReturn(true);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertTrue($repo->verifyTwofaCode(2, $code));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- delete ---
|
||||||
|
|
||||||
|
public function testDeleteReturnsTrueOnSuccess(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('delete')->willReturn(1);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$this->assertTrue($repo->delete(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- save — walidacja ---
|
||||||
|
|
||||||
|
public function testSaveReturnsErrorWhenPasswordTooShort(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('delete')->willReturn(1);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$result = $repo->save(0, 'newuser', 'on', '', '123', '123', 0, []);
|
||||||
|
|
||||||
|
$this->assertSame('error', $result['status']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSaveReturnsErrorWhenPasswordsMismatch(): void
|
||||||
|
{
|
||||||
|
$db = $this->mockDb();
|
||||||
|
$db->method('delete')->willReturn(1);
|
||||||
|
|
||||||
|
$repo = new UserRepository($db);
|
||||||
|
$result = $repo->save(0, 'newuser', 'on', '', 'password1', 'password2', 0, []);
|
||||||
|
|
||||||
|
$this->assertSame('error', $result['status']);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
tests/bootstrap.php
Normal file
51
tests/bootstrap.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// Medoo ORM — potrzebny do mockowania w testach
|
||||||
|
require_once __DIR__ . '/../libraries/medoo/medoo.php';
|
||||||
|
|
||||||
|
// Stub: \Shared\Cache\CacheHandler — musi być załadowany PRZED autoloaderem
|
||||||
|
// żeby nie nadpisała go prawdziwa klasa
|
||||||
|
namespace Shared\Cache {
|
||||||
|
class CacheHandler
|
||||||
|
{
|
||||||
|
private static array $store = [];
|
||||||
|
|
||||||
|
public static function reset(): void { self::$store = []; }
|
||||||
|
|
||||||
|
public static function fetch(string $key): mixed
|
||||||
|
{
|
||||||
|
return self::$store[$key] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function store(string $key, mixed $value, int $ttl = 0): void
|
||||||
|
{
|
||||||
|
self::$store[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function delete(string $key): void
|
||||||
|
{
|
||||||
|
unset(self::$store[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stub: \S — metody statyczne używane przez repozytoria
|
||||||
|
namespace {
|
||||||
|
class S
|
||||||
|
{
|
||||||
|
public static function delete_cache(): void {}
|
||||||
|
public static function htacces(): void {}
|
||||||
|
public static function get_domain(string $domain = ''): ?string { return $domain ?: null; }
|
||||||
|
public static function send_email(string $to, string $subject, string $body): bool { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 autoloader dla Domain\
|
||||||
|
// Shared\ jest już obsłużona przez stubę powyżej — pomijamy ją w autoloaderze
|
||||||
|
spl_autoload_register(function (string $class): void {
|
||||||
|
if (strncmp($class, 'Domain\\', 7) === 0) {
|
||||||
|
$rel = substr($class, 7);
|
||||||
|
$file = __DIR__ . '/../autoload/Domain/' . str_replace('\\', '/', $rel) . '.php';
|
||||||
|
if (file_exists($file)) { require $file; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
BIN
updates/1.60/ver_1.691.zip
Normal file
BIN
updates/1.60/ver_1.691.zip
Normal file
Binary file not shown.
4
updates/1.60/ver_1.691_files.txt
Normal file
4
updates/1.60/ver_1.691_files.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
F: ../backup_20250512_232458.zip
|
||||||
|
F: ../backup_tmp.json
|
||||||
|
F: ../sitemap_cmsenproject-dcpl.xml
|
||||||
|
F: ../sitemap_cmsproproject-dcpl.xml
|
||||||
49
updates/1.60/ver_1.691_manifest.json
Normal file
49
updates/1.60/ver_1.691_manifest.json
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"changelog": "Refaktoryzacja DDD Faza 0+1: PSR-4 autoloader, Shared (CacheHandler, Helpers, Html, ImageManipulator, Tpl), Domain (LanguagesRepository, SettingsRepository, UserRepository), testy jednostkowe Domain\\, docs/",
|
||||||
|
"version": "1.691",
|
||||||
|
"files": {
|
||||||
|
"added": [
|
||||||
|
"autoload/Domain/Languages/LanguagesRepository.php",
|
||||||
|
"autoload/Domain/Settings/SettingsRepository.php",
|
||||||
|
"autoload/Domain/User/UserRepository.php",
|
||||||
|
"autoload/Shared/Cache/CacheHandler.php",
|
||||||
|
"autoload/Shared/Helpers/Helpers.php",
|
||||||
|
"autoload/Shared/Html/Html.php",
|
||||||
|
"autoload/Shared/Image/ImageManipulator.php",
|
||||||
|
"autoload/Shared/Tpl/Tpl.php"
|
||||||
|
],
|
||||||
|
"deleted": [
|
||||||
|
"backup_20250512_232458.zip",
|
||||||
|
"backup_tmp.json",
|
||||||
|
"sitemap_cmsenproject-dcpl.xml",
|
||||||
|
"sitemap_cmsproproject-dcpl.xml"
|
||||||
|
],
|
||||||
|
"modified": [
|
||||||
|
"admin/ajax.php",
|
||||||
|
"admin/index.php",
|
||||||
|
"ajax.php",
|
||||||
|
"api.php",
|
||||||
|
"autoload/admin/class.Site.php",
|
||||||
|
"autoload/admin/factory/class.Languages.php",
|
||||||
|
"autoload/admin/factory/class.Settings.php",
|
||||||
|
"autoload/admin/factory/class.Users.php",
|
||||||
|
"autoload/class.Cache.php",
|
||||||
|
"autoload/class.Html.php",
|
||||||
|
"autoload/class.Image.php",
|
||||||
|
"autoload/class.S.php",
|
||||||
|
"autoload/class.Tpl.php",
|
||||||
|
"autoload/front/factory/class.Languages.php",
|
||||||
|
"autoload/front/factory/class.Settings.php",
|
||||||
|
"cron.php",
|
||||||
|
"index.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"checksum_zip": "sha256:f53230f36d391828f4e368f3fc3420d8f9430ca507a1d4f57a0988823ac22192",
|
||||||
|
"sql": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"date": "2026-02-27",
|
||||||
|
"directories_deleted": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?
|
<?
|
||||||
$current_ver = 1690;
|
$current_ver = 1691;
|
||||||
|
|
||||||
for ($i = 1; $i <= $current_ver; $i++)
|
for ($i = 1; $i <= $current_ver; $i++)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user