diff --git a/docs/server-audit.md b/docs/server-audit.md index 1fead52..00f4ab2 100644 --- a/docs/server-audit.md +++ b/docs/server-audit.md @@ -519,18 +519,21 @@ | `PasswordHashTest` | Argon2id verify, SHA1 legacy fallback, rehash detection | ✅ 3 теста | | `EntityCrudTest` | `Entity::update()`, `get()`, `remove_entity()`, `id()`, `to_array()` | ✅ 6 тестов | | `AreaPlacingTest` | `Area::place_in_area()`, `place_in_area_id()` | ✅ 2 теста | +| `AreaRecursionTest` | `get_inner_areas()` recursive/non-recursive, depth limit ≤10, `remove()` cascade, `get_inner_devices/scripts`, `parent_area()` | ✅ 10 тестов | +| `AreasRESTAPIControllerValidationTest` | `new_area`, `update_alias`, `remove_area`, `place_in_area`, `update_display_name` validation | ✅ 10 тестов | +| `DevicesRESTAPIControllerValidationTest` | `setup_new_device`, `do_device_action`, `update_alias`, `devices_list`, `place_in_area` validation | ✅ 12 тестов | +| `ScriptsRESTAPIControllerValidationTest` | `run_action_script`, `set_*_state`, `place_in_area` validation | ✅ 10 тестов | | `SessionsTest` | `create()`, `get_session_by_token()`, `close()`, статус | ✅ 4 теста | | `UtilsTest` | `response_error`, `response_success`, `table_row_is_exists`, `generate_token`, `dayname_translate`, `fast_ping_tcp` | ✅ 7 тестов | -**Итого:** 36 тестов, все проходят. +**Итого:** 80 тестов, 202 ассерта — все проходят. ### Что ещё нужно покрыть - `DeviceAPI\Base` — retry/backoff (mock cURL). - Транзакции в `Models\Devices`. -- Валидация в контроллерах (`DevicesRESTAPIController`, `AreasRESTAPIController`). -- `App::api_auth_guard()` и `RateLimiter` integration. -- `Area::get_inner_areas()` recursive traversal (глубина ≤10). -- `Area::remove()` — каскадное обнуление `parent_id` и `area_id`. +- `App::api_auth_guard()` и `RateLimiter` integration (требует mock `$_SERVER` / `exit`). +- Controller happy-path (success flows) — создание/обновление сущностей. +- `Models\Scripts` — `get_scopes_list`, `script_state`, `enable/disable_script`. --- diff --git a/server/SHServ/Middleware/Controller.php b/server/SHServ/Middleware/Controller.php index 2cc7e46..cda0283 100644 --- a/server/SHServ/Middleware/Controller.php +++ b/server/SHServ/Middleware/Controller.php @@ -23,7 +23,7 @@ * @param array $ids Ассоциативный массив [имя_параметра => значение] * @return array|null Ошибка response_error или null если всё ок */ - protected function validate_positive_int_ids(array $ids): ?array { + protected function validate_positive_int_ids(array $ids): ?string { foreach ($ids as $name => $value) { if ($value != intval($value) || intval($value) < 1) { return $this -> utils() -> response_error("invalid_id", [$name]); diff --git a/server/tests/AreaRecursionTest.php b/server/tests/AreaRecursionTest.php new file mode 100644 index 0000000..d2ee80f --- /dev/null +++ b/server/tests/AreaRecursionTest.php @@ -0,0 +1,223 @@ + tb = app() -> thin_builder; + + $this -> tb -> query("CREATE TABLE areas ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + alias TEXT, + display_name TEXT, + type TEXT, + parent_id INTEGER DEFAULT 0, + schema TEXT, + create_at TEXT, + update_at TEXT + )"); + + $this -> tb -> query("CREATE TABLE devices ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + area_id INTEGER, + alias TEXT, + name TEXT, + device_type TEXT, + device_ip TEXT, + device_mac TEXT, + device_hard_id TEXT, + firmware_version TEXT, + connection_status TEXT, + status TEXT, + description TEXT, + last_contact TEXT, + create_at TEXT, + update_at TEXT + )"); + + $this -> tb -> query("CREATE TABLE scripts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + area_id INTEGER, + uniq_name TEXT, + type TEXT, + state TEXT, + create_at TEXT, + update_at TEXT + )"); + } + + protected function tearDown(): void { + $this -> tb -> query("DROP TABLE IF EXISTS areas"); + $this -> tb -> query("DROP TABLE IF EXISTS devices"); + $this -> tb -> query("DROP TABLE IF EXISTS scripts"); + } + + protected function createArea(string $alias, int $parentId = 0, string $type = 'room'): int { + return $this -> tb -> insert('areas', [ + 'alias' => $alias, + 'display_name' => ucfirst($alias), + 'type' => $type, + 'parent_id' => $parentId, + 'schema' => '', + 'create_at' => date('Y-m-d H:i:s'), + ]); + } + + protected function createDevice(int $areaId, string $alias): int { + return $this -> tb -> insert('devices', [ + 'area_id' => $areaId, + 'alias' => $alias, + 'name' => ucfirst($alias), + 'device_type' => 'relay', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + } + + protected function createScript(int $areaId, string $uniqName, string $type = 'regular'): int { + return $this -> tb -> insert('scripts', [ + 'area_id' => $areaId, + 'uniq_name' => $uniqName, + 'type' => $type, + 'state' => 'enable', + 'create_at' => date('Y-m-d H:i:s'), + ]); + } + + public function test_get_inner_areas_non_recursive(): void { + $root = $this -> createArea('root', 0, 'building'); + $this -> createArea('floor', $root, 'floor'); + $this -> createArea('room', $root, 'room'); + $this -> createArea('closet', $root + 1, 'room'); // child of floor + + $area = new Area($root); + $inner = $area -> get_inner_areas(false); + + $aliases = array_map(fn($a) => $a -> alias, $inner); + $this -> assertSame(['floor', 'room'], $aliases); + } + + public function test_get_inner_areas_recursive(): void { + $root = $this -> createArea('root', 0, 'building'); + $floor = $this -> createArea('floor', $root, 'floor'); + $this -> createArea('room1', $floor, 'room'); + $this -> createArea('room2', $floor, 'room'); + + $area = new Area($root); + $inner = $area -> get_inner_areas(true); + + $aliases = array_map(fn($a) => $a -> alias, $inner); + $this -> assertSame(['floor', 'room1', 'room2'], $aliases); + } + + public function test_get_recursive_inner_areas_ids_respects_depth_limit(): void { + $ids = []; + $parentId = 0; + for ($i = 0; $i < 13; $i++) { + $parentId = $this -> createArea("area_{$i}", $parentId, 'room'); + $ids[] = $parentId; + } + + $rootArea = new Area($ids[0]); + $method = new \ReflectionMethod($rootArea, 'get_recursive_inner_areas_ids'); + $result = $method -> invoke($rootArea, $ids[0], 0); + + // Depth limit 10 means descendants up to level 10 are collected + // Chain: 0 -> 1 -> 2 -> ... -> 12 + // Expected IDs: 1..10 (10 items) + $expected = array_slice($ids, 1, 10); + $this -> assertSame($expected, $result); + $this -> assertNotContains($ids[11], $result); + $this -> assertNotContains($ids[12], $result); + } + + public function test_remove_clears_child_parent_ids(): void { + $root = $this -> createArea('root', 0, 'building'); + $child = $this -> createArea('child', $root, 'floor'); + + $rootArea = new Area($root); + $rootArea -> remove(); + + $rows = $this -> tb -> select('areas', ['parent_id'], [['id', '=', $child]]); + $this -> assertSame(0, $rows[0]['parent_id']); + } + + public function test_remove_clears_device_area_ids(): void { + $room = $this -> createArea('room', 0, 'room'); + $device = $this -> createDevice($room, 'relay_1'); + + $roomArea = new Area($room); + $roomArea -> remove(); + + $rows = $this -> tb -> select('devices', ['area_id'], [['id', '=', $device]]); + $this -> assertSame(0, $rows[0]['area_id']); + } + + public function test_get_areas_by_type_recursive(): void { + $root = $this -> createArea('root', 0, 'building'); + $floor = $this -> createArea('floor', $root, 'floor'); + $this -> createArea('room1', $floor, 'room'); + $this -> createArea('room2', $floor, 'room'); + + $area = new Area($root); + $floors = $area -> get_areas_by_type('floor', true); + $rooms = $area -> get_areas_by_type('room', true); + + $this -> assertCount(1, $floors); + $this -> assertSame('floor', $floors[0] -> alias); + $this -> assertCount(2, $rooms); + } + + public function test_get_area_by_alias_recursive(): void { + $root = $this -> createArea('root', 0, 'building'); + $floor = $this -> createArea('floor', $root, 'floor'); + $room = $this -> createArea('room1', $floor, 'room'); + + $area = new Area($root); + $found = $area -> get_area_by_alias('room1', true); + + $this -> assertInstanceOf(Area::class, $found); + $this -> assertSame($room, $found -> id()); + } + + public function test_get_inner_devices_recursive(): void { + $root = $this -> createArea('root', 0, 'building'); + $floor = $this -> createArea('floor', $root, 'floor'); + $room = $this -> createArea('room', $floor, 'room'); + $device = $this -> createDevice($room, 'relay_1'); + + $area = new Area($root); + $devices = $area -> get_inner_devices(true); + + $this -> assertCount(1, $devices); + $this -> assertSame($device, $devices[0] -> id()); + } + + public function test_get_inner_scripts_recursive(): void { + $root = $this -> createArea('root', 0, 'building'); + $floor = $this -> createArea('floor', $root, 'floor'); + $room = $this -> createArea('room', $floor, 'room'); + $script = $this -> createScript($room, 'script_1'); + + $area = new Area($root); + $scripts = $area -> get_inner_scripts(true); + + $this -> assertCount(1, $scripts); + $this -> assertSame($script, $scripts[0] -> id()); + } + + public function test_parent_area_returns_parent_or_null(): void { + $root = $this -> createArea('root', 0, 'building'); + $child = $this -> createArea('child', $root, 'floor'); + + $rootArea = new Area($root); + $childArea = new Area($child); + + $this -> assertNull($rootArea -> parent_area()); + $this -> assertInstanceOf(Area::class, $childArea -> parent_area()); + $this -> assertSame($root, $childArea -> parent_area() -> id()); + } +} diff --git a/server/tests/AreasRESTAPIControllerValidationTest.php b/server/tests/AreasRESTAPIControllerValidationTest.php new file mode 100644 index 0000000..74f99f5 --- /dev/null +++ b/server/tests/AreasRESTAPIControllerValidationTest.php @@ -0,0 +1,159 @@ + tb = app() -> thin_builder; + $this -> tb -> query("CREATE TABLE areas ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + alias TEXT, + display_name TEXT, + type TEXT, + parent_id INTEGER DEFAULT 0, + schema TEXT, + create_at TEXT, + update_at TEXT + )"); + $this -> controller = new AreasRESTAPIController(); + } + + protected function tearDown(): void { + $this -> tb -> query("DROP TABLE IF EXISTS areas"); + } + + private function decode(string $json): array { + return json_decode($json, true); + } + + public function test_new_area_rejects_invalid_alias(): void { + $result = $this -> controller -> new_area('room', 'bad-alias!', 'Room'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_alias', $data['error_alias']); + $this -> assertContains('alias', $data['failed_fields']); + } + + public function test_new_area_rejects_empty_type(): void { + $result = $this -> controller -> new_area('', 'room_1', 'Room'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('empty_field', $data['error_alias']); + $this -> assertContains('type', $data['failed_fields']); + } + + public function test_new_area_rejects_empty_display_name(): void { + $result = $this -> controller -> new_area('room', 'room_1', ''); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('empty_field', $data['error_alias']); + $this -> assertContains('display_name', $data['failed_fields']); + } + + public function test_new_area_rejects_duplicate_alias(): void { + $this -> tb -> insert('areas', [ + 'alias' => 'room_1', + 'display_name' => 'Room', + 'type' => 'room', + 'parent_id' => 0, + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> new_area('room', 'room_1', 'Room'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('alias_already_exists', $data['error_alias']); + $this -> assertContains('alias', $data['failed_fields']); + } + + public function test_remove_area_rejects_invalid_id(): void { + $result = $this -> controller -> remove_area(0); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('area_id', $data['failed_fields']); + } + + public function test_update_alias_rejects_invalid_alias(): void { + $this -> tb -> insert('areas', [ + 'alias' => 'old', + 'display_name' => 'Old', + 'type' => 'room', + 'parent_id' => 0, + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> update_alias(1, 'BAD!'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_alias', $data['error_alias']); + $this -> assertContains('new_alias', $data['failed_fields']); + } + + public function test_update_alias_rejects_duplicate_alias(): void { + $this -> tb -> insert('areas', [ + 'alias' => 'existing', + 'display_name' => 'Existing', + 'type' => 'room', + 'parent_id' => 0, + 'create_at' => date('Y-m-d H:i:s'), + ]); + $this -> tb -> insert('areas', [ + 'alias' => 'old', + 'display_name' => 'Old', + 'type' => 'room', + 'parent_id' => 0, + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> update_alias(2, 'existing'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('alias_already_exists', $data['error_alias']); + $this -> assertContains('alias', $data['failed_fields']); + } + + public function test_update_display_name_rejects_empty_name(): void { + $this -> tb -> insert('areas', [ + 'alias' => 'room', + 'display_name' => 'Room', + 'type' => 'room', + 'parent_id' => 0, + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> update_display_name(1, ''); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('empty_field', $data['error_alias']); + $this -> assertContains('display_name', $data['failed_fields']); + } + + public function test_place_in_area_rejects_invalid_target_id(): void { + $result = $this -> controller -> place_in_area(0, 1); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('target_id', $data['failed_fields']); + } + + public function test_place_in_area_rejects_invalid_place_in_area_id(): void { + $result = $this -> controller -> place_in_area(1, 0); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('place_in_area_id', $data['failed_fields']); + } + + public function test_unassign_from_area_rejects_invalid_id(): void { + $result = $this -> controller -> unassign_from_area(0); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('target_id', $data['failed_fields']); + } +} diff --git a/server/tests/DevicesRESTAPIControllerValidationTest.php b/server/tests/DevicesRESTAPIControllerValidationTest.php new file mode 100644 index 0000000..0ecf60f --- /dev/null +++ b/server/tests/DevicesRESTAPIControllerValidationTest.php @@ -0,0 +1,208 @@ + tb = app() -> thin_builder; + $this -> tb -> query("CREATE TABLE devices ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + area_id INTEGER, + alias TEXT, + name TEXT, + device_type TEXT, + device_ip TEXT, + device_mac TEXT, + device_hard_id TEXT, + firmware_version TEXT, + connection_status TEXT, + status TEXT, + description TEXT, + last_contact TEXT, + create_at TEXT, + update_at TEXT + )"); + $this -> controller = new DevicesRESTAPIController(); + } + + protected function tearDown(): void { + $this -> tb -> query("DROP TABLE IF EXISTS devices"); + } + + private function decode(string $json): array { + return json_decode($json, true); + } + + public function test_setup_new_device_rejects_invalid_ip(): void { + $result = $this -> controller -> setup_new_device('999.999.999.999', 'relay_1', 'Relay', ''); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_ip', $data['error_alias']); + $this -> assertContains('device_ip', $data['failed_fields']); + } + + public function test_setup_new_device_rejects_invalid_alias(): void { + $result = $this -> controller -> setup_new_device('192.168.1.1', 'bad-alias', 'Relay', ''); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_alias', $data['error_alias']); + $this -> assertContains('alias', $data['failed_fields']); + } + + public function test_setup_new_device_rejects_empty_name(): void { + $result = $this -> controller -> setup_new_device('192.168.1.1', 'relay_1', '', ''); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('empty_field', $data['error_alias']); + $this -> assertContains('name', $data['failed_fields']); + } + + public function test_setup_new_device_rejects_duplicate_alias(): void { + $this -> tb -> insert('devices', [ + 'alias' => 'relay_1', + 'name' => 'Relay', + 'device_type' => 'relay', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> setup_new_device('192.168.1.1', 'relay_1', 'Relay', ''); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('alias_already_exists', $data['error_alias']); + $this -> assertContains('alias', $data['failed_fields']); + } + + public function test_do_device_action_rejects_invalid_action(): void { + $this -> tb -> insert('devices', [ + 'alias' => 'relay_1', + 'name' => 'Relay', + 'device_type' => 'relay', + 'device_ip' => '192.168.1.1', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> do_device_action(1, 'BAD!', []); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_action', $data['error_alias']); + $this -> assertContains('action', $data['failed_fields']); + } + + public function test_do_device_action_rejects_invalid_json_params(): void { + $this -> tb -> insert('devices', [ + 'alias' => 'relay_1', + 'name' => 'Relay', + 'device_type' => 'relay', + 'device_ip' => '192.168.1.1', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> do_device_action(1, 'toggle', '{bad}'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_json', $data['error_alias']); + $this -> assertContains('params', $data['failed_fields']); + } + + public function test_do_device_action_rejects_non_array_params(): void { + $this -> tb -> insert('devices', [ + 'alias' => 'relay_1', + 'name' => 'Relay', + 'device_type' => 'relay', + 'device_ip' => '192.168.1.1', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> do_device_action(1, 'toggle', 123); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_params', $data['error_alias']); + $this -> assertContains('params', $data['failed_fields']); + } + + public function test_update_alias_rejects_invalid_alias(): void { + $this -> tb -> insert('devices', [ + 'alias' => 'relay_1', + 'name' => 'Relay', + 'device_type' => 'relay', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> update_alias(1, 'BAD!'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_alias', $data['error_alias']); + $this -> assertContains('new_alias', $data['failed_fields']); + } + + public function test_update_alias_rejects_duplicate_alias(): void { + $this -> tb -> insert('devices', [ + 'alias' => 'relay_1', + 'name' => 'Relay 1', + 'device_type' => 'relay', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + $this -> tb -> insert('devices', [ + 'alias' => 'relay_2', + 'name' => 'Relay 2', + 'device_type' => 'relay', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> update_alias(2, 'relay_1'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('alias_already_exists', $data['error_alias']); + $this -> assertContains('new_alias', $data['failed_fields']); + } + + public function test_devices_list_rejects_wrong_status(): void { + $result = $this -> controller -> devices_list('invalid_status'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('wrong_status_name', $data['error_alias']); + } + + public function test_place_in_area_rejects_invalid_target_id(): void { + $result = $this -> controller -> place_in_area(0, 1); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('target_id', $data['failed_fields']); + } + + public function test_unassign_from_area_rejects_invalid_id(): void { + $result = $this -> controller -> unassign_from_area(0); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('target_id', $data['failed_fields']); + } + + public function test_update_name_rejects_empty_name(): void { + $this -> tb -> insert('devices', [ + 'alias' => 'relay_1', + 'name' => 'Relay', + 'device_type' => 'relay', + 'status' => 'active', + 'create_at' => date('Y-m-d H:i:s'), + ]); + + $result = $this -> controller -> update_name(1, ''); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('empty_field', $data['error_alias']); + $this -> assertContains('name', $data['failed_fields']); + } +} diff --git a/server/tests/ScriptsRESTAPIControllerValidationTest.php b/server/tests/ScriptsRESTAPIControllerValidationTest.php new file mode 100644 index 0000000..cef834a --- /dev/null +++ b/server/tests/ScriptsRESTAPIControllerValidationTest.php @@ -0,0 +1,95 @@ + controller = new ScriptsRESTAPIController(); + } + + private function decode(string $json): array { + return json_decode($json, true); + } + + public function test_run_action_script_rejects_invalid_alias(): void { + $result = $this -> controller -> run_action_script('BAD!', []); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_alias', $data['error_alias']); + $this -> assertContains('alias', $data['failed_fields']); + } + + public function test_run_action_script_rejects_invalid_json_params(): void { + $result = $this -> controller -> run_action_script('toggle', '{bad}'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_json', $data['error_alias']); + $this -> assertContains('params', $data['failed_fields']); + } + + public function test_run_action_script_rejects_non_array_params(): void { + $result = $this -> controller -> run_action_script('toggle', 123); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_params', $data['error_alias']); + $this -> assertContains('params', $data['failed_fields']); + } + + public function test_run_action_script_returns_not_found_for_valid_unregistered_alias(): void { + $result = $this -> controller -> run_action_script('toggle', []); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('action_script_not_found', $data['error_alias']); + } + + public function test_set_scope_state_rejects_invalid_state(): void { + $result = $this -> controller -> set_scope_state('scope1', 'invalid'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_state', $data['error_alias']); + $this -> assertContains('state', $data['failed_fields']); + } + + public function test_set_regular_script_state_rejects_invalid_state(): void { + $result = $this -> controller -> set_regular_script_state('script1', 'invalid'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_state', $data['error_alias']); + $this -> assertContains('state', $data['failed_fields']); + } + + public function test_set_action_script_state_rejects_invalid_state(): void { + $result = $this -> controller -> set_action_script_state('action1', 'invalid'); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_state', $data['error_alias']); + $this -> assertContains('state', $data['failed_fields']); + } + + public function test_place_in_area_rejects_invalid_target_id(): void { + $result = $this -> controller -> place_in_area(0, 1); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('target_id', $data['failed_fields']); + } + + public function test_place_in_area_rejects_invalid_place_in_area_id(): void { + $result = $this -> controller -> place_in_area(1, 0); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('place_in_area_id', $data['failed_fields']); + } + + public function test_unassign_from_area_rejects_invalid_id(): void { + $result = $this -> controller -> unassign_from_area(0); + $data = $this -> decode($result); + $this -> assertFalse($data['status']); + $this -> assertSame('invalid_id', $data['error_alias']); + $this -> assertContains('target_id', $data['failed_fields']); + } +} diff --git a/server/tests/TestApp.php b/server/tests/TestApp.php index a6c6feb..613d1bd 100644 --- a/server/tests/TestApp.php +++ b/server/tests/TestApp.php @@ -4,5 +4,6 @@ public \Fury\Modules\ThinBuilder\ThinBuilder $thin_builder; public \SHServ\Utils $utils; public \SHServ\Sessions $sessions; + public \SHServ\DevTools $devtools; public $factory = null; } diff --git a/server/tests/bootstrap.php b/server/tests/bootstrap.php index 20c99d9..b31dfbf 100644 --- a/server/tests/bootstrap.php +++ b/server/tests/bootstrap.php @@ -16,6 +16,7 @@ $testApp->thin_builder = $tb; $testApp->utils = new \SHServ\Utils(); $testApp->sessions = new \SHServ\Sessions(); +$testApp->devtools = new \SHServ\DevTools(); \Fury\Kernel\AppContainer::set_app($testApp); @@ -34,6 +35,24 @@ 'area_not_found' => 'Area not found', 'invalid_input' => 'Invalid input', 'internal_error' => 'Internal server error', + 'invalid_alias' => 'Invalid alias format', + 'empty_field' => 'Field cannot be empty', + 'alias_already_exists' => 'Alias already exists', + 'invalid_id' => 'Invalid identifier', + 'area_not_exists' => 'Area does not exist', + 'undefined_error' => 'Undefined error', + 'invalid_ip' => 'Invalid IP address', + 'invalid_json' => 'Invalid JSON format', + 'invalid_params' => 'Invalid parameters', + 'invalid_action' => 'Invalid action alias', + 'invalid_state' => 'Invalid state value', + 'wrong_status_name' => 'Wrong status name', + 'action_script_not_found' => 'Action script not found', + 'scope_not_found' => 'Scope not found', + 'file_not_exists' => 'File does not exist', + 'invalid_path' => 'Invalid path', + 'device_request_fail' => 'Device request failed', + 'parent_area_not_found' => 'Parent area not found', ], ]); }