API updadte
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -84,3 +84,13 @@ docker-compose.override.yml
|
|||||||
/var/
|
/var/
|
||||||
/vendor/
|
/vendor/
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
||||||
|
###> phpunit/phpunit ###
|
||||||
|
/phpunit.xml
|
||||||
|
/.phpunit.cache/
|
||||||
|
###< phpunit/phpunit ###
|
||||||
|
|
||||||
|
###> friendsofphp/php-cs-fixer ###
|
||||||
|
/.php-cs-fixer.php
|
||||||
|
/.php-cs-fixer.cache
|
||||||
|
###< friendsofphp/php-cs-fixer ###
|
||||||
|
|||||||
30
.php-cs-fixer.dist.php
Normal file
30
.php-cs-fixer.dist.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$finder = (new PhpCsFixer\Finder())
|
||||||
|
->in(__DIR__)
|
||||||
|
->exclude('var')
|
||||||
|
->exclude('vendor')
|
||||||
|
->exclude('migrations')
|
||||||
|
->notPath('bin/console')
|
||||||
|
->notPath('public/index.php')
|
||||||
|
;
|
||||||
|
|
||||||
|
return (new PhpCsFixer\Config())
|
||||||
|
->setRules([
|
||||||
|
'@Symfony' => true,
|
||||||
|
'array_syntax' => ['syntax' => 'short'],
|
||||||
|
'ordered_imports' => ['sort_algorithm' => 'alpha'],
|
||||||
|
'no_unused_imports' => true,
|
||||||
|
'not_operator_with_successor_space' => true,
|
||||||
|
'trailing_comma_in_multiline' => true,
|
||||||
|
'phpdoc_scalar' => true,
|
||||||
|
'unary_operator_spaces' => true,
|
||||||
|
'binary_operator_spaces' => true,
|
||||||
|
'blank_line_before_statement' => [
|
||||||
|
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
|
||||||
|
],
|
||||||
|
'phpdoc_single_line_var_spacing' => true,
|
||||||
|
'phpdoc_var_without_name' => true,
|
||||||
|
])
|
||||||
|
->setFinder($finder)
|
||||||
|
;
|
||||||
102
README.md
102
README.md
@@ -14,6 +14,8 @@ Eine moderne Webanwendung zur Immobilienberechnung, entwickelt mit Symfony 7.3,
|
|||||||
- **Öffentliche Ressourcen**: Bundesländer und Heizungstypen ohne Authentifizierung abrufbar
|
- **Öffentliche Ressourcen**: Bundesländer und Heizungstypen ohne Authentifizierung abrufbar
|
||||||
- **Docker-Setup**: Vollständig containerisierte Entwicklungsumgebung
|
- **Docker-Setup**: Vollständig containerisierte Entwicklungsumgebung
|
||||||
- **Datenbank-Migrationen**: Versionskontrollierte Datenbankschema-Verwaltung mit Doctrine
|
- **Datenbank-Migrationen**: Versionskontrollierte Datenbankschema-Verwaltung mit Doctrine
|
||||||
|
- **Testing**: Umfassende Test-Suite mit PHPUnit für Unit- und Funktions-Tests
|
||||||
|
- **Code Quality**: PHP-CS-Fixer für konsistenten Code-Style nach Symfony Standards
|
||||||
- **CORS-Unterstützung**: Konfigurierbare CORS-Einstellungen für API-Zugriffe
|
- **CORS-Unterstützung**: Konfigurierbare CORS-Einstellungen für API-Zugriffe
|
||||||
- **phpMyAdmin**: Integriertes Datenbank-Verwaltungstool
|
- **phpMyAdmin**: Integriertes Datenbank-Verwaltungstool
|
||||||
|
|
||||||
@@ -182,6 +184,106 @@ Wichtigste Endpunkte:
|
|||||||
- Grundbuchkosten: ca. 0,5% des Kaufpreises
|
- Grundbuchkosten: ca. 0,5% des Kaufpreises
|
||||||
- Grunderwerbsteuer: abhängig vom Bundesland (3,5% - 6,5%)
|
- Grunderwerbsteuer: abhängig vom Bundesland (3,5% - 6,5%)
|
||||||
|
|
||||||
|
## Testing & Code Quality
|
||||||
|
|
||||||
|
Das Projekt verwendet **PHPUnit** für Unit- und Funktionstests sowie **PHP-CS-Fixer** für Code-Qualität und Linting.
|
||||||
|
|
||||||
|
### Tests ausführen
|
||||||
|
|
||||||
|
#### Alle Tests ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose exec web php bin/phpunit
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Tests mit Ausgabedetails
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose exec web php bin/phpunit --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Nur bestimmte Testklassen ausführen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Entity-Tests
|
||||||
|
docker-compose exec web php bin/phpunit tests/Entity
|
||||||
|
|
||||||
|
# API-Tests
|
||||||
|
docker-compose exec web php bin/phpunit tests/Api
|
||||||
|
|
||||||
|
# Einzelne Testklasse
|
||||||
|
docker-compose exec web php bin/phpunit tests/Entity/UserTest.php
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Code Coverage Report (optional)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose exec web php bin/phpunit --coverage-text
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code-Linting mit PHP-CS-Fixer
|
||||||
|
|
||||||
|
#### Code-Style prüfen (Dry-Run)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose exec web vendor/bin/php-cs-fixer fix --dry-run --diff
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Code automatisch formatieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose exec web vendor/bin/php-cs-fixer fix
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Bestimmte Verzeichnisse prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Nur src/ Verzeichnis
|
||||||
|
docker-compose exec web vendor/bin/php-cs-fixer fix src --dry-run
|
||||||
|
|
||||||
|
# Nur tests/ Verzeichnis
|
||||||
|
docker-compose exec web vendor/bin/php-cs-fixer fix tests --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test-Struktur
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/
|
||||||
|
├── Entity/ # Unit-Tests für Entities
|
||||||
|
│ ├── UserTest.php # User-Entity Tests
|
||||||
|
│ ├── ImmobilieTest.php # Immobilie-Entity Tests (inkl. Kaufnebenkosten)
|
||||||
|
│ ├── BundeslandTest.php # Bundesland-Entity Tests
|
||||||
|
│ └── HeizungstypTest.php # Heizungstyp-Entity Tests
|
||||||
|
└── Api/ # Funktions-/API-Tests
|
||||||
|
├── BundeslandApiTest.php # Bundesländer API-Tests
|
||||||
|
├── HeizungstypApiTest.php # Heizungstypen API-Tests
|
||||||
|
└── ApiDocumentationTest.php # API-Dokumentations-Tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test-Abdeckung
|
||||||
|
|
||||||
|
Die Tests decken folgende Bereiche ab:
|
||||||
|
|
||||||
|
**Entity-Tests:**
|
||||||
|
- User-Entity: API-Key-Generierung, Rollen, UserInterface-Methoden
|
||||||
|
- Immobilie-Entity: Gesamtflächen-Berechnung, Kaufnebenkosten-Berechnung
|
||||||
|
- Bundesland-Entity: Grunderwerbsteuer-Werte für alle Bundesländer
|
||||||
|
- Heizungstyp-Entity: CRUD-Operationen
|
||||||
|
|
||||||
|
**API-Tests:**
|
||||||
|
- Öffentlicher Zugriff auf Bundesländer und Heizungstypen (GET)
|
||||||
|
- Authentifizierung für CREATE-Operationen
|
||||||
|
- API-Dokumentation Zugänglichkeit
|
||||||
|
|
||||||
|
### Code-Style Regeln
|
||||||
|
|
||||||
|
Die PHP-CS-Fixer-Konfiguration (`.php-cs-fixer.dist.php`) verwendet:
|
||||||
|
- Symfony Coding Standards
|
||||||
|
- Short Array Syntax
|
||||||
|
- Sortierte Imports
|
||||||
|
- Trailing Commas in Multiline Arrays
|
||||||
|
- Und weitere PSR-12 kompatible Regeln
|
||||||
|
|
||||||
## Entwicklung
|
## Entwicklung
|
||||||
|
|
||||||
### Neue Entity erstellen
|
### Neue Entity erstellen
|
||||||
|
|||||||
4
bin/phpunit
Normal file
4
bin/phpunit
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit';
|
||||||
@@ -45,5 +45,11 @@
|
|||||||
"cache:clear": "symfony-cmd",
|
"cache:clear": "symfony-cmd",
|
||||||
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^12.4",
|
||||||
|
"symfony/browser-kit": "^7.3",
|
||||||
|
"symfony/css-selector": "^7.3",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.89"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3281
composer.lock
generated
3281
composer.lock
generated
File diff suppressed because it is too large
Load Diff
44
phpunit.dist.xml
Normal file
44
phpunit.dist.xml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
colors="true"
|
||||||
|
failOnDeprecation="true"
|
||||||
|
failOnNotice="true"
|
||||||
|
failOnWarning="true"
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
cacheDirectory=".phpunit.cache"
|
||||||
|
>
|
||||||
|
<php>
|
||||||
|
<ini name="display_errors" value="1" />
|
||||||
|
<ini name="error_reporting" value="-1" />
|
||||||
|
<server name="APP_ENV" value="test" force="true" />
|
||||||
|
<server name="SHELL_VERBOSITY" value="-1" />
|
||||||
|
</php>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Project Test Suite">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<source ignoreSuppressionOfDeprecations="true"
|
||||||
|
ignoreIndirectDeprecations="true"
|
||||||
|
restrictNotices="true"
|
||||||
|
restrictWarnings="true"
|
||||||
|
>
|
||||||
|
<include>
|
||||||
|
<directory>src</directory>
|
||||||
|
</include>
|
||||||
|
|
||||||
|
<deprecationTrigger>
|
||||||
|
<method>Doctrine\Deprecations\Deprecation::trigger</method>
|
||||||
|
<method>Doctrine\Deprecations\Deprecation::delegateTriggerToBackend</method>
|
||||||
|
<function>trigger_deprecation</function>
|
||||||
|
</deprecationTrigger>
|
||||||
|
</source>
|
||||||
|
|
||||||
|
<extensions>
|
||||||
|
</extensions>
|
||||||
|
</phpunit>
|
||||||
@@ -19,12 +19,12 @@ class CurrentUserExtension implements QueryCollectionExtensionInterface, QueryIt
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
|
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
|
||||||
{
|
{
|
||||||
$this->addWhere($queryBuilder, $resourceClass);
|
$this->addWhere($queryBuilder, $resourceClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
|
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?Operation $operation = null, array $context = []): void
|
||||||
{
|
{
|
||||||
$this->addWhere($queryBuilder, $resourceClass);
|
$this->addWhere($queryBuilder, $resourceClass);
|
||||||
}
|
}
|
||||||
@@ -39,13 +39,14 @@ class CurrentUserExtension implements QueryCollectionExtensionInterface, QueryIt
|
|||||||
$user = $this->security->getUser();
|
$user = $this->security->getUser();
|
||||||
|
|
||||||
// Wenn nicht eingeloggt, keine Ergebnisse
|
// Wenn nicht eingeloggt, keine Ergebnisse
|
||||||
if (!$user instanceof User) {
|
if (! $user instanceof User) {
|
||||||
$queryBuilder->andWhere('1 = 0');
|
$queryBuilder->andWhere('1 = 0');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin sieht alles
|
// Admin sieht alles
|
||||||
if ($user->getRole() === UserRole::ADMIN) {
|
if (UserRole::ADMIN === $user->getRole()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
|
use ApiPlatform\Metadata\Delete;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
use ApiPlatform\Metadata\Post;
|
use ApiPlatform\Metadata\Post;
|
||||||
use ApiPlatform\Metadata\Put;
|
use ApiPlatform\Metadata\Put;
|
||||||
use ApiPlatform\Metadata\Delete;
|
|
||||||
use App\Repository\BundeslandRepository;
|
use App\Repository\BundeslandRepository;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
@@ -20,7 +20,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
new GetCollection(security: 'is_granted("PUBLIC_ACCESS")'),
|
new GetCollection(security: 'is_granted("PUBLIC_ACCESS")'),
|
||||||
new Post(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
new Post(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
||||||
new Put(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
new Put(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
||||||
new Delete(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")')
|
new Delete(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
||||||
]
|
]
|
||||||
)]
|
)]
|
||||||
class Bundesland
|
class Bundesland
|
||||||
@@ -53,6 +53,7 @@ class Bundesland
|
|||||||
public function setName(string $name): self
|
public function setName(string $name): self
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ class Bundesland
|
|||||||
public function setGrunderwerbsteuer(float $grunderwerbsteuer): self
|
public function setGrunderwerbsteuer(float $grunderwerbsteuer): self
|
||||||
{
|
{
|
||||||
$this->grunderwerbsteuer = $grunderwerbsteuer;
|
$this->grunderwerbsteuer = $grunderwerbsteuer;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
|
use ApiPlatform\Metadata\Delete;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
use ApiPlatform\Metadata\Post;
|
use ApiPlatform\Metadata\Post;
|
||||||
use ApiPlatform\Metadata\Put;
|
use ApiPlatform\Metadata\Put;
|
||||||
use ApiPlatform\Metadata\Delete;
|
|
||||||
use App\Repository\HeizungstypRepository;
|
use App\Repository\HeizungstypRepository;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
@@ -20,7 +20,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
new GetCollection(security: 'is_granted("PUBLIC_ACCESS")'),
|
new GetCollection(security: 'is_granted("PUBLIC_ACCESS")'),
|
||||||
new Post(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
new Post(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
||||||
new Put(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
new Put(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
||||||
new Delete(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")')
|
new Delete(security: 'is_granted("ROLE_ADMIN") or is_granted("ROLE_TECHNICAL")'),
|
||||||
]
|
]
|
||||||
)]
|
)]
|
||||||
class Heizungstyp
|
class Heizungstyp
|
||||||
@@ -48,6 +48,7 @@ class Heizungstyp
|
|||||||
public function setName(string $name): self
|
public function setName(string $name): self
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
|
use ApiPlatform\Metadata\Delete;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
use ApiPlatform\Metadata\Post;
|
|
||||||
use ApiPlatform\Metadata\Patch;
|
use ApiPlatform\Metadata\Patch;
|
||||||
use ApiPlatform\Metadata\Delete;
|
use ApiPlatform\Metadata\Post;
|
||||||
use App\Enum\ImmobilienTyp;
|
use App\Enum\ImmobilienTyp;
|
||||||
use App\Repository\ImmobilieRepository;
|
use App\Repository\ImmobilieRepository;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
@@ -22,7 +22,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
new GetCollection(),
|
new GetCollection(),
|
||||||
new Post(),
|
new Post(),
|
||||||
new Patch(security: 'is_granted("edit", object)'),
|
new Patch(security: 'is_granted("edit", object)'),
|
||||||
new Delete(security: 'is_granted("delete", object)')
|
new Delete(security: 'is_granted("delete", object)'),
|
||||||
],
|
],
|
||||||
security: 'is_granted("ROLE_USER")'
|
security: 'is_granted("ROLE_USER")'
|
||||||
)]
|
)]
|
||||||
@@ -125,6 +125,7 @@ class Immobilie
|
|||||||
public function setAdresse(string $adresse): self
|
public function setAdresse(string $adresse): self
|
||||||
{
|
{
|
||||||
$this->adresse = $adresse;
|
$this->adresse = $adresse;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +137,7 @@ class Immobilie
|
|||||||
public function setWohnflaeche(int $wohnflaeche): self
|
public function setWohnflaeche(int $wohnflaeche): self
|
||||||
{
|
{
|
||||||
$this->wohnflaeche = $wohnflaeche;
|
$this->wohnflaeche = $wohnflaeche;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +149,7 @@ class Immobilie
|
|||||||
public function setNutzflaeche(int $nutzflaeche): self
|
public function setNutzflaeche(int $nutzflaeche): self
|
||||||
{
|
{
|
||||||
$this->nutzflaeche = $nutzflaeche;
|
$this->nutzflaeche = $nutzflaeche;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +166,7 @@ class Immobilie
|
|||||||
public function setGarage(bool $garage): self
|
public function setGarage(bool $garage): self
|
||||||
{
|
{
|
||||||
$this->garage = $garage;
|
$this->garage = $garage;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +178,7 @@ class Immobilie
|
|||||||
public function setZimmer(int $zimmer): self
|
public function setZimmer(int $zimmer): self
|
||||||
{
|
{
|
||||||
$this->zimmer = $zimmer;
|
$this->zimmer = $zimmer;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +190,7 @@ class Immobilie
|
|||||||
public function setBaujahr(?int $baujahr): self
|
public function setBaujahr(?int $baujahr): self
|
||||||
{
|
{
|
||||||
$this->baujahr = $baujahr;
|
$this->baujahr = $baujahr;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +202,7 @@ class Immobilie
|
|||||||
public function setTyp(ImmobilienTyp $typ): self
|
public function setTyp(ImmobilienTyp $typ): self
|
||||||
{
|
{
|
||||||
$this->typ = $typ;
|
$this->typ = $typ;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +214,7 @@ class Immobilie
|
|||||||
public function setBeschreibung(?string $beschreibung): self
|
public function setBeschreibung(?string $beschreibung): self
|
||||||
{
|
{
|
||||||
$this->beschreibung = $beschreibung;
|
$this->beschreibung = $beschreibung;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +226,7 @@ class Immobilie
|
|||||||
public function setEtage(?int $etage): self
|
public function setEtage(?int $etage): self
|
||||||
{
|
{
|
||||||
$this->etage = $etage;
|
$this->etage = $etage;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +238,7 @@ class Immobilie
|
|||||||
public function setHeizungstyp(?Heizungstyp $heizungstyp): self
|
public function setHeizungstyp(?Heizungstyp $heizungstyp): self
|
||||||
{
|
{
|
||||||
$this->heizungstyp = $heizungstyp;
|
$this->heizungstyp = $heizungstyp;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +250,7 @@ class Immobilie
|
|||||||
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
||||||
{
|
{
|
||||||
$this->createdAt = $createdAt;
|
$this->createdAt = $createdAt;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +262,7 @@ class Immobilie
|
|||||||
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
|
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
|
||||||
{
|
{
|
||||||
$this->updatedAt = $updatedAt;
|
$this->updatedAt = $updatedAt;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +274,7 @@ class Immobilie
|
|||||||
public function setAbschreibungszeit(?int $abschreibungszeit): self
|
public function setAbschreibungszeit(?int $abschreibungszeit): self
|
||||||
{
|
{
|
||||||
$this->abschreibungszeit = $abschreibungszeit;
|
$this->abschreibungszeit = $abschreibungszeit;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +286,7 @@ class Immobilie
|
|||||||
public function setBundesland(?Bundesland $bundesland): self
|
public function setBundesland(?Bundesland $bundesland): self
|
||||||
{
|
{
|
||||||
$this->bundesland = $bundesland;
|
$this->bundesland = $bundesland;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +298,7 @@ class Immobilie
|
|||||||
public function setKaufpreis(?int $kaufpreis): self
|
public function setKaufpreis(?int $kaufpreis): self
|
||||||
{
|
{
|
||||||
$this->kaufpreis = $kaufpreis;
|
$this->kaufpreis = $kaufpreis;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,11 +310,12 @@ class Immobilie
|
|||||||
public function setVerwalter(User $verwalter): self
|
public function setVerwalter(User $verwalter): self
|
||||||
{
|
{
|
||||||
$this->verwalter = $verwalter;
|
$this->verwalter = $verwalter;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Berechnet die Gesamtfläche (Wohnfläche + Nutzfläche)
|
* Berechnet die Gesamtfläche (Wohnfläche + Nutzfläche).
|
||||||
*/
|
*/
|
||||||
public function getGesamtflaeche(): int
|
public function getGesamtflaeche(): int
|
||||||
{
|
{
|
||||||
@@ -308,16 +324,16 @@ class Immobilie
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Berechnet die Kaufnebenkosten basierend auf dem Bundesland
|
* Berechnet die Kaufnebenkosten basierend auf dem Bundesland
|
||||||
* Rückgabe: Array mit Notar, Grundbuch, Grunderwerbsteuer und Gesamt
|
* Rückgabe: Array mit Notar, Grundbuch, Grunderwerbsteuer und Gesamt.
|
||||||
*/
|
*/
|
||||||
public function getKaufnebenkosten(): array
|
public function getKaufnebenkosten(): array
|
||||||
{
|
{
|
||||||
if (!$this->getKaufpreis() || !$this->bundesland) {
|
if (! $this->getKaufpreis() || ! $this->bundesland) {
|
||||||
return [
|
return [
|
||||||
'notar' => 0,
|
'notar' => 0,
|
||||||
'grundbuch' => 0,
|
'grundbuch' => 0,
|
||||||
'grunderwerbsteuer' => 0,
|
'grunderwerbsteuer' => 0,
|
||||||
'gesamt' => 0
|
'gesamt' => 0,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +353,7 @@ class Immobilie
|
|||||||
'notar' => $notar,
|
'notar' => $notar,
|
||||||
'grundbuch' => $grundbuch,
|
'grundbuch' => $grundbuch,
|
||||||
'grunderwerbsteuer' => $grunderwerbsteuer,
|
'grunderwerbsteuer' => $grunderwerbsteuer,
|
||||||
'gesamt' => $gesamt
|
'gesamt' => $gesamt,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
|
use ApiPlatform\Metadata\Delete;
|
||||||
use ApiPlatform\Metadata\Get;
|
use ApiPlatform\Metadata\Get;
|
||||||
use ApiPlatform\Metadata\GetCollection;
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
use ApiPlatform\Metadata\Post;
|
use ApiPlatform\Metadata\Post;
|
||||||
use ApiPlatform\Metadata\Put;
|
use ApiPlatform\Metadata\Put;
|
||||||
use ApiPlatform\Metadata\Delete;
|
|
||||||
use App\Enum\UserRole;
|
use App\Enum\UserRole;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
@@ -24,7 +24,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
new GetCollection(),
|
new GetCollection(),
|
||||||
new Post(),
|
new Post(),
|
||||||
new Put(),
|
new Put(),
|
||||||
new Delete()
|
new Delete(),
|
||||||
]
|
]
|
||||||
)]
|
)]
|
||||||
class User implements UserInterface
|
class User implements UserInterface
|
||||||
@@ -65,11 +65,11 @@ class User implements UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generiert einen eindeutigen API-Key
|
* Generiert einen eindeutigen API-Key.
|
||||||
*/
|
*/
|
||||||
private function generateApiKey(): string
|
private function generateApiKey(): string
|
||||||
{
|
{
|
||||||
return hash('sha256', random_bytes(32) . microtime(true));
|
return hash('sha256', random_bytes(32).microtime(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
@@ -85,6 +85,7 @@ class User implements UserInterface
|
|||||||
public function setName(string $name): self
|
public function setName(string $name): self
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +97,7 @@ class User implements UserInterface
|
|||||||
public function setEmail(string $email): self
|
public function setEmail(string $email): self
|
||||||
{
|
{
|
||||||
$this->email = $email;
|
$this->email = $email;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +109,7 @@ class User implements UserInterface
|
|||||||
public function setRole(UserRole $role): self
|
public function setRole(UserRole $role): self
|
||||||
{
|
{
|
||||||
$this->role = $role;
|
$this->role = $role;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +121,7 @@ class User implements UserInterface
|
|||||||
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
||||||
{
|
{
|
||||||
$this->createdAt = $createdAt;
|
$this->createdAt = $createdAt;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ class User implements UserInterface
|
|||||||
|
|
||||||
public function addImmobilie(Immobilie $immobilie): self
|
public function addImmobilie(Immobilie $immobilie): self
|
||||||
{
|
{
|
||||||
if (!$this->immobilien->contains($immobilie)) {
|
if (! $this->immobilien->contains($immobilie)) {
|
||||||
$this->immobilien->add($immobilie);
|
$this->immobilien->add($immobilie);
|
||||||
$immobilie->setVerwalter($this);
|
$immobilie->setVerwalter($this);
|
||||||
}
|
}
|
||||||
@@ -142,6 +146,7 @@ class User implements UserInterface
|
|||||||
public function removeImmobilie(Immobilie $immobilie): self
|
public function removeImmobilie(Immobilie $immobilie): self
|
||||||
{
|
{
|
||||||
$this->immobilien->removeElement($immobilie);
|
$this->immobilien->removeElement($immobilie);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,16 +156,17 @@ class User implements UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generiert einen neuen API-Key
|
* Generiert einen neuen API-Key.
|
||||||
*/
|
*/
|
||||||
public function regenerateApiKey(): self
|
public function regenerateApiKey(): self
|
||||||
{
|
{
|
||||||
$this->apiKey = $this->generateApiKey();
|
$this->apiKey = $this->generateApiKey();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserInterface Methods
|
* UserInterface Methods.
|
||||||
*/
|
*/
|
||||||
public function getUserIdentifier(): string
|
public function getUserIdentifier(): string
|
||||||
{
|
{
|
||||||
@@ -171,11 +177,11 @@ class User implements UserInterface
|
|||||||
{
|
{
|
||||||
$roles = ['ROLE_USER'];
|
$roles = ['ROLE_USER'];
|
||||||
|
|
||||||
if ($this->role === UserRole::ADMIN) {
|
if (UserRole::ADMIN === $this->role) {
|
||||||
$roles[] = 'ROLE_ADMIN';
|
$roles[] = 'ROLE_ADMIN';
|
||||||
} elseif ($this->role === UserRole::MODERATOR) {
|
} elseif (UserRole::MODERATOR === $this->role) {
|
||||||
$roles[] = 'ROLE_MODERATOR';
|
$roles[] = 'ROLE_MODERATOR';
|
||||||
} elseif ($this->role === UserRole::TECHNICAL) {
|
} elseif (UserRole::TECHNICAL === $this->role) {
|
||||||
$roles[] = 'ROLE_TECHNICAL';
|
$roles[] = 'ROLE_TECHNICAL';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ enum Bundesland: string
|
|||||||
|
|
||||||
public function getLabel(): string
|
public function getLabel(): string
|
||||||
{
|
{
|
||||||
return match($this) {
|
return match ($this) {
|
||||||
self::BADEN_WUERTTEMBERG => 'Baden-Württemberg',
|
self::BADEN_WUERTTEMBERG => 'Baden-Württemberg',
|
||||||
self::BAYERN => 'Bayern',
|
self::BAYERN => 'Bayern',
|
||||||
self::BERLIN => 'Berlin',
|
self::BERLIN => 'Berlin',
|
||||||
@@ -45,11 +45,11 @@ enum Bundesland: string
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die Grunderwerbsteuer in Prozent für das Bundesland zurück
|
* Gibt die Grunderwerbsteuer in Prozent für das Bundesland zurück
|
||||||
* Stand: 2025
|
* Stand: 2025.
|
||||||
*/
|
*/
|
||||||
public function getGrunderwerbsteuer(): float
|
public function getGrunderwerbsteuer(): float
|
||||||
{
|
{
|
||||||
return match($this) {
|
return match ($this) {
|
||||||
self::BADEN_WUERTTEMBERG => 5.0,
|
self::BADEN_WUERTTEMBERG => 5.0,
|
||||||
self::BAYERN => 3.5,
|
self::BAYERN => 3.5,
|
||||||
self::BERLIN => 6.0,
|
self::BERLIN => 6.0,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ enum Heizungstyp: string
|
|||||||
|
|
||||||
public function getLabel(): string
|
public function getLabel(): string
|
||||||
{
|
{
|
||||||
return match($this) {
|
return match ($this) {
|
||||||
self::GASHEIZUNG => 'Gasheizung',
|
self::GASHEIZUNG => 'Gasheizung',
|
||||||
self::WAERMEPUMPE => 'Wärmepumpe',
|
self::WAERMEPUMPE => 'Wärmepumpe',
|
||||||
self::OELHEIZUNG => 'Ölheizung',
|
self::OELHEIZUNG => 'Ölheizung',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ enum ImmobilienTyp: string
|
|||||||
|
|
||||||
public function getLabel(): string
|
public function getLabel(): string
|
||||||
{
|
{
|
||||||
return match($this) {
|
return match ($this) {
|
||||||
self::WOHNUNG => 'Wohnung',
|
self::WOHNUNG => 'Wohnung',
|
||||||
self::HAUS => 'Haus',
|
self::HAUS => 'Haus',
|
||||||
self::GRUNDSTUECK => 'Grundstück',
|
self::GRUNDSTUECK => 'Grundstück',
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ enum UserRole: string
|
|||||||
|
|
||||||
public function getLabel(): string
|
public function getLabel(): string
|
||||||
{
|
{
|
||||||
return match($this) {
|
return match ($this) {
|
||||||
self::USER => 'Benutzer',
|
self::USER => 'Benutzer',
|
||||||
self::ADMIN => 'Administrator',
|
self::ADMIN => 'Administrator',
|
||||||
self::MODERATOR => 'Moderator',
|
self::MODERATOR => 'Moderator',
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find available properties
|
* Find available properties.
|
||||||
*/
|
*/
|
||||||
public function findVerfuegbare(): array
|
public function findVerfuegbare(): array
|
||||||
{
|
{
|
||||||
@@ -31,7 +31,7 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find properties by type
|
* Find properties by type.
|
||||||
*/
|
*/
|
||||||
public function findByTyp(ImmobilienTyp $typ): array
|
public function findByTyp(ImmobilienTyp $typ): array
|
||||||
{
|
{
|
||||||
@@ -44,7 +44,7 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find properties within price range
|
* Find properties within price range.
|
||||||
*/
|
*/
|
||||||
public function findByPreisRange(float $minPreis, float $maxPreis): array
|
public function findByPreisRange(float $minPreis, float $maxPreis): array
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find properties within area range
|
* Find properties within area range.
|
||||||
*/
|
*/
|
||||||
public function findByFlaecheRange(float $minFlaeche, float $maxFlaeche): array
|
public function findByFlaecheRange(float $minFlaeche, float $maxFlaeche): array
|
||||||
{
|
{
|
||||||
@@ -76,7 +76,7 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find properties with garage
|
* Find properties with garage.
|
||||||
*/
|
*/
|
||||||
public function findMitGarage(): array
|
public function findMitGarage(): array
|
||||||
{
|
{
|
||||||
@@ -91,7 +91,7 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find properties by minimum number of rooms
|
* Find properties by minimum number of rooms.
|
||||||
*/
|
*/
|
||||||
public function findByMinZimmer(int $minZimmer): array
|
public function findByMinZimmer(int $minZimmer): array
|
||||||
{
|
{
|
||||||
@@ -106,7 +106,7 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get average price per sqm by type
|
* Get average price per sqm by type.
|
||||||
*/
|
*/
|
||||||
public function getAveragePreisProQmByTyp(ImmobilienTyp $typ): float
|
public function getAveragePreisProQmByTyp(ImmobilienTyp $typ): float
|
||||||
{
|
{
|
||||||
@@ -123,13 +123,13 @@ class ImmobilieRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search properties by address
|
* Search properties by address.
|
||||||
*/
|
*/
|
||||||
public function searchByAdresse(string $search): array
|
public function searchByAdresse(string $search): array
|
||||||
{
|
{
|
||||||
return $this->createQueryBuilder('i')
|
return $this->createQueryBuilder('i')
|
||||||
->andWhere('i.adresse LIKE :search')
|
->andWhere('i.adresse LIKE :search')
|
||||||
->setParameter('search', '%' . $search . '%')
|
->setParameter('search', '%'.$search.'%')
|
||||||
->orderBy('i.createdAt', 'DESC')
|
->orderBy('i.createdAt', 'DESC')
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->getResult();
|
->getResult();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class UserRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find users by role
|
* Find users by role.
|
||||||
*/
|
*/
|
||||||
public function findByRole(string $role): array
|
public function findByRole(string $role): array
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ class UserRepository extends ServiceEntityRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find user by email
|
* Find user by email.
|
||||||
*/
|
*/
|
||||||
public function findOneByEmail(string $email): ?User
|
public function findOneByEmail(string $email): ?User
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ class ApiKeyAuthenticator extends AbstractAuthenticator
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new SelfValidatingPassport(
|
return new SelfValidatingPassport(
|
||||||
new UserBadge($apiKey, function($apiKey) {
|
new UserBadge($apiKey, function ($apiKey) {
|
||||||
$user = $this->userRepository->findOneBy(['apiKey' => $apiKey]);
|
$user = $this->userRepository->findOneBy(['apiKey' => $apiKey]);
|
||||||
|
|
||||||
if (!$user) {
|
if (! $user) {
|
||||||
throw new CustomUserMessageAuthenticationException('Invalid API key');
|
throw new CustomUserMessageAuthenticationException('Invalid API key');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ class ApiKeyAuthenticator extends AbstractAuthenticator
|
|||||||
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
|
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
|
||||||
{
|
{
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
|
'message' => strtr($exception->getMessageKey(), $exception->getMessageData()),
|
||||||
], Response::HTTP_UNAUTHORIZED);
|
], Response::HTTP_UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
|||||||
|
|
||||||
class ImmobilieVoter extends Voter
|
class ImmobilieVoter extends Voter
|
||||||
{
|
{
|
||||||
const VIEW = 'view';
|
public const VIEW = 'view';
|
||||||
const EDIT = 'edit';
|
public const EDIT = 'edit';
|
||||||
const DELETE = 'delete';
|
public const DELETE = 'delete';
|
||||||
|
|
||||||
protected function supports(string $attribute, mixed $subject): bool
|
protected function supports(string $attribute, mixed $subject): bool
|
||||||
{
|
{
|
||||||
@@ -26,7 +26,7 @@ class ImmobilieVoter extends Voter
|
|||||||
$user = $token->getUser();
|
$user = $token->getUser();
|
||||||
|
|
||||||
// User muss eingeloggt sein
|
// User muss eingeloggt sein
|
||||||
if (!$user instanceof User) {
|
if (! $user instanceof User) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,12 +34,12 @@ class ImmobilieVoter extends Voter
|
|||||||
$immobilie = $subject;
|
$immobilie = $subject;
|
||||||
|
|
||||||
// Admin hat uneingeschränkten Zugriff
|
// Admin hat uneingeschränkten Zugriff
|
||||||
if ($user->getRole() === UserRole::ADMIN) {
|
if (UserRole::ADMIN === $user->getRole()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfe je nach Attribut
|
// Prüfe je nach Attribut
|
||||||
return match($attribute) {
|
return match ($attribute) {
|
||||||
self::VIEW => $this->canView($immobilie, $user),
|
self::VIEW => $this->canView($immobilie, $user),
|
||||||
self::EDIT => $this->canEdit($immobilie, $user),
|
self::EDIT => $this->canEdit($immobilie, $user),
|
||||||
self::DELETE => $this->canDelete($immobilie, $user),
|
self::DELETE => $this->canDelete($immobilie, $user),
|
||||||
|
|||||||
27
symfony.lock
27
symfony.lock
@@ -49,6 +49,18 @@
|
|||||||
"./migrations/.gitignore"
|
"./migrations/.gitignore"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"friendsofphp/php-cs-fixer": {
|
||||||
|
"version": "3.89",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "3.0",
|
||||||
|
"ref": "be2103eb4a20942e28a6dd87736669b757132435"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
".php-cs-fixer.dist.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
"nelmio/cors-bundle": {
|
"nelmio/cors-bundle": {
|
||||||
"version": "2.6",
|
"version": "2.6",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
@@ -61,6 +73,21 @@
|
|||||||
"./config/packages/nelmio_cors.yaml"
|
"./config/packages/nelmio_cors.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"phpunit/phpunit": {
|
||||||
|
"version": "12.4",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "11.1",
|
||||||
|
"ref": "1117deb12541f35793eec9fff7494d7aa12283fc"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
".env.test",
|
||||||
|
"phpunit.dist.xml",
|
||||||
|
"tests/bootstrap.php",
|
||||||
|
"bin/phpunit"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/console": {
|
"symfony/console": {
|
||||||
"version": "7.3",
|
"version": "7.3",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
|||||||
28
tests/Api/ApiDocumentationTest.php
Normal file
28
tests/Api/ApiDocumentationTest.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Api;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
|
class ApiDocumentationTest extends WebTestCase
|
||||||
|
{
|
||||||
|
public function testSwaggerUIAccessible(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: Swagger UI ist öffentlich zugänglich
|
||||||
|
$client->request('GET', '/api/docs.html');
|
||||||
|
|
||||||
|
$this->assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOpenAPIJsonLdAccessible(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: OpenAPI JSON-LD ist öffentlich zugänglich
|
||||||
|
$client->request('GET', '/api/docs.jsonld');
|
||||||
|
|
||||||
|
$this->assertResponseIsSuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
50
tests/Api/BundeslandApiTest.php
Normal file
50
tests/Api/BundeslandApiTest.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Api;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
|
class BundeslandApiTest extends WebTestCase
|
||||||
|
{
|
||||||
|
public function testGetBundeslaenderPublicAccess(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: Bundesländer können ohne API-Key abgerufen werden
|
||||||
|
$client->request('GET', '/api/bundeslands');
|
||||||
|
|
||||||
|
$this->assertResponseIsSuccessful();
|
||||||
|
$this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSingleBundeslandPublicAccess(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: Einzelnes Bundesland kann ohne API-Key abgerufen werden
|
||||||
|
$client->request('GET', '/api/bundeslands/1');
|
||||||
|
|
||||||
|
// Response kann 200 (OK) oder 404 (Not Found) sein, beides ist akzeptabel
|
||||||
|
$this->assertResponseStatusCodeSame(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateBundeslandRequiresAuthentication(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: Bundesland erstellen ohne API-Key sollte fehlschlagen
|
||||||
|
$client->request(
|
||||||
|
'POST',
|
||||||
|
'/api/bundeslands',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
['CONTENT_TYPE' => 'application/json'],
|
||||||
|
json_encode([
|
||||||
|
'name' => 'Test Bundesland',
|
||||||
|
'grunderwerbsteuer' => 5.0,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertResponseStatusCodeSame(403); // Access Denied (no authentication on this firewall)
|
||||||
|
}
|
||||||
|
}
|
||||||
49
tests/Api/HeizungstypApiTest.php
Normal file
49
tests/Api/HeizungstypApiTest.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Api;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
|
class HeizungstypApiTest extends WebTestCase
|
||||||
|
{
|
||||||
|
public function testGetHeizungstypenPublicAccess(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: Heizungstypen können ohne API-Key abgerufen werden
|
||||||
|
$client->request('GET', '/api/heizungstyps');
|
||||||
|
|
||||||
|
$this->assertResponseIsSuccessful();
|
||||||
|
$this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetSingleHeizungstypPublicAccess(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: Einzelner Heizungstyp kann ohne API-Key abgerufen werden
|
||||||
|
$client->request('GET', '/api/heizungstyps/1');
|
||||||
|
|
||||||
|
// Response kann 200 (OK) oder 404 (Not Found) sein
|
||||||
|
$this->assertResponseStatusCodeSame(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateHeizungstypRequiresAuthentication(): void
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
|
||||||
|
// Test: Heizungstyp erstellen ohne API-Key sollte fehlschlagen
|
||||||
|
$client->request(
|
||||||
|
'POST',
|
||||||
|
'/api/heizungstyps',
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
['CONTENT_TYPE' => 'application/json'],
|
||||||
|
json_encode([
|
||||||
|
'name' => 'Test Heizung',
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertResponseStatusCodeSame(401); // Unauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
65
tests/Entity/BundeslandTest.php
Normal file
65
tests/Entity/BundeslandTest.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Bundesland;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class BundeslandTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testBundeslandCreation(): void
|
||||||
|
{
|
||||||
|
$bundesland = new Bundesland();
|
||||||
|
$bundesland->setName('Bayern');
|
||||||
|
$bundesland->setGrunderwerbsteuer(3.5);
|
||||||
|
|
||||||
|
$this->assertEquals('Bayern', $bundesland->getName());
|
||||||
|
$this->assertEquals(3.5, $bundesland->getGrunderwerbsteuer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGrunderwerbsteuerValues(): void
|
||||||
|
{
|
||||||
|
$testCases = [
|
||||||
|
['Baden-Württemberg', 5.0],
|
||||||
|
['Bayern', 3.5],
|
||||||
|
['Berlin', 6.0],
|
||||||
|
['Brandenburg', 6.5],
|
||||||
|
['Bremen', 5.0],
|
||||||
|
['Hamburg', 5.5],
|
||||||
|
['Hessen', 6.0],
|
||||||
|
['Mecklenburg-Vorpommern', 6.0],
|
||||||
|
['Niedersachsen', 5.0],
|
||||||
|
['Nordrhein-Westfalen', 6.5],
|
||||||
|
['Rheinland-Pfalz', 5.0],
|
||||||
|
['Saarland', 6.5],
|
||||||
|
['Sachsen', 5.5],
|
||||||
|
['Sachsen-Anhalt', 5.0],
|
||||||
|
['Schleswig-Holstein', 6.5],
|
||||||
|
['Thüringen', 5.0],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($testCases as [$name, $steuer]) {
|
||||||
|
$bundesland = new Bundesland();
|
||||||
|
$bundesland->setName($name);
|
||||||
|
$bundesland->setGrunderwerbsteuer($steuer);
|
||||||
|
|
||||||
|
$this->assertEquals($name, $bundesland->getName());
|
||||||
|
$this->assertEquals($steuer, $bundesland->getGrunderwerbsteuer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMinMaxGrunderwerbsteuer(): void
|
||||||
|
{
|
||||||
|
// Niedrigster Satz: Bayern mit 3.5%
|
||||||
|
$bayern = new Bundesland();
|
||||||
|
$bayern->setName('Bayern');
|
||||||
|
$bayern->setGrunderwerbsteuer(3.5);
|
||||||
|
$this->assertEquals(3.5, $bayern->getGrunderwerbsteuer());
|
||||||
|
|
||||||
|
// Höchster Satz: Brandenburg, NRW, Saarland, SH mit 6.5%
|
||||||
|
$nrw = new Bundesland();
|
||||||
|
$nrw->setName('Nordrhein-Westfalen');
|
||||||
|
$nrw->setGrunderwerbsteuer(6.5);
|
||||||
|
$this->assertEquals(6.5, $nrw->getGrunderwerbsteuer());
|
||||||
|
}
|
||||||
|
}
|
||||||
29
tests/Entity/HeizungstypTest.php
Normal file
29
tests/Entity/HeizungstypTest.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Heizungstyp;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class HeizungstypTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testHeizungstypCreation(): void
|
||||||
|
{
|
||||||
|
$heizungstyp = new Heizungstyp();
|
||||||
|
$heizungstyp->setName('Wärmepumpe');
|
||||||
|
|
||||||
|
$this->assertEquals('Wärmepumpe', $heizungstyp->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCommonHeizungstypen(): void
|
||||||
|
{
|
||||||
|
$typen = ['Gasheizung', 'Wärmepumpe', 'Ölheizung', 'Fernwärme', 'Pelletheizung'];
|
||||||
|
|
||||||
|
foreach ($typen as $typName) {
|
||||||
|
$heizungstyp = new Heizungstyp();
|
||||||
|
$heizungstyp->setName($typName);
|
||||||
|
|
||||||
|
$this->assertEquals($typName, $heizungstyp->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
160
tests/Entity/ImmobilieTest.php
Normal file
160
tests/Entity/ImmobilieTest.php
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Entity;
|
||||||
|
|
||||||
|
use App\Entity\Bundesland;
|
||||||
|
use App\Entity\Immobilie;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Enum\ImmobilienTyp;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ImmobilieTest extends TestCase
|
||||||
|
{
|
||||||
|
private User $verwalter;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->verwalter = new User();
|
||||||
|
$this->verwalter->setName('Test Verwalter');
|
||||||
|
$this->verwalter->setEmail('verwalter@example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testImmobilieCreation(): void
|
||||||
|
{
|
||||||
|
$immobilie = new Immobilie();
|
||||||
|
$immobilie->setVerwalter($this->verwalter);
|
||||||
|
$immobilie->setAdresse('Teststraße 123, 12345 Teststadt');
|
||||||
|
$immobilie->setWohnflaeche(100);
|
||||||
|
$immobilie->setNutzflaeche(20);
|
||||||
|
$immobilie->setZimmer(4);
|
||||||
|
$immobilie->setTyp(ImmobilienTyp::WOHNUNG);
|
||||||
|
|
||||||
|
$this->assertEquals('Teststraße 123, 12345 Teststadt', $immobilie->getAdresse());
|
||||||
|
$this->assertEquals(100, $immobilie->getWohnflaeche());
|
||||||
|
$this->assertEquals(20, $immobilie->getNutzflaeche());
|
||||||
|
$this->assertEquals(4, $immobilie->getZimmer());
|
||||||
|
$this->assertEquals(ImmobilienTyp::WOHNUNG, $immobilie->getTyp());
|
||||||
|
$this->assertEquals($this->verwalter, $immobilie->getVerwalter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGesamtflaecheCalculation(): void
|
||||||
|
{
|
||||||
|
$immobilie = new Immobilie();
|
||||||
|
$immobilie->setVerwalter($this->verwalter);
|
||||||
|
$immobilie->setAdresse('Test');
|
||||||
|
$immobilie->setWohnflaeche(85);
|
||||||
|
$immobilie->setNutzflaeche(15);
|
||||||
|
$immobilie->setZimmer(3);
|
||||||
|
|
||||||
|
$this->assertEquals(100, $immobilie->getGesamtflaeche());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testKaufnebenkostenWithoutBundesland(): void
|
||||||
|
{
|
||||||
|
$immobilie = new Immobilie();
|
||||||
|
$immobilie->setVerwalter($this->verwalter);
|
||||||
|
$immobilie->setAdresse('Test');
|
||||||
|
$immobilie->setWohnflaeche(100);
|
||||||
|
$immobilie->setNutzflaeche(0);
|
||||||
|
$immobilie->setZimmer(3);
|
||||||
|
$immobilie->setKaufpreis(300000);
|
||||||
|
|
||||||
|
$kosten = $immobilie->getKaufnebenkosten();
|
||||||
|
|
||||||
|
$this->assertEquals(0, $kosten['notar']);
|
||||||
|
$this->assertEquals(0, $kosten['grundbuch']);
|
||||||
|
$this->assertEquals(0, $kosten['grunderwerbsteuer']);
|
||||||
|
$this->assertEquals(0, $kosten['gesamt']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testKaufnebenkostenWithBundesland(): void
|
||||||
|
{
|
||||||
|
$bundesland = new Bundesland();
|
||||||
|
$bundesland->setName('Bayern');
|
||||||
|
$bundesland->setGrunderwerbsteuer(3.5);
|
||||||
|
|
||||||
|
$immobilie = new Immobilie();
|
||||||
|
$immobilie->setVerwalter($this->verwalter);
|
||||||
|
$immobilie->setAdresse('Test');
|
||||||
|
$immobilie->setWohnflaeche(100);
|
||||||
|
$immobilie->setNutzflaeche(0);
|
||||||
|
$immobilie->setZimmer(3);
|
||||||
|
$immobilie->setKaufpreis(300000);
|
||||||
|
$immobilie->setBundesland($bundesland);
|
||||||
|
|
||||||
|
$kosten = $immobilie->getKaufnebenkosten();
|
||||||
|
|
||||||
|
// Notar: 1.5% von 300000 = 4500
|
||||||
|
$this->assertEquals(4500, $kosten['notar']);
|
||||||
|
|
||||||
|
// Grundbuch: 0.5% von 300000 = 1500
|
||||||
|
$this->assertEquals(1500, $kosten['grundbuch']);
|
||||||
|
|
||||||
|
// Grunderwerbsteuer: 3.5% von 300000 = 10500
|
||||||
|
$this->assertEqualsWithDelta(10500, $kosten['grunderwerbsteuer'], 0.01);
|
||||||
|
|
||||||
|
// Gesamt: 4500 + 1500 + 10500 = 16500
|
||||||
|
$this->assertEquals(16500, $kosten['gesamt']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testKaufnebenkostenWithDifferentBundesland(): void
|
||||||
|
{
|
||||||
|
$bundesland = new Bundesland();
|
||||||
|
$bundesland->setName('Nordrhein-Westfalen');
|
||||||
|
$bundesland->setGrunderwerbsteuer(6.5);
|
||||||
|
|
||||||
|
$immobilie = new Immobilie();
|
||||||
|
$immobilie->setVerwalter($this->verwalter);
|
||||||
|
$immobilie->setAdresse('Test');
|
||||||
|
$immobilie->setWohnflaeche(100);
|
||||||
|
$immobilie->setNutzflaeche(0);
|
||||||
|
$immobilie->setZimmer(3);
|
||||||
|
$immobilie->setKaufpreis(400000);
|
||||||
|
$immobilie->setBundesland($bundesland);
|
||||||
|
|
||||||
|
$kosten = $immobilie->getKaufnebenkosten();
|
||||||
|
|
||||||
|
// Notar: 1.5% von 400000 = 6000
|
||||||
|
$this->assertEquals(6000, $kosten['notar']);
|
||||||
|
|
||||||
|
// Grundbuch: 0.5% von 400000 = 2000
|
||||||
|
$this->assertEquals(2000, $kosten['grundbuch']);
|
||||||
|
|
||||||
|
// Grunderwerbsteuer: 6.5% von 400000 = 26000
|
||||||
|
$this->assertEquals(26000, $kosten['grunderwerbsteuer']);
|
||||||
|
|
||||||
|
// Gesamt: 6000 + 2000 + 26000 = 34000
|
||||||
|
$this->assertEquals(34000, $kosten['gesamt']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefaultValues(): void
|
||||||
|
{
|
||||||
|
$immobilie = new Immobilie();
|
||||||
|
|
||||||
|
$this->assertEquals(ImmobilienTyp::WOHNUNG, $immobilie->getTyp());
|
||||||
|
$this->assertEquals(0, $immobilie->getNutzflaeche());
|
||||||
|
$this->assertFalse($immobilie->getGarage());
|
||||||
|
$this->assertNotNull($immobilie->getCreatedAt());
|
||||||
|
$this->assertNotNull($immobilie->getUpdatedAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOptionalFields(): void
|
||||||
|
{
|
||||||
|
$immobilie = new Immobilie();
|
||||||
|
$immobilie->setVerwalter($this->verwalter);
|
||||||
|
$immobilie->setAdresse('Test');
|
||||||
|
$immobilie->setWohnflaeche(100);
|
||||||
|
$immobilie->setNutzflaeche(0);
|
||||||
|
$immobilie->setZimmer(3);
|
||||||
|
|
||||||
|
$immobilie->setBaujahr(2020);
|
||||||
|
$immobilie->setEtage(3);
|
||||||
|
$immobilie->setAbschreibungszeit(50);
|
||||||
|
$immobilie->setBeschreibung('Schöne Wohnung');
|
||||||
|
|
||||||
|
$this->assertEquals(2020, $immobilie->getBaujahr());
|
||||||
|
$this->assertEquals(3, $immobilie->getEtage());
|
||||||
|
$this->assertEquals(50, $immobilie->getAbschreibungszeit());
|
||||||
|
$this->assertEquals('Schöne Wohnung', $immobilie->getBeschreibung());
|
||||||
|
}
|
||||||
|
}
|
||||||
92
tests/Entity/UserTest.php
Normal file
92
tests/Entity/UserTest.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Tests\Entity;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Enum\UserRole;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class UserTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testUserCreation(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setName('Max Mustermann');
|
||||||
|
$user->setEmail('max@example.com');
|
||||||
|
$user->setRole(UserRole::USER);
|
||||||
|
|
||||||
|
$this->assertEquals('Max Mustermann', $user->getName());
|
||||||
|
$this->assertEquals('max@example.com', $user->getEmail());
|
||||||
|
$this->assertEquals(UserRole::USER, $user->getRole());
|
||||||
|
$this->assertNotNull($user->getApiKey());
|
||||||
|
$this->assertNotNull($user->getCreatedAt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testApiKeyGeneration(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setName('Test User');
|
||||||
|
$user->setEmail('test@example.com');
|
||||||
|
|
||||||
|
$apiKey = $user->getApiKey();
|
||||||
|
|
||||||
|
$this->assertNotEmpty($apiKey);
|
||||||
|
$this->assertEquals(64, strlen($apiKey)); // SHA256 hash length
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegenerateApiKey(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setName('Test User');
|
||||||
|
$user->setEmail('test@example.com');
|
||||||
|
|
||||||
|
$originalKey = $user->getApiKey();
|
||||||
|
$user->regenerateApiKey();
|
||||||
|
$newKey = $user->getApiKey();
|
||||||
|
|
||||||
|
$this->assertNotEquals($originalKey, $newKey);
|
||||||
|
$this->assertEquals(64, strlen($newKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserRoles(): void
|
||||||
|
{
|
||||||
|
// Test USER role
|
||||||
|
$user = new User();
|
||||||
|
$user->setRole(UserRole::USER);
|
||||||
|
$this->assertContains('ROLE_USER', $user->getRoles());
|
||||||
|
|
||||||
|
// Test ADMIN role
|
||||||
|
$admin = new User();
|
||||||
|
$admin->setRole(UserRole::ADMIN);
|
||||||
|
$this->assertContains('ROLE_ADMIN', $admin->getRoles());
|
||||||
|
$this->assertContains('ROLE_USER', $admin->getRoles());
|
||||||
|
|
||||||
|
// Test TECHNICAL role
|
||||||
|
$technical = new User();
|
||||||
|
$technical->setRole(UserRole::TECHNICAL);
|
||||||
|
$this->assertContains('ROLE_TECHNICAL', $technical->getRoles());
|
||||||
|
$this->assertContains('ROLE_USER', $technical->getRoles());
|
||||||
|
|
||||||
|
// Test MODERATOR role
|
||||||
|
$moderator = new User();
|
||||||
|
$moderator->setRole(UserRole::MODERATOR);
|
||||||
|
$this->assertContains('ROLE_MODERATOR', $moderator->getRoles());
|
||||||
|
$this->assertContains('ROLE_USER', $moderator->getRoles());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserIdentifier(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$user->setEmail('identifier@example.com');
|
||||||
|
|
||||||
|
$this->assertEquals('identifier@example.com', $user->getUserIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefaultRole(): void
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
|
||||||
|
// Default role should be USER
|
||||||
|
$this->assertEquals(UserRole::USER, $user->getRole());
|
||||||
|
}
|
||||||
|
}
|
||||||
13
tests/bootstrap.php
Normal file
13
tests/bootstrap.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Component\Dotenv\Dotenv;
|
||||||
|
|
||||||
|
require dirname(__DIR__).'/vendor/autoload.php';
|
||||||
|
|
||||||
|
if (method_exists(Dotenv::class, 'bootEnv')) {
|
||||||
|
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['APP_DEBUG']) {
|
||||||
|
umask(0000);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user