Skip to content

Commit 0a6425a

Browse files
committed
feat: Add security rules configuration, database schema, and secrets management
- Created `rules.tsv` for ZAP security rules configuration. - Added `schema.sql` for the Vision Week Virtual Exploration database schema, including tables for users, categories, animals, videos, comments, and more. - Introduced `secrets.yaml` for managing sensitive information such as API keys and database credentials. - Implemented `security.yml` for automated security scans, including dependency checks, code analysis, secret detection, and penetration testing. - Configured `sonar-project.properties` for SonarCloud integration to monitor code quality and security. - Established `storage.yaml` for persistent volume claims and storage classes in Kubernetes. - Set up `supervisord.conf` for managing application processes with Supervisor. - Added `xdebug.ini` for Xdebug configuration to enhance PHP development and debugging experience.
1 parent d7ccb98 commit 0a6425a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+10278
-0
lines changed

AnimalApiTest.php

Lines changed: 458 additions & 0 deletions
Large diffs are not rendered by default.

AnimalModelTest.php

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Unit;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use VisionWeek\Models\Animal;
9+
use VisionWeek\Services\DatabaseService;
10+
11+
class AnimalModelTest extends TestCase
12+
{
13+
private Animal $animal;
14+
private $mockDb;
15+
16+
protected function setUp(): void
17+
{
18+
// Mock the database service
19+
$this->mockDb = $this->createMock(DatabaseService::class);
20+
$this->animal = new Animal($this->mockDb);
21+
}
22+
23+
public function testCreateAnimalWithValidData(): void
24+
{
25+
$animalData = [
26+
'name' => 'African Lion',
27+
'scientific_name' => 'Panthera leo',
28+
'category_id' => 1,
29+
'description' => 'The African lion is a large cat native to Africa.',
30+
'habitat' => 'Savanna and grasslands',
31+
'diet' => 'Carnivore',
32+
'conservation_status' => 'Vulnerable',
33+
'average_lifespan' => 15,
34+
'weight_range' => '120-250 kg',
35+
'height_range' => '0.9-1.2 m',
36+
'fun_facts' => ['Known as the "King of the Jungle"', 'Lives in prides'],
37+
'image_url' => 'https://example.com/lion.jpg'
38+
];
39+
40+
// Mock successful database insertion
41+
$this->mockDb->expects($this->once())
42+
->method('insert')
43+
->with('animals', $this->anything())
44+
->willReturn(['id' => 1, 'uuid' => 'test-uuid-123']);
45+
46+
$result = $this->animal->create($animalData);
47+
48+
$this->assertIsArray($result);
49+
$this->assertArrayHasKey('id', $result);
50+
$this->assertArrayHasKey('uuid', $result);
51+
$this->assertEquals(1, $result['id']);
52+
}
53+
54+
public function testCreateAnimalWithDuplicateScientificName(): void
55+
{
56+
$animalData = [
57+
'name' => 'Lion',
58+
'scientific_name' => 'Panthera leo',
59+
'category_id' => 1
60+
];
61+
62+
// Mock database constraint violation
63+
$this->mockDb->expects($this->once())
64+
->method('insert')
65+
->willThrowException(new \PDOException('UNIQUE constraint failed'));
66+
67+
$this->expectException(\InvalidArgumentException::class);
68+
$this->expectExceptionMessage('Animal with this scientific name already exists');
69+
70+
$this->animal->create($animalData);
71+
}
72+
73+
public function testFindByIdReturnsAnimal(): void
74+
{
75+
$expectedAnimal = [
76+
'id' => 1,
77+
'uuid' => 'test-uuid-123',
78+
'name' => 'Lion',
79+
'scientific_name' => 'Panthera leo',
80+
'category_id' => 1,
81+
'created_at' => '2024-01-01 12:00:00'
82+
];
83+
84+
$this->mockDb->expects($this->once())
85+
->method('findById')
86+
->with('animals', 1)
87+
->willReturn($expectedAnimal);
88+
89+
$result = $this->animal->findById(1);
90+
91+
$this->assertEquals($expectedAnimal, $result);
92+
}
93+
94+
public function testFindByIdReturnsNullForNonExistent(): void
95+
{
96+
$this->mockDb->expects($this->once())
97+
->method('findById')
98+
->with('animals', 999)
99+
->willReturn(null);
100+
101+
$result = $this->animal->findById(999);
102+
103+
$this->assertNull($result);
104+
}
105+
106+
public function testFindByScientificName(): void
107+
{
108+
$scientificName = 'Panthera leo';
109+
$expectedAnimal = [
110+
'id' => 1,
111+
'name' => 'Lion',
112+
'scientific_name' => $scientificName
113+
];
114+
115+
$this->mockDb->expects($this->once())
116+
->method('findBy')
117+
->with('animals', ['scientific_name' => $scientificName])
118+
->willReturn($expectedAnimal);
119+
120+
$result = $this->animal->findByScientificName($scientificName);
121+
122+
$this->assertEquals($expectedAnimal, $result);
123+
}
124+
125+
public function testSearchAnimalsWithQuery(): void
126+
{
127+
$searchQuery = 'lion';
128+
$expectedResults = [
129+
[
130+
'id' => 1,
131+
'name' => 'African Lion',
132+
'scientific_name' => 'Panthera leo',
133+
'relevance_score' => 0.95
134+
],
135+
[
136+
'id' => 2,
137+
'name' => 'Mountain Lion',
138+
'scientific_name' => 'Puma concolor',
139+
'relevance_score' => 0.75
140+
]
141+
];
142+
143+
$this->mockDb->expects($this->once())
144+
->method('search')
145+
->with('animals', $searchQuery, $this->anything())
146+
->willReturn($expectedResults);
147+
148+
$result = $this->animal->search($searchQuery);
149+
150+
$this->assertIsArray($result);
151+
$this->assertCount(2, $result);
152+
$this->assertEquals($expectedResults, $result);
153+
}
154+
155+
public function testGetAllWithPagination(): void
156+
{
157+
$page = 1;
158+
$limit = 10;
159+
$expectedAnimals = [
160+
['id' => 1, 'name' => 'Lion'],
161+
['id' => 2, 'name' => 'Tiger'],
162+
['id' => 3, 'name' => 'Elephant']
163+
];
164+
165+
$this->mockDb->expects($this->once())
166+
->method('paginate')
167+
->with('animals', $page, $limit, $this->anything())
168+
->willReturn([
169+
'data' => $expectedAnimals,
170+
'total' => 25,
171+
'page' => $page,
172+
'limit' => $limit,
173+
'pages' => 3
174+
]);
175+
176+
$result = $this->animal->getAll($page, $limit);
177+
178+
$this->assertIsArray($result);
179+
$this->assertArrayHasKey('data', $result);
180+
$this->assertArrayHasKey('total', $result);
181+
$this->assertArrayHasKey('pages', $result);
182+
$this->assertEquals($expectedAnimals, $result['data']);
183+
$this->assertEquals(25, $result['total']);
184+
}
185+
186+
public function testUpdateAnimal(): void
187+
{
188+
$animalId = 1;
189+
$updateData = [
190+
'description' => 'Updated description',
191+
'conservation_status' => 'Endangered'
192+
];
193+
194+
$this->mockDb->expects($this->once())
195+
->method('update')
196+
->with('animals', $animalId, $this->anything())
197+
->willReturn(true);
198+
199+
$result = $this->animal->update($animalId, $updateData);
200+
201+
$this->assertTrue($result);
202+
}
203+
204+
public function testDeleteAnimalSoftDelete(): void
205+
{
206+
$animalId = 1;
207+
208+
$this->mockDb->expects($this->once())
209+
->method('softDelete')
210+
->with('animals', $animalId)
211+
->willReturn(true);
212+
213+
$result = $this->animal->delete($animalId);
214+
215+
$this->assertTrue($result);
216+
}
217+
218+
public function testGetByCategory(): void
219+
{
220+
$categoryId = 1;
221+
$expectedAnimals = [
222+
['id' => 1, 'name' => 'Lion', 'category_id' => 1],
223+
['id' => 2, 'name' => 'Tiger', 'category_id' => 1]
224+
];
225+
226+
$this->mockDb->expects($this->once())
227+
->method('findAllBy')
228+
->with('animals', ['category_id' => $categoryId])
229+
->willReturn($expectedAnimals);
230+
231+
$result = $this->animal->getByCategory($categoryId);
232+
233+
$this->assertEquals($expectedAnimals, $result);
234+
}
235+
236+
public function testGetByConservationStatus(): void
237+
{
238+
$status = 'Endangered';
239+
$expectedAnimals = [
240+
['id' => 1, 'name' => 'Tiger', 'conservation_status' => 'Endangered'],
241+
['id' => 2, 'name' => 'Rhino', 'conservation_status' => 'Endangered']
242+
];
243+
244+
$this->mockDb->expects($this->once())
245+
->method('findAllBy')
246+
->with('animals', ['conservation_status' => $status])
247+
->willReturn($expectedAnimals);
248+
249+
$result = $this->animal->getByConservationStatus($status);
250+
251+
$this->assertEquals($expectedAnimals, $result);
252+
}
253+
254+
public function testValidateAnimalDataSuccess(): void
255+
{
256+
$validData = [
257+
'name' => 'Lion',
258+
'scientific_name' => 'Panthera leo',
259+
'category_id' => 1,
260+
'description' => 'A large cat',
261+
'habitat' => 'Savanna',
262+
'diet' => 'Carnivore',
263+
'conservation_status' => 'Vulnerable',
264+
'average_lifespan' => 15
265+
];
266+
267+
$result = $this->animal->validateData($validData);
268+
269+
$this->assertTrue($result['valid']);
270+
$this->assertEmpty($result['errors']);
271+
}
272+
273+
public function testValidateAnimalDataFailure(): void
274+
{
275+
$invalidData = [
276+
'name' => '', // Empty name
277+
'scientific_name' => 'invalid name', // Invalid format
278+
'category_id' => 'not-a-number', // Invalid type
279+
'average_lifespan' => -5 // Negative value
280+
];
281+
282+
$result = $this->animal->validateData($invalidData);
283+
284+
$this->assertFalse($result['valid']);
285+
$this->assertNotEmpty($result['errors']);
286+
$this->assertArrayHasKey('name', $result['errors']);
287+
$this->assertArrayHasKey('scientific_name', $result['errors']);
288+
$this->assertArrayHasKey('category_id', $result['errors']);
289+
$this->assertArrayHasKey('average_lifespan', $result['errors']);
290+
}
291+
292+
public function testBulkInsertAnimals(): void
293+
{
294+
$animalsData = [
295+
[
296+
'name' => 'Lion',
297+
'scientific_name' => 'Panthera leo',
298+
'category_id' => 1
299+
],
300+
[
301+
'name' => 'Tiger',
302+
'scientific_name' => 'Panthera tigris',
303+
'category_id' => 1
304+
]
305+
];
306+
307+
$this->mockDb->expects($this->once())
308+
->method('bulkInsert')
309+
->with('animals', $this->anything())
310+
->willReturn([
311+
'inserted' => 2,
312+
'failed' => 0,
313+
'errors' => []
314+
]);
315+
316+
$result = $this->animal->bulkInsert($animalsData);
317+
318+
$this->assertEquals(2, $result['inserted']);
319+
$this->assertEquals(0, $result['failed']);
320+
$this->assertEmpty($result['errors']);
321+
}
322+
323+
public function testGetStatistics(): void
324+
{
325+
$expectedStats = [
326+
'total_animals' => 150,
327+
'by_category' => [
328+
'Mammals' => 75,
329+
'Birds' => 45,
330+
'Reptiles' => 20,
331+
'Fish' => 10
332+
],
333+
'by_conservation_status' => [
334+
'Least Concern' => 80,
335+
'Vulnerable' => 35,
336+
'Endangered' => 25,
337+
'Critically Endangered' => 10
338+
]
339+
];
340+
341+
$this->mockDb->expects($this->once())
342+
->method('getStatistics')
343+
->with('animals')
344+
->willReturn($expectedStats);
345+
346+
$result = $this->animal->getStatistics();
347+
348+
$this->assertEquals($expectedStats, $result);
349+
}
350+
351+
protected function tearDown(): void
352+
{
353+
$this->animal = null;
354+
$this->mockDb = null;
355+
}
356+
}
357+

0 commit comments

Comments
 (0)