From 3ffcb447aafd0d59a1850f03c7631a4d690212d0 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sat, 18 Oct 2025 15:14:12 +0200 Subject: [PATCH 01/62] add test test_can_get_virtual_servername --- tests/DevLiveServer/RefactorFunctionsTest.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/DevLiveServer/RefactorFunctionsTest.php b/tests/DevLiveServer/RefactorFunctionsTest.php index a2a09c52..4e868c3e 100644 --- a/tests/DevLiveServer/RefactorFunctionsTest.php +++ b/tests/DevLiveServer/RefactorFunctionsTest.php @@ -270,6 +270,29 @@ public function test_invalid_parameter_size(): void $this->assertEquals('--Lorem ipsum dolür sit amet,', $result); $this->ts3_VirtualServer->serverGroupDelete($sid); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + + /** + * @throws \Exception + */ + public function test_can_get_virtual_servername(): void + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + try { + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + } catch(TeamSpeak3Exception $e) { + echo $e->getMessage(); + } + + $serverName = $this->ts3_VirtualServer->getInfo()['virtualserver_name']; + $this->assertNotEmpty($serverName); + + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); } From a60bf49ea622cd2281cc09ec536900f840756d99 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sat, 18 Oct 2025 15:35:30 +0200 Subject: [PATCH 02/62] add test test_can_get_virtual_servername --- tests/DevLiveServer/RefactorFunctionsTest.php | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/tests/DevLiveServer/RefactorFunctionsTest.php b/tests/DevLiveServer/RefactorFunctionsTest.php index 4e868c3e..a2a09c52 100644 --- a/tests/DevLiveServer/RefactorFunctionsTest.php +++ b/tests/DevLiveServer/RefactorFunctionsTest.php @@ -270,29 +270,6 @@ public function test_invalid_parameter_size(): void $this->assertEquals('--Lorem ipsum dolür sit amet,', $result); $this->ts3_VirtualServer->serverGroupDelete($sid); - $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); - $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); - } - - /** - * @throws \Exception - */ - public function test_can_get_virtual_servername(): void - { - if ($this->active == 'false') { - $this->markTestSkipped('DevLiveServer ist not active'); - } - - try { - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - echo $e->getMessage(); - } - - $serverName = $this->ts3_VirtualServer->getInfo()['virtualserver_name']; - $this->assertNotEmpty($serverName); - - $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); } From 71ae742345afcad474e39ea1c2d05d4c2fe31eb6 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sat, 18 Oct 2025 15:36:15 +0200 Subject: [PATCH 03/62] add test where found issues at public projects --- .../FoundIssuesOnPublicProjectsTest.php | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php diff --git a/tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php b/tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php new file mode 100644 index 00000000..e74de4dd --- /dev/null +++ b/tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php @@ -0,0 +1,84 @@ +active = str_replace('DEV_LIVE_SERVER_AVAILABLE=', '', preg_replace('#\n(?!\n)#', '', $env[2])); + $this->host = str_replace('DEV_LIVE_SERVER_HOST=', '', preg_replace('#\n(?!\n)#', '', $env[3])); + $this->queryPort = str_replace('DEV_LIVE_SERVER_QUERY_PORT=', '', preg_replace('#\n(?!\n)#', '', $env[4])); + $this->user = str_replace('DEV_LIVE_SERVER_QUERY_USER=', '', preg_replace('#\n(?!\n)#', '', $env[5])); + $this->password = str_replace('DEV_LIVE_SERVER_QUERY_USER_PASSWORD=', '', preg_replace('#\n(?!\n)#', '', $env[6])); + } else { + $this->active = 'false'; + } + + $this->ts3_server_uri = 'serverquery://'.$this->user.':'.$this->password.'@'.$this->host.':'.$this->queryPort. + '/?server_port=9987'. + '&no_query_clients=0'. + '&timeout=30'; + } + + /** + * @throws \Exception + */ + public function test_can_get_virtual_servername(): void + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + try { + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + } catch(TeamSpeak3Exception $e) { + echo $e->getMessage(); + } + + try { + $this->ts3_VirtualServer->virtualserver_name; + }catch (TeamSpeak3Exception $e) { + $this->assertEquals("node 'PlanetTeamSpeak\TeamSpeak3Framework\Node\Server' has no property named 'virtualserver_name'", $e->getMessage()); + } + + $serverName = $this->ts3_VirtualServer->getInfo()['virtualserver_name']; + $this->assertNotEmpty($serverName); + + + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } +} From 2d635abcddaf99b8b427d19a0246e27be41bf8aa Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sat, 18 Oct 2025 19:10:56 +0200 Subject: [PATCH 04/62] fix the convert issue at iconGetName wrong variable, $key instead of $val --- src/Node/Node.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Node/Node.php b/src/Node/Node.php index 772ed024..3457a4dd 100644 --- a/src/Node/Node.php +++ b/src/Node/Node.php @@ -136,8 +136,11 @@ public function iconIsLocal(string $key): bool public function iconGetName(string $key): StringHelper { $iconid = $this[$key]; - if (! is_int($iconid)) { + + if ($iconid instanceof StringHelper) { $iconid = $iconid->toInt(); + } elseif (!is_int($iconid)) { + $iconid = (int) $iconid; } $iconid = $iconid < 0 ? pow(2, 32) - abs($iconid) : $iconid; @@ -318,7 +321,7 @@ public function getInfo(bool $extend = true, bool $convert = false): array } elseif (str_ends_with($key, '_version')) { $info[$key] = Convert::version($val); } elseif (str_ends_with($key, '_icon_id')) { - $info[$key] = $this->iconGetName($key)->filterDigits(); + $info[$key] = $this->iconGetName($val)->filterDigits(); } } } From 379a461f9ea1c34629ee2d4e98298184b42f1e84 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sat, 18 Oct 2025 19:13:04 +0200 Subject: [PATCH 05/62] add test_can_getInfo_with_convert_option --- .../FoundIssuesOnPublicProjectsTest.php | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php b/tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php index e74de4dd..3bacda92 100644 --- a/tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php +++ b/tests/DevLiveServer/FoundIssuesOnPublicProjectsTest.php @@ -70,13 +70,36 @@ public function test_can_get_virtual_servername(): void try { $this->ts3_VirtualServer->virtualserver_name; - }catch (TeamSpeak3Exception $e) { + } catch (TeamSpeak3Exception $e) { $this->assertEquals("node 'PlanetTeamSpeak\TeamSpeak3Framework\Node\Server' has no property named 'virtualserver_name'", $e->getMessage()); } - $serverName = $this->ts3_VirtualServer->getInfo()['virtualserver_name']; + $serverName = $this->ts3_VirtualServer->getInfo()['virtualserver_name']; $this->assertNotEmpty($serverName); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + + /** + * @throws \Exception + */ + public function test_can_getInfo_with_convert_option(): void + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + try { + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + } catch(TeamSpeak3Exception $e) { + echo $e->getMessage(); + } + + $serverinfo = $this->ts3_VirtualServer->getInfo(true, true); + $this->assertArrayHasKey('virtualserver_icon_id', $serverinfo); + $this->assertEquals(1, $serverinfo['virtualserver_icon_id']); + $this->assertIsInt($serverinfo['virtualserver_icon_id']); $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); From 5a1c06288a3a52888b85a3b051f085dd2dd18522 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sat, 18 Oct 2025 19:36:47 +0200 Subject: [PATCH 06/62] missing chaining toInt() --- src/Node/Node.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Node/Node.php b/src/Node/Node.php index 3457a4dd..3fc5880a 100644 --- a/src/Node/Node.php +++ b/src/Node/Node.php @@ -139,7 +139,7 @@ public function iconGetName(string $key): StringHelper if ($iconid instanceof StringHelper) { $iconid = $iconid->toInt(); - } elseif (!is_int($iconid)) { + } elseif (! is_int($iconid)) { $iconid = (int) $iconid; } @@ -321,7 +321,7 @@ public function getInfo(bool $extend = true, bool $convert = false): array } elseif (str_ends_with($key, '_version')) { $info[$key] = Convert::version($val); } elseif (str_ends_with($key, '_icon_id')) { - $info[$key] = $this->iconGetName($val)->filterDigits(); + $info[$key] = $this->iconGetName($val)->filterDigits()->toInt(); } } } @@ -386,7 +386,7 @@ public function getFullInfo(?int $serverPort = null, bool $convert = false): arr } elseif ($keyObj->endsWith('_version')) { $info[$keyObj->toString()] = Convert::version($val); } elseif ($keyObj->endsWith('_icon_id')) { - $info[$keyObj->toString()] = $this->iconGetName($key)->filterDigits(); + $info[$keyObj->toString()] = $this->iconGetName($key)->filterDigits()->toInt(); } } } From 81465f6394eb90586ef891bc43bfa492b38ee6a1 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sat, 18 Oct 2025 22:28:37 +0200 Subject: [PATCH 07/62] Remove getFullInfo. Duplicate information same as getInfo() but in non flatten array. --- src/Node/Node.php | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/src/Node/Node.php b/src/Node/Node.php index 3fc5880a..edf16210 100644 --- a/src/Node/Node.php +++ b/src/Node/Node.php @@ -349,51 +349,6 @@ public function getInfo(bool $extend = true, bool $convert = false): array return $info; } - /** - * Returns information about the host node and optionally the selected virtual server. - */ - public function getFullInfo(?int $serverPort = null, bool $convert = false): array - { - // Retrieve host/node information - $this->fetchNodeInfo(); - $info = $this->nodeInfo; - - // Retrieve VirtualServer if port has been specified - if ($serverPort !== null) { - try { - $virtualServer = $this->serverGetByPort($serverPort); //polymorphic call is okay because we use strict analysis - $vsInfo = $virtualServer->getInfo(false, false); // raw value - $info = array_merge($info, $vsInfo); - } catch (\Exception $e) { - $info['virtualserver_error'] = $e->getMessage(); - } - } - - if ($convert) { - foreach ($info as $key => $val) { - $keyObj = StringHelper::factory($key); - - if ($keyObj->contains('_bytes_')) { - $info[$keyObj->toString()] = Convert::bytes($val); - } elseif ($keyObj->contains('_bandwidth_')) { - $info[$keyObj->toString()] = Convert::bytes($val).'/s'; - } elseif ($keyObj->contains('_packets_')) { - $info[$keyObj->toString()] = number_format($val, 0, null, '.'); - } elseif ($keyObj->contains('_packetloss_')) { - $info[$keyObj->toString()] = sprintf('%01.2f', floatval($val instanceof StringHelper ? $val->toString() : strval($val)) * 100).'%'; - } elseif ($keyObj->endsWith('_uptime')) { - $info[$keyObj->toString()] = Convert::seconds($val); - } elseif ($keyObj->endsWith('_version')) { - $info[$keyObj->toString()] = Convert::version($val); - } elseif ($keyObj->endsWith('_icon_id')) { - $info[$keyObj->toString()] = $this->iconGetName($key)->filterDigits()->toInt(); - } - } - } - - return $info; - } - /** * Returns the specified property or a pre-defined default value from the node info array. * From e4739d5db5a37b9a5bd5577ae2b4837df66f0f85 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 14:25:54 +0200 Subject: [PATCH 08/62] add UniTest Badges --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6102a896..252923b2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # TeamSpeak X PHP Framework - +[![PHP-CS-Fixer](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpcsfixer.yml/badge.svg?branch=main)](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpcsfixer.yml) +[![PHPUnit](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpunit.yml/badge.svg?branch=main)](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpunit.yml) > **_IMPORTANT CHANGE_**
> With Version 3.0.0 we refactored to integrate phpseclib3. This changes affects how TCP connections are established. > The "raw" mode was removed, and the support for only ssh mode was established to handel Teamspeak 3 and Teamspeak 6 Server connections. From d5746ceef4a7ed874719318a874943fb0d6f9684 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 14:45:25 +0200 Subject: [PATCH 09/62] try integrate codecov --- .github/workflows/coverage.yml | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 00000000..5d0c6294 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,37 @@ +name: Run tests and upload coverage + +on: + push: + branches: + - main + - ts-x-refactoring-dev + pull_request: + branches: + - main + - ts-x-refactoring-dev + +jobs: + test: + name: Run tests and collect coverage + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: pip install pytest pytest-cov + + - name: Run tests + run: pytest --cov --cov-branch --cov-report=xml + + - name: Upload results to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} From c36dba8dfe62db16d5056b15ace5ca405aca2bdc Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 14:48:52 +0200 Subject: [PATCH 10/62] try integrate codecov --- .github/workflows/coverage.yml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5d0c6294..e50c8899 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -12,26 +12,28 @@ on: jobs: test: - name: Run tests and collect coverage runs-on: ubuntu-latest + steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 - with: - fetch-depth: 2 - - name: Set up Python - uses: actions/setup-python@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 with: - python-version: '3.11' + php-version: '8.2' + coverage: xdebug - name: Install dependencies - run: pip install pytest pytest-cov + run: composer install --no-progress --prefer-dist - - name: Run tests - run: pytest --cov --cov-branch --cov-report=xml + - name: Run PHPUnit tests with coverage + run: | + vendor/bin/phpunit --coverage-clover=coverage.xml - - name: Upload results to Codecov + - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.xml + fail_ci_if_error: true From 85fd4752aa12eaa76dc44aabd26aa2879e7eb34a Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 14:50:58 +0200 Subject: [PATCH 11/62] Setup environment .env update to example to port 10022 --- .env.testing.example | 2 +- .github/workflows/coverage.yml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.env.testing.example b/.env.testing.example index 165994d1..1434c598 100644 --- a/.env.testing.example +++ b/.env.testing.example @@ -2,7 +2,7 @@ HOST=127.0.0.1 PORT=9987 DEV_LIVE_SERVER_AVAILABLE=false DEV_LIVE_SERVER_HOST=127.0.0.1 -DEV_LIVE_SERVER_QUERY_PORT=10011 +DEV_LIVE_SERVER_QUERY_PORT=10022 DEV_LIVE_SERVER_QUERY_USER=serveradmin DEV_LIVE_SERVER_QUERY_USER_PASSWORD=PASSWORD DEV_LIVE_SERVER_UNIT_TEST_CHANNEL=UnitTest diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e50c8899..e5cc1d44 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -27,6 +27,9 @@ jobs: - name: Install dependencies run: composer install --no-progress --prefer-dist + - name: Setup environment + run: cp .env.testing.example .env.testing + - name: Run PHPUnit tests with coverage run: | vendor/bin/phpunit --coverage-clover=coverage.xml From 110ca7ecd5ff65ed0454ea37e0576e156eba26de Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 15:41:55 +0200 Subject: [PATCH 12/62] add create coverage badge from local without third party tools --- .github/workflows/coverage.yml | 42 --------------------- README.md | 2 + composer.json | 5 ++- doc/coverage/coverage-badge.svg | 19 ++++++++++ generate-coverage-badge.php | 66 +++++++++++++++++++++++++++++++++ phpunit.xml.dist | 2 +- 6 files changed, 92 insertions(+), 44 deletions(-) delete mode 100644 .github/workflows/coverage.yml create mode 100644 doc/coverage/coverage-badge.svg create mode 100644 generate-coverage-badge.php diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index e5cc1d44..00000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Run tests and upload coverage - -on: - push: - branches: - - main - - ts-x-refactoring-dev - pull_request: - branches: - - main - - ts-x-refactoring-dev - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - coverage: xdebug - - - name: Install dependencies - run: composer install --no-progress --prefer-dist - - - name: Setup environment - run: cp .env.testing.example .env.testing - - - name: Run PHPUnit tests with coverage - run: | - vendor/bin/phpunit --coverage-clover=coverage.xml - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.xml - fail_ci_if_error: true diff --git a/README.md b/README.md index 252923b2..6a9d6f5e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # TeamSpeak X PHP Framework [![PHP-CS-Fixer](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpcsfixer.yml/badge.svg?branch=main)](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpcsfixer.yml) [![PHPUnit](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpunit.yml/badge.svg?branch=main)](https://github.com/Prestige-Solution/ts-x-php-framework/actions/workflows/phpunit.yml) +![Coverage](doc/coverage/coverage-badge.svg) +> [!CAUTION] > **_IMPORTANT CHANGE_**
> With Version 3.0.0 we refactored to integrate phpseclib3. This changes affects how TCP connections are established. > The "raw" mode was removed, and the support for only ssh mode was established to handel Teamspeak 3 and Teamspeak 6 Server connections. diff --git a/composer.json b/composer.json index 4bdec724..25bc4d88 100644 --- a/composer.json +++ b/composer.json @@ -46,6 +46,9 @@ "Composer\\Config::disableProcessTimeout", "\"vendor/bin/phpunit\" --no-coverage" ], - "coverage": "\"vendor/bin/phpunit\"" + "coverage": [ + "Composer\\Config::disableProcessTimeout", + "\"vendor/bin/phpunit\" --configuration phpunit.xml.dist" + ] } } diff --git a/doc/coverage/coverage-badge.svg b/doc/coverage/coverage-badge.svg new file mode 100644 index 00000000..f2467fa9 --- /dev/null +++ b/doc/coverage/coverage-badge.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + coverage + 44% + + \ No newline at end of file diff --git a/generate-coverage-badge.php b/generate-coverage-badge.php new file mode 100644 index 00000000..0fae18d9 --- /dev/null +++ b/generate-coverage-badge.php @@ -0,0 +1,66 @@ +project->metrics)) { + die("No project-level metrics found in Clover XML\n"); +} + +$metrics = $clover->project->metrics; +$elements = (int) $metrics['elements']; +$covered = (int) $metrics['coveredelements']; + +$coverage = $elements > 0 ? round($covered / $elements * 100) : 0; + +if ($coverage >= 90) { + $color = "#4c1"; +} elseif ($coverage >= 75) { + $color = "#dfb317"; +} elseif ($coverage >= 50) { + $color = "#fe7d37"; +} else { + $color = "#e05d44"; +} + +$label = "coverage"; +$value = $coverage . "%"; +$labelWidth = 60; +$valueWidth = 40; +$height = 20; + +$totalWidth = $labelWidth + $valueWidth; +$labelX = $labelWidth / 2; +$valueX = $labelWidth + ($valueWidth / 2); + + +$svg = << + + + + + + + + + + + + + + {$label} + {$value} + + +SVG; + + + +file_put_contents($outputSvg, $svg); +echo "Badge created: $outputSvg ($coverage%)\n"; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5f7c640b..7b7a8ef8 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,7 +19,7 @@ > - ./ + ./src ./images From 9ad37902aaace7f0a03153638b204e1eeab626a2 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 19:30:28 +0200 Subject: [PATCH 13/62] code-style --- generate-coverage-badge.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/generate-coverage-badge.php b/generate-coverage-badge.php index 0fae18d9..86fcd48a 100644 --- a/generate-coverage-badge.php +++ b/generate-coverage-badge.php @@ -1,13 +1,14 @@ project->metrics)) { +if (! isset($clover->project->metrics)) { die("No project-level metrics found in Clover XML\n"); } @@ -18,17 +19,17 @@ $coverage = $elements > 0 ? round($covered / $elements * 100) : 0; if ($coverage >= 90) { - $color = "#4c1"; + $color = '#4c1'; } elseif ($coverage >= 75) { - $color = "#dfb317"; + $color = '#dfb317'; } elseif ($coverage >= 50) { - $color = "#fe7d37"; + $color = '#fe7d37'; } else { - $color = "#e05d44"; + $color = '#e05d44'; } -$label = "coverage"; -$value = $coverage . "%"; +$label = 'coverage'; +$value = $coverage.'%'; $labelWidth = 60; $valueWidth = 40; $height = 20; @@ -37,7 +38,6 @@ $labelX = $labelWidth / 2; $valueX = $labelWidth + ($valueWidth / 2); - $svg = << @@ -60,7 +60,5 @@ SVG; - - file_put_contents($outputSvg, $svg); echo "Badge created: $outputSvg ($coverage%)\n"; From 2b90c447363f840fe47aee01903f30ccbdf29f0f Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 19:40:44 +0200 Subject: [PATCH 14/62] validate generate code-coverage badge with an full test. --- doc/coverage/coverage-badge.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/coverage/coverage-badge.svg b/doc/coverage/coverage-badge.svg index f2467fa9..673d686e 100644 --- a/doc/coverage/coverage-badge.svg +++ b/doc/coverage/coverage-badge.svg @@ -14,6 +14,6 @@ coverage - 44% + 46% \ No newline at end of file From 0a193c8e2fe9340b06b3dea0978de49d8a88de4a Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 20:54:04 +0200 Subject: [PATCH 15/62] add test can send text message and poke client --- tests/DevLiveServer/ClientTest.php | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index 0bf45607..e466e67c 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -151,6 +151,50 @@ public function test_can_move_user() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_send_client_text_message() + { + if ($this->user_test_active == 'false' || $this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + + $userID = $ts3_VirtualServer->clientGetByName($this->ts3_unit_test_userName)->getId(); + $userID = $ts3_VirtualServer->clientGetById($userID); + $userID->message('Hello World'); + + $this->asserttrue(true); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_send_client_poke() + { + if ($this->user_test_active == 'false' || $this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + + $userID = $ts3_VirtualServer->clientGetByName($this->ts3_unit_test_userName)->getId(); + $userID = $ts3_VirtualServer->clientGetById($userID); + $userID->poke('UnitTest'); + + $this->asserttrue(true); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws ServerQueryException From 11ff90c6fa061195bec6e06148bfbb675cf870b3 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 21:41:44 +0200 Subject: [PATCH 16/62] fix issues with array handling at channelFileList --- src/Node/Server.php | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/Node/Server.php b/src/Node/Server.php index 426857a8..776a9d80 100644 --- a/src/Node/Server.php +++ b/src/Node/Server.php @@ -476,14 +476,31 @@ public function channelClientPermRemove(int $cid, int $cldbid, int|array $permid */ public function channelFileList(int $cid, string $cpw = '', string $path = '/', bool $recursive = false): array { - $files = $this->execute('ftgetfilelist', ['cid' => $cid, 'cpw' => $cpw, 'path' => $path])->toArray(); + try { + $files = $this->execute('ftgetfilelist', ['cid' => $cid, 'cpw' => $cpw, 'path' => $path])->toArray(); + } catch (ServerQueryException $e) { + // only catch an empty result + if (str_contains($e->getMessage(), 'database empty result set')) { + return []; + } + throw $e; + } + $count = count($files); + $flattened = []; for ($i = 0; $i < $count; $i++) { + // proof exists + if (empty($files[$i]['name'])) { + continue; // skip entry without a valid file extension + } + $files[$i]['sid'] = $this->getId(); - $files[$i]['cid'] = $files[0]['cid']; - $files[$i]['path'] = $files[0]['path']; - $files[$i]['src'] = new StringHelper($cid ? $files[$i]['path'] : '/'); + $files[$i]['cid'] = $files[$i]['cid'] ?? $cid; + $files[$i]['path'] = $files[$i]['path'] ?? $path; + + $src = $files[$i]['path'] ?? '/'; + $files[$i]['src'] = StringHelper::factory($src); if (! $files[$i]['src']->endsWith('/')) { $files[$i]['src']->append('/'); @@ -491,14 +508,17 @@ public function channelFileList(int $cid, string $cpw = '', string $path = '/', $files[$i]['src']->append($files[$i]['name']); - if ($recursive && $files[$i]['type'] == TeamSpeak3::FILE_TYPE_DIRECTORY) { - $files = array_merge($files, $this->channelFileList($cid, $cpw, $path.$files[$i]['name'], $recursive)); + $flattened[] = $files[$i]; + + if ($recursive && ($files[$i]['type'] ?? '') == TeamSpeak3::FILE_TYPE_DIRECTORY) { + $nested = $this->channelFileList($cid, $cpw, $files[$i]['path'].$files[$i]['name'], $recursive); + $flattened = array_merge($flattened, $nested); } } - uasort($files, [__CLASS__, 'sortFileList']); + uasort($flattened, [__CLASS__, 'sortFileList']); - return $files; + return $flattened; } /** From 3f7858300b514424f54dff4f443fbed5f88ca896 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Sun, 19 Oct 2025 21:42:57 +0200 Subject: [PATCH 17/62] validate directory can create, get file lists and delete the directory validate can get channel level --- doc/coverage/coverage-badge.svg | 2 +- tests/DevLiveServer/ChannelTest.php | 59 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/doc/coverage/coverage-badge.svg b/doc/coverage/coverage-badge.svg index 673d686e..ff2419e7 100644 --- a/doc/coverage/coverage-badge.svg +++ b/doc/coverage/coverage-badge.svg @@ -14,6 +14,6 @@ coverage - 46% + 47% \ No newline at end of file diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index ed38ffd5..2f775c05 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -575,6 +575,65 @@ public function test_invalid_parameter_size_length() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_handle_file_directory() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_channel($ts3_VirtualServer); + + $ts3_VirtualServer->channelDirCreate($this->test_cid, '', '/test_dir'); + $fileList = $ts3_VirtualServer->channelFileList($this->test_cid); + + foreach ($fileList as $file) { + $this->assertEquals('test_dir', $file['name']); + $this->assertIsString($file['name']); + } + + $ts3_VirtualServer->channelFileDelete($this->test_cid, '', '/test_dir'); + $fileList = $ts3_VirtualServer->channelFileList($this->test_cid); + $this->assertEmpty($fileList); + + $this->unset_play_test_channel($ts3_VirtualServer); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_get_level() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_channel($ts3_VirtualServer); + + //get level means hierarchy level + $getLevel = $ts3_VirtualServer->channelGetLevel($this->test_cid); + $this->assertEquals(1, $getLevel); + + $channelCidLevelOne = $ts3_VirtualServer->channelgetByName('Play-Test')->getId(); + $cidChannelLevelTwo = $ts3_VirtualServer->channelcreate(['channel_name' => 'Standard Channel Level 2', 'channel_flag_permanent' => 1, 'cpid' => $channelCidLevelOne]); + $getLevelTwo = $ts3_VirtualServer->channelGetLevel($cidChannelLevelTwo); + $this->assertEquals(2, $getLevelTwo); + + $this->unset_play_test_channel($ts3_VirtualServer); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws TransportException From 5ab04c821ef80a4227903c86954aec2bbcb9637b Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 19:06:37 +0200 Subject: [PATCH 18/62] add test_can_find_client_by_name_pattern --- tests/DevLiveServer/ClientTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index e466e67c..2bfbba7f 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -195,6 +195,30 @@ public function test_can_send_client_poke() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws AdapterException + * @throws TransportException + * @throws ServerQueryException + * @throws HelperException + */ + public function test_can_find_client_by_name_pattern() + { + if ($this->user_test_active == 'false' || $this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + + $userFindings = $ts3_VirtualServer->clientFind('UnitT'); + + foreach ($userFindings as $user) { + $this->assertEquals('UnitTestUser', $user['client_nickname']); + } + + $this->asserttrue(true); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws ServerQueryException From 4c5f8ac4f5231ed6222f7bd11a326a07b239b1a6 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 19:20:13 +0200 Subject: [PATCH 19/62] add test_can_get_clientListDB and test_can_get_clientInfoDB --- tests/DevLiveServer/ClientTest.php | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index 2bfbba7f..66144dbc 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -219,6 +219,56 @@ public function test_can_find_client_by_name_pattern() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws AdapterException + * @throws TransportException + * @throws ServerQueryException + * @throws HelperException + */ + public function test_can_get_clientListDB() + { + if ($this->user_test_active == 'false' || $this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + + $clientListDb = $ts3_VirtualServer->clientListDb(); + $this->assertIsArray($clientListDb); + + $this->asserttrue(true); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_get_clientInfoDB() + { + if ($this->user_test_active == 'false' || $this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $clientInfoDB= []; + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $clientListDb = $ts3_VirtualServer->clientListDb(); + + foreach ($clientListDb as $client) { + if ($client['client_nickname'] == 'UnitTestUser') { + $clientInfoDB = $ts3_VirtualServer->clientInfoDb($client['cldbid']); + } + } + + $this->assertIsArray($clientInfoDB); + + $this->asserttrue(true); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws ServerQueryException From bb1e6845dde9a1585550cbd598744413b329a1f6 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 19:33:28 +0200 Subject: [PATCH 20/62] wrong writeline ['pattern' => $pattern, ($uid) ? '-uid' : null, '-details'] convert to clientdbfind pattern=UnitTestUser 0=-details --- src/Node/Server.php | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Node/Server.php b/src/Node/Server.php index 776a9d80..4a17c84d 100644 --- a/src/Node/Server.php +++ b/src/Node/Server.php @@ -786,8 +786,25 @@ public function clientInfoDb(int $cldbid): array */ public function clientFindDb(string $pattern, bool $uid = false): array { - return array_keys($this->execute('clientdbfind', ['pattern' => $pattern, ($uid) ? '-uid' : null, '-details']) - ->toAssocArray('cldbid')); + $args = ['pattern' => $pattern]; + + // Flags must be appended as separate parameters + if ($uid) { + $args['-uid'] = null; + } + + $args['-details'] = null; + + try { + $result = $this->execute('clientdbfind', $args)->toAssocArray('cldbid'); + } catch (ServerQueryException $e) { + if (str_contains($e->getMessage(), 'database empty result set')) { + return []; + } + throw $e; + } + + return array_keys($result); } /** From 2edb52dfc3cf7daa51652a87f5e73cbc5af5c429 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 19:33:55 +0200 Subject: [PATCH 21/62] validate fix with clientFindDb --- tests/DevLiveServer/ClientTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index 66144dbc..8b10f109 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -269,6 +269,31 @@ public function test_can_get_clientInfoDB() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws AdapterException + * @throws TransportException + * @throws ServerQueryException + * @throws HelperException + */ + public function test_can_clientFindDb_by_name_pattern() + { + if ($this->user_test_active == 'false' || $this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + + $resultArray = $ts3_VirtualServer->clientFindDb('UnitT'); + + foreach ($resultArray as $user) { + var_dump($user); + $this->assertEquals('UnitTestUser', $user['client_nickname']); + } + + $this->asserttrue(true); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws ServerQueryException From 0f4049a679044eed831fdc47dcbd86efbdc43b58 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 19:35:22 +0200 Subject: [PATCH 22/62] set username by defined config --- tests/DevLiveServer/ClientTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index 8b10f109..83883b06 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -212,7 +212,7 @@ public function test_can_find_client_by_name_pattern() $userFindings = $ts3_VirtualServer->clientFind('UnitT'); foreach ($userFindings as $user) { - $this->assertEquals('UnitTestUser', $user['client_nickname']); + $this->assertEquals($this->ts3_unit_test_userName, $user['client_nickname']); } $this->asserttrue(true); @@ -258,7 +258,7 @@ public function test_can_get_clientInfoDB() $clientListDb = $ts3_VirtualServer->clientListDb(); foreach ($clientListDb as $client) { - if ($client['client_nickname'] == 'UnitTestUser') { + if ($client['client_nickname'] == $this->ts3_unit_test_userName) { $clientInfoDB = $ts3_VirtualServer->clientInfoDb($client['cldbid']); } } @@ -287,7 +287,7 @@ public function test_can_clientFindDb_by_name_pattern() foreach ($resultArray as $user) { var_dump($user); - $this->assertEquals('UnitTestUser', $user['client_nickname']); + $this->assertEquals($this->ts3_unit_test_userName, $user['client_nickname']); } $this->asserttrue(true); From 2520c42b5459df53f2c33a66913d16aa8f735ec1 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 20:58:46 +0200 Subject: [PATCH 23/62] fix server->isOffline() --- src/Node/Server.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Node/Server.php b/src/Node/Server.php index 4a17c84d..819b1740 100644 --- a/src/Node/Server.php +++ b/src/Node/Server.php @@ -2894,7 +2894,21 @@ public function isOnline(): bool */ public function isOffline(): bool { - return $this['virtualserver_status'] == 'offline'; + // First, try to get the property without forcing a refresh + $status = $this->getProperty('virtualserver_status'); + + if ($status === null) { + // Force refresh the node info + try { + $this->fetchNodeInfo(); + $status = $this->getProperty('virtualserver_status', 'offline'); + } catch (Exception) { + // If fetching fails, assume it offline + return true; + } + } + + return $status === 'offline'; } /** From c4baeb929faf63b3c281f042d138b25fd0263627 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 20:59:04 +0200 Subject: [PATCH 24/62] fix server->isOnline() --- src/Node/Server.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Node/Server.php b/src/Node/Server.php index 819b1740..048737ed 100644 --- a/src/Node/Server.php +++ b/src/Node/Server.php @@ -2884,7 +2884,21 @@ protected static function sortFileList(array $a, array $b): int */ public function isOnline(): bool { - return $this['virtualserver_status'] == 'online'; + // First, try to get the property without forcing a refresh + $status = $this->getProperty('virtualserver_status'); + + if ($status === null) { + // Force refresh the node info + try { + $this->fetchNodeInfo(); + $status = $this->getProperty('virtualserver_status', 'offline'); + } catch (Exception) { + // If fetching fails, assume it offline + return false; + } + } + + return $status === 'online'; } /** From f29998fb7355851b79905fc0dae92a06906d4cdf Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 21:00:07 +0200 Subject: [PATCH 25/62] update fetchNodeInfo() to more robust logical --- src/Node/Server.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Node/Server.php b/src/Node/Server.php index 048737ed..a2325f20 100644 --- a/src/Node/Server.php +++ b/src/Node/Server.php @@ -2806,7 +2806,25 @@ protected function fetchNodeList(): void */ protected function fetchNodeInfo(): void { - $this->nodeInfo = array_merge($this->nodeInfo, $this->request('serverinfo')->toList()); + $serverInfo = $this->request('serverinfo')->toList(); + $this->nodeInfo = array_merge($this->nodeInfo, $serverInfo); + + // Ensure virtualserver_status is set + if (!isset($this->nodeInfo['virtualserver_status'])) { + // Try to get the status from the parent's server list if available + try { + $serverList = $this->getParent()->serverList(); + if (isset($serverList[$this->getId()])) { + $serverData = $serverList[$this->getId()]->getInfo(false); + if (isset($serverData['virtualserver_status'])) { + $this->nodeInfo['virtualserver_status'] = $serverData['virtualserver_status']; + } + } + } catch (Exception) { + // If we can't get the status, assume it offline + $this->nodeInfo['virtualserver_status'] = 'offline'; + } + } } /** From bdf1e7df02679140fa0322532ea2c0a5c1a1bbba Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 21:00:28 +0200 Subject: [PATCH 26/62] fix clientCount() and add fallbacks --- src/Node/Server.php | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Node/Server.php b/src/Node/Server.php index a2325f20..2670ba48 100644 --- a/src/Node/Server.php +++ b/src/Node/Server.php @@ -818,7 +818,33 @@ public function clientCount(): int return 0; } - return $this['virtualserver_clientsonline'] - $this['virtualserver_queryclientsonline']; + // Try direct access first + if (isset($this->nodeInfo[1]['virtualserver_clientsonline']) && isset($this->nodeInfo[1]['virtualserver_queryclientsonline'])) { + $clientsOnline = (int) $this->nodeInfo[1]['virtualserver_clientsonline']; + $queryClientsOnline = (int) $this->nodeInfo[1]['virtualserver_queryclientsonline']; + $result = $clientsOnline - $queryClientsOnline; + + return max(0, $result); + } + + // Fallback: try to refresh node info + try { + $this->fetchNodeInfo(); + + if (isset($this->nodeInfo[1]['virtualserver_clientsonline']) && isset($this->nodeInfo[1]['virtualserver_queryclientsonline'])) { + $clientsOnline = (int) $this->nodeInfo[1]['virtualserver_clientsonline']; + $queryClientsOnline = (int) $this->nodeInfo[1]['virtualserver_queryclientsonline']; + $result = $clientsOnline - $queryClientsOnline; + + return max(0, $result); + } + } catch (Exception) { + // If fetching fails, return 0 + return 0; + } + + // Last fallback + return 0; } /** @@ -2810,7 +2836,7 @@ protected function fetchNodeInfo(): void $this->nodeInfo = array_merge($this->nodeInfo, $serverInfo); // Ensure virtualserver_status is set - if (!isset($this->nodeInfo['virtualserver_status'])) { + if (! isset($this->nodeInfo['virtualserver_status'])) { // Try to get the status from the parent's server list if available try { $serverList = $this->getParent()->serverList(); From 0b20ec621c247cb01cedb251c3fc678890430bd2 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 21:00:51 +0200 Subject: [PATCH 27/62] test_can_get_isOnline_isOffline --- tests/DevLiveServer/RefactorFunctionsTest.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/DevLiveServer/RefactorFunctionsTest.php b/tests/DevLiveServer/RefactorFunctionsTest.php index a2a09c52..4ead40a9 100644 --- a/tests/DevLiveServer/RefactorFunctionsTest.php +++ b/tests/DevLiveServer/RefactorFunctionsTest.php @@ -273,4 +273,29 @@ public function test_invalid_parameter_size(): void $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); } + + /** + * @throws \Exception + */ + public function test_can_get_isOnline_isOffline(): void + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + try { + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + } catch(TeamSpeak3Exception $e) { + echo $e->getMessage(); + } + + $statusOnline = $this->ts3_VirtualServer->isOnline(); + $statusOffline = $this->ts3_VirtualServer->isOffline(); + + $this->assertTrue($statusOnline); + $this->assertFalse($statusOffline); + + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } } From d46e9cb98c6d6845da9351fd889d9de3a54a0e86 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 21:01:03 +0200 Subject: [PATCH 28/62] add test_can_get_clientCount --- tests/DevLiveServer/ClientTest.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index 83883b06..fc116e14 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -252,7 +252,7 @@ public function test_can_get_clientInfoDB() $this->markTestSkipped('DevLiveServer ist not active'); } - $clientInfoDB= []; + $clientInfoDB = []; $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); $clientListDb = $ts3_VirtualServer->clientListDb(); @@ -294,6 +294,27 @@ public function test_can_clientFindDb_by_name_pattern() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws AdapterException + * @throws HelperException + * @throws ServerQueryException + */ + public function test_can_get_clientCount() + { + if ($this->user_test_active == 'false' || $this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + + $clientCount = $ts3_VirtualServer->clientCount(); + $this->assertIsInt($clientCount); + $this->assertGreaterThan(0, $clientCount); + + $this->asserttrue(true); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws ServerQueryException From 0dc65c8383b01e3936bc5a925e785d890a141ae6 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 21:06:28 +0200 Subject: [PATCH 29/62] add assert to test_can_send_client_text_message --- tests/DevLiveServer/ClientTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index fc116e14..3eeff809 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -166,7 +166,9 @@ public function test_can_send_client_text_message() $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); $userID = $ts3_VirtualServer->clientGetByName($this->ts3_unit_test_userName)->getId(); + $this->assertIsInt($userID); $userID = $ts3_VirtualServer->clientGetById($userID); + $this->assertIsObject($userID); $userID->message('Hello World'); $this->asserttrue(true); From d6043543d217c3528a6faaacbeba8b9d80823bbf Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 21:08:37 +0200 Subject: [PATCH 30/62] remove skipped tests, implement later --- tests/Adapter/ServerQuery/ReplyTest.php | 25 ------------------- tests/Transport/TransportTest.php | 33 ------------------------- 2 files changed, 58 deletions(-) delete mode 100644 tests/Transport/TransportTest.php diff --git a/tests/Adapter/ServerQuery/ReplyTest.php b/tests/Adapter/ServerQuery/ReplyTest.php index 3ec59125..345fd109 100644 --- a/tests/Adapter/ServerQuery/ReplyTest.php +++ b/tests/Adapter/ServerQuery/ReplyTest.php @@ -160,11 +160,6 @@ public function testToArray() $this->assertEquals(static::$E_CLIENTLIST_EXTENDED_SINGLE_CLIENT_BADGES, (string) $clientlist_array['client_badges']); } - public function testToAssocArray() - { - $this->markTestSkipped('todo: testToTable'); - } - /** * @throws AdapterException * @throws ServerQueryException @@ -194,24 +189,4 @@ public function testToList() $this->assertEquals('N', $reply->toList()[3]['client_nickname']); $this->assertEquals('Q', $reply->toList()[3]['client_unique_identifier']); } - - public function testToObjectArray() - { - $this->markTestSkipped('todo: testToObjectArray'); - } - - public function testGetCommandString() - { - $this->markTestSkipped('todo: testGetCommandString'); - } - - public function testGetNotifyEvents() - { - $this->markTestSkipped('todo: testGetNotifyEvents'); - } - - public function testFetchReply() - { - $this->markTestSkipped('todo: testFetchReply'); - } } diff --git a/tests/Transport/TransportTest.php b/tests/Transport/TransportTest.php deleted file mode 100644 index ad27c9f6..00000000 --- a/tests/Transport/TransportTest.php +++ /dev/null @@ -1,33 +0,0 @@ - '0.0.0.0', 'port' => 9987]); - } - - /** - * @throws AdapterException - */ - public function testGetAdapterTypeReturnValue() - { - $this->markTestSkipped('Mockserverquery is not working it will be fixed in the future'); - $mockServerQuery = $this->createMockServerQuery(); - - // The original value should be returned as it is - $this->assertEquals('MockServerQuery', $mockServerQuery->getTransport()->getAdapterType()); - - // The Signal class combines the lowered class name with an additional string for the `emit()` function - $this->assertEquals('mockserverquery', strtolower($mockServerQuery->getTransport()->getAdapterType())); - } -} From 313cdf78db7ab1adf7e733f3a95958d711be2dd8 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Mon, 20 Oct 2025 21:17:45 +0200 Subject: [PATCH 31/62] generate badge --- doc/coverage/coverage-badge.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/coverage/coverage-badge.svg b/doc/coverage/coverage-badge.svg index ff2419e7..783b4967 100644 --- a/doc/coverage/coverage-badge.svg +++ b/doc/coverage/coverage-badge.svg @@ -14,6 +14,6 @@ coverage - 47% + 48% \ No newline at end of file From c758d297a1e5def0d305009a7296a04c6d765012 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 16:46:03 +0200 Subject: [PATCH 32/62] add test_can_get_subChannelList --- tests/DevLiveServer/ChannelTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index 2f775c05..bac46755 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -634,6 +634,31 @@ public function test_can_get_level() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_get_subChannelList() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_channel($ts3_VirtualServer); + + $subChannelList = $ts3_VirtualServer->channelGetByName('UnitTest')->subChannelList(); + + foreach ($subChannelList as $subChannel) { + $this->assertEquals('Play-Test', $subChannel['channel_name']); + } + + $this->unset_play_test_channel($ts3_VirtualServer); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws TransportException From 330fdf1b01758aa6f9ae3cd72b9958167fa1fc49 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 16:59:14 +0200 Subject: [PATCH 33/62] fix the issue at subChannelGetById --- src/Node/Channel.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Node/Channel.php b/src/Node/Channel.php index 29034342..79b446ea 100644 --- a/src/Node/Channel.php +++ b/src/Node/Channel.php @@ -74,11 +74,13 @@ public function subChannelList(array $filter = []): array */ public function subChannelGetById(int $cid): self { - if (! array_key_exists($cid, $this->subChannelList())) { + $subChannels = $this->subChannelList(); + + if (! array_key_exists($cid, $subChannels)) { throw new ServerQueryException('invalid channelID', 0x300); } - return $this->channelList[$cid]; + return $subChannels[$cid]; } /** From d668927b595c1e8de81833b65a247ba359a7c4d5 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 16:59:52 +0200 Subject: [PATCH 34/62] add test_can_get_subChannelGetById to validate fix --- tests/DevLiveServer/ChannelTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index bac46755..f8bf1f40 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -659,6 +659,30 @@ public function test_can_get_subChannelList() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_get_subChannelGetById() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_channel($ts3_VirtualServer); + + $subChannel = $ts3_VirtualServer->channelGetByName('UnitTest')->subChannelGetById($this->test_cid); + + $this->assertEquals('Play-Test', $subChannel['channel_name']); + $this->assertIsInt($subChannel['cid']); + + $this->unset_play_test_channel($ts3_VirtualServer); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws TransportException From 9d8e16e386736a00612d8f3fd983f24301f77e9c Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 17:03:04 +0200 Subject: [PATCH 35/62] fix type at subChannelGetByName --- src/Node/Channel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Node/Channel.php b/src/Node/Channel.php index 79b446ea..54db12dd 100644 --- a/src/Node/Channel.php +++ b/src/Node/Channel.php @@ -86,13 +86,13 @@ public function subChannelGetById(int $cid): self /** * Returns the PlanetTeamSpeak\TeamSpeak3Framework\Node\Channel object matching the given name. * - * @param int $name + * @param string $name * @return Channel * @throws AdapterException * @throws ServerQueryException * @throws TransportException */ - public function subChannelGetByName(int $name): self + public function subChannelGetByName(string $name): self { foreach ($this->subChannelList() as $channel) { if ($channel['channel_name'] == $name) { From 4f8f490e56aea93210c7101d1abbf862098e75a4 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 17:04:06 +0200 Subject: [PATCH 36/62] add test_can_get_subChannelGetByName to validate a type issue --- tests/DevLiveServer/ChannelTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index f8bf1f40..005342cc 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -683,6 +683,30 @@ public function test_can_get_subChannelGetById() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + */ + public function test_can_get_subChannelGetByName() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_channel($ts3_VirtualServer); + + $subChannel = $ts3_VirtualServer->channelGetByName('UnitTest')->subChannelGetByName('Play-Test'); + + $this->assertEquals('Play-Test', $subChannel['channel_name']); + $this->assertIsInt($subChannel['cid']); + + $this->unset_play_test_channel($ts3_VirtualServer); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + } + /** * @throws AdapterException * @throws TransportException From f50b1302bbfe01e76685999d09e83f8dc85d687e Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 17:11:00 +0200 Subject: [PATCH 37/62] enhance test to test channel->clientList() --- tests/DevLiveServer/ClientTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index 3eeff809..dbe98b36 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -147,6 +147,12 @@ public function test_can_move_user() $userMoved = $ts3_VirtualServer->clientGetByName($this->ts3_unit_test_userName)->getInfo(); $this->assertEquals($userMoved['cid'], $testCid); + //get client by channel list + $clientChannelList = $ts3_VirtualServer->channelGetById($testCid)->clientList(); + foreach ($clientChannelList as $client) { + $this->assertEquals($this->ts3_unit_test_userName, $client['client_nickname']); + } + $this->unset_play_test_channel($ts3_VirtualServer); $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } From caffe223f2d46f2f2f8b3d26e561611530c542ec Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 17:15:29 +0200 Subject: [PATCH 38/62] fix channel->clientGetById --- src/Node/Channel.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Node/Channel.php b/src/Node/Channel.php index 54db12dd..b2248bc4 100644 --- a/src/Node/Channel.php +++ b/src/Node/Channel.php @@ -136,11 +136,13 @@ public function clientList(array $filter = []): array */ public function clientGetById(int $clid): Client { - if (! array_key_exists($clid, $this->clientList())) { + $clientList = $this->clientList(); + + if (! array_key_exists($clid, $clientList)) { throw new ServerQueryException('invalid clientID', 0x200); } - return $this->clientList[$clid]; + return $clientList[$clid]; } /** From d7fde770f0905ab01e4fb1e0339b59f1b66f64e9 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 17:15:44 +0200 Subject: [PATCH 39/62] enhance test to validate fix channel->clientGetByI --- tests/DevLiveServer/ClientTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index dbe98b36..e0ba27ef 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -153,6 +153,10 @@ public function test_can_move_user() $this->assertEquals($this->ts3_unit_test_userName, $client['client_nickname']); } + //get client by channel clientGetById + $clientChannelByID = $ts3_VirtualServer->channelGetById($testCid)->clientGetById($userID); + $this->assertEquals($this->ts3_unit_test_userName, $clientChannelByID['client_nickname']); + $this->unset_play_test_channel($ts3_VirtualServer); $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } From adaeea9a097550547cf22cda3aa4826f707324ee Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 17:18:41 +0200 Subject: [PATCH 40/62] fix type at clientGetByName --- src/Node/Channel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Node/Channel.php b/src/Node/Channel.php index b2248bc4..2a421130 100644 --- a/src/Node/Channel.php +++ b/src/Node/Channel.php @@ -148,13 +148,13 @@ public function clientGetById(int $clid): Client /** * Returns the PlanetTeamSpeak\TeamSpeak3Framework\Node\Client object matching the given name. * - * @param int $name + * @param string $name * @return Client * @throws AdapterException * @throws ServerQueryException * @throws TransportException */ - public function clientGetByName(int $name): Client + public function clientGetByName(string $name): Client { foreach ($this->clientList() as $client) { if ($client['client_nickname'] == $name) { From 2aabe2216d18b8ed1722eeee94e15b49937b07a5 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 17:19:07 +0200 Subject: [PATCH 41/62] enhance test to validate fix a type issue at clientGetByName --- tests/DevLiveServer/ClientTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index e0ba27ef..ebe13255 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -157,6 +157,10 @@ public function test_can_move_user() $clientChannelByID = $ts3_VirtualServer->channelGetById($testCid)->clientGetById($userID); $this->assertEquals($this->ts3_unit_test_userName, $clientChannelByID['client_nickname']); + //get client by channel clientGetByName + $clientChannelByName = $ts3_VirtualServer->channelGetById($testCid)->clientGetByName($this->ts3_unit_test_userName); + $this->assertEquals($this->ts3_unit_test_userName, $clientChannelByName['client_nickname']); + $this->unset_play_test_channel($ts3_VirtualServer); $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } From 8568e05f78fcaaee5e1ed519c01f6a50bae0dc79 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 18:05:13 +0200 Subject: [PATCH 42/62] enhance test channel->fileList() and channel->fileDelete() and channel->dirCreate() enhance test channel->fileList() and channel->fileDelete() --- tests/DevLiveServer/ChannelTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index 005342cc..a5e50574 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -602,6 +602,20 @@ public function test_can_handle_file_directory() $fileList = $ts3_VirtualServer->channelFileList($this->test_cid); $this->assertEmpty($fileList); + //enhance test to validate over channel.php + $ts3_VirtualServer->channelGetById($this->test_cid)->dirCreate('', '/test_dir'); + $fileListGetByChannelID = $ts3_VirtualServer->channelGetById($this->test_cid)->fileList(); + + foreach ($fileListGetByChannelID as $fileGetByChannelID) { + $this->assertEquals('test_dir', $fileGetByChannelID['name']); + $this->assertIsString($fileGetByChannelID['name']); + } + + $ts3_VirtualServer->channelGetById($this->test_cid)->fileDelete('', '/test_dir'); + $fileList = $ts3_VirtualServer->channelFileList($this->test_cid); + $this->assertEmpty($fileList); + + $this->unset_play_test_channel($ts3_VirtualServer); $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } From 8c9d283819c39a8f9132ac4eee6b650aa6633e48 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 18:12:33 +0200 Subject: [PATCH 43/62] enhance test server->channelFileRename() --- tests/DevLiveServer/ChannelTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index a5e50574..a3a813ca 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -598,7 +598,8 @@ public function test_can_handle_file_directory() $this->assertIsString($file['name']); } - $ts3_VirtualServer->channelFileDelete($this->test_cid, '', '/test_dir'); + $ts3_VirtualServer->channelFileRename($this->test_cid, '', '/test_dir', '/test_dir_renamed'); + $ts3_VirtualServer->channelFileDelete($this->test_cid, '', '/test_dir_renamed'); $fileList = $ts3_VirtualServer->channelFileList($this->test_cid); $this->assertEmpty($fileList); From 6fdc110aadb78bf000a57ac39921b9a58bafa883 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 18:12:55 +0200 Subject: [PATCH 44/62] enhance test channel->fileRename() --- tests/DevLiveServer/ChannelTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index a3a813ca..9e33857b 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -606,17 +606,17 @@ public function test_can_handle_file_directory() //enhance test to validate over channel.php $ts3_VirtualServer->channelGetById($this->test_cid)->dirCreate('', '/test_dir'); $fileListGetByChannelID = $ts3_VirtualServer->channelGetById($this->test_cid)->fileList(); + $ts3_VirtualServer->channelGetById($this->test_cid)->fileRename('', '/test_dir', '/test_dir_renamed'); foreach ($fileListGetByChannelID as $fileGetByChannelID) { $this->assertEquals('test_dir', $fileGetByChannelID['name']); $this->assertIsString($fileGetByChannelID['name']); } - $ts3_VirtualServer->channelGetById($this->test_cid)->fileDelete('', '/test_dir'); - $fileList = $ts3_VirtualServer->channelFileList($this->test_cid); + $ts3_VirtualServer->channelGetById($this->test_cid)->fileDelete('', '/test_dir_renamed'); + $fileList = $ts3_VirtualServer->channelGetById($this->test_cid)->fileList(); $this->assertEmpty($fileList); - $this->unset_play_test_channel($ts3_VirtualServer); $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } From 362dffab4601fbc121e76a596ff6d514f5b65b99 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 18:17:03 +0200 Subject: [PATCH 45/62] enhance test channel->getLevel() --- tests/DevLiveServer/ChannelTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/DevLiveServer/ChannelTest.php b/tests/DevLiveServer/ChannelTest.php index 9e33857b..bdaa89e0 100644 --- a/tests/DevLiveServer/ChannelTest.php +++ b/tests/DevLiveServer/ChannelTest.php @@ -645,6 +645,10 @@ public function test_can_get_level() $getLevelTwo = $ts3_VirtualServer->channelGetLevel($cidChannelLevelTwo); $this->assertEquals(2, $getLevelTwo); + //enhance test to validate over channel.php + $getLevelOverChannel = $ts3_VirtualServer->channelGetByName('Standard Channel Level 2')->getLevel(); + $this->assertEquals(2, $getLevelOverChannel); + $this->unset_play_test_channel($ts3_VirtualServer); $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } From 842101bb33dae9406f20b3b0731bc7bda0f09287 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 19:24:55 +0200 Subject: [PATCH 46/62] remove try-catch. Factory should be okay everytime --- tests/DevLiveServer/RefactorFunctionsTest.php | 49 +++---------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/tests/DevLiveServer/RefactorFunctionsTest.php b/tests/DevLiveServer/RefactorFunctionsTest.php index 4ead40a9..724bbd42 100644 --- a/tests/DevLiveServer/RefactorFunctionsTest.php +++ b/tests/DevLiveServer/RefactorFunctionsTest.php @@ -7,7 +7,6 @@ use PlanetTeamSpeak\TeamSpeak3Framework\Exception\AdapterException; use PlanetTeamSpeak\TeamSpeak3Framework\Exception\NodeException; use PlanetTeamSpeak\TeamSpeak3Framework\Exception\ServerQueryException; -use PlanetTeamSpeak\TeamSpeak3Framework\Exception\TeamSpeak3Exception; use PlanetTeamSpeak\TeamSpeak3Framework\Exception\TransportException; use PlanetTeamSpeak\TeamSpeak3Framework\Node\Host; use PlanetTeamSpeak\TeamSpeak3Framework\Node\Node; @@ -69,13 +68,7 @@ public function test_clientupdate_getErrorProperty() $this->markTestSkipped('DevLiveServer ist not active'); } - try { - // Connect to the specified server, authenticate and spawn an object for the virtual server - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - //catch exception - echo $e->getMessage(); - } + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); $clientUpdate = $this->ts3_VirtualServer->getAdapter()->request('clientupdate'); $clientUpdateResult = $clientUpdate->getErrorProperty('msg')->toString(); @@ -99,13 +92,7 @@ public function test_serverGroupList_serverGroupClientList() $this->markTestSkipped('DevLiveServer ist not active'); } - try { - // Connect to the specified server, authenticate and spawn an object for the virtual server - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - //catch exception - echo $e->getMessage(); - } + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); // Resetting lists $this->ts3_VirtualServer->clientListReset(); @@ -142,13 +129,7 @@ public function test_channelGroupList_channelGroupClientList() $this->markTestSkipped('DevLiveServer ist not active'); } - try { - // Connect to the specified server, authenticate and spawn an object for the virtual server - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - //catch exception - echo $e->getMessage(); - } + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); // Resetting lists $this->ts3_VirtualServer->clientListReset(); @@ -179,11 +160,7 @@ public function test_can_create_server_group(): void $this->markTestSkipped('DevLiveServer ist not active'); } - try { - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - echo $e->getMessage(); - } + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); $sid = $this->ts3_VirtualServer->serverGroupCreate('UniTest', 1); $this->ts3_VirtualServer->serverGroupDelete($sid); @@ -204,11 +181,7 @@ public function test_can_create_channel_group(): void $this->markTestSkipped('DevLiveServer ist not active'); } - try { - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - echo $e->getMessage(); - } + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); $cgid = $this->ts3_VirtualServer->channelGroupCreate('UniTest', 1); $this->ts3_VirtualServer->channelGroupDelete($cgid); @@ -229,11 +202,7 @@ public function test_invalid_parameter_size(): void $this->markTestSkipped('DevLiveServer ist not active'); } - try { - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - echo $e->getMessage(); - } + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); //#channel name //30 chars @@ -283,11 +252,7 @@ public function test_can_get_isOnline_isOffline(): void $this->markTestSkipped('DevLiveServer ist not active'); } - try { - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - } catch(TeamSpeak3Exception $e) { - echo $e->getMessage(); - } + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); $statusOnline = $this->ts3_VirtualServer->isOnline(); $statusOffline = $this->ts3_VirtualServer->isOffline(); From 2acc2d08318d7c02e478663f4e920bf60403207c Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Tue, 21 Oct 2025 20:04:52 +0200 Subject: [PATCH 47/62] fix serverGroupCopy and add test class to validate serverGroups --- doc/coverage/coverage-badge.svg | 2 +- src/Node/Server.php | 24 +++- tests/DevLiveServer/ServerGroupTest.php | 154 ++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 tests/DevLiveServer/ServerGroupTest.php diff --git a/doc/coverage/coverage-badge.svg b/doc/coverage/coverage-badge.svg index 783b4967..027e25c4 100644 --- a/doc/coverage/coverage-badge.svg +++ b/doc/coverage/coverage-badge.svg @@ -14,6 +14,6 @@ coverage - 48% + 49% \ No newline at end of file diff --git a/src/Node/Server.php b/src/Node/Server.php index 2670ba48..797d8d93 100644 --- a/src/Node/Server.php +++ b/src/Node/Server.php @@ -1269,14 +1269,32 @@ public function serverGroupCopy(int $ssgid, string $name = null, int $tsgid = 0, { $this->serverGroupListReset(); - $sgid = $this->execute('servergroupcopy', ['ssgid' => $ssgid, 'tsgid' => $tsgid, 'name' => $name, 'type' => $type]) - ->toList(); + $result = $this->execute('servergroupcopy', ['ssgid' => $ssgid, 'tsgid' => $tsgid, 'name' => $name, 'type' => $type])->toList(); if ($tsgid && $name) { $this->serverGroupRename($tsgid, $name); } - return count($sgid) ? $sgid['sgid'] : $tsgid; + // Search for the new sgid in all elements of the result array + $sgid = null; + + for ($i = count($result) - 1; $i >= 0; $i--) { + foreach ($result[$i] as $key => $value) { + if (stripos($key, 'sgid') !== false) { + // Extrahiere nur die führenden Ziffern + if (preg_match('/\d+/', $value, $matches)) { + $sgid = (int) $matches[0]; + break 2; + } + } + } + } + + if ($sgid === null) { + throw new \RuntimeException('serverGroupCopy: Could not determine a valid server group ID.'); + } + + return $sgid; } /** diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php new file mode 100644 index 00000000..a878dbfd --- /dev/null +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -0,0 +1,154 @@ +active = str_replace('DEV_LIVE_SERVER_AVAILABLE=', '', preg_replace('#\n(?!\n)#', '', $env[2])); + $this->host = str_replace('DEV_LIVE_SERVER_HOST=', '', preg_replace('#\n(?!\n)#', '', $env[3])); + $this->queryPort = str_replace('DEV_LIVE_SERVER_QUERY_PORT=', '', preg_replace('#\n(?!\n)#', '', $env[4])); + $this->user = str_replace('DEV_LIVE_SERVER_QUERY_USER=', '', preg_replace('#\n(?!\n)#', '', $env[5])); + $this->password = str_replace('DEV_LIVE_SERVER_QUERY_USER_PASSWORD=', '', preg_replace('#\n(?!\n)#', '', $env[6])); + } else { + $this->active = 'false'; + } + + $this->ts3_server_uri = 'serverquery://'.$this->user.':'.$this->password.'@'.$this->host.':'.$this->queryPort. + '/?server_port=9987'. + '&no_query_clients=0'. + '&timeout=30'; + } + + /** + * @throws AdapterException + * @throws TransportException + * @throws ServerQueryException + * @throws \Exception + */ + public function test_can_rename_servergroup() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_servergroup($this->ts3_VirtualServer); + + $this->ts3_VirtualServer->servergrouprename($this->sgid, 'UnitTest-Renamed'); + $renamedServerGroup = $this->ts3_VirtualServer->serverGroupGetById($this->sgid); + $this->assertEquals('UnitTest-Renamed', $renamedServerGroup['name']); + + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws NodeException + * @throws HelperException + */ + public function test_can_copy_servergroup() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_servergroup($this->ts3_VirtualServer); + + $duplicatedSGID = $this->ts3_VirtualServer->serverGroupCopy($this->sgid, 'UnitTest-Copy'); + $getDuplicatedServerGroup = $this->ts3_VirtualServer->serverGroupGetById($duplicatedSGID); + $this->assertEquals('UnitTest-Copy', $getDuplicatedServerGroup['name']); + + $this->ts3_VirtualServer->serverGroupDelete($duplicatedSGID); + try { + $this->ts3_VirtualServer->serverGroupGetById($duplicatedSGID); + $this->fail('ServerGroup should not exist'); + } catch (ServerQueryException $e) { + $this->assertEquals('invalid groupID', $e->getMessage()); + } + + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + + /** + * @throws AdapterException + * @throws ServerQueryException + * @throws TransportException + */ + private function set_play_test_servergroup(Server $ts3VirtualServer): void + { + $this->sgid = $ts3VirtualServer->serverGroupCreate('UnitTest', 1); + } + + /** + * @throws AdapterException + * @throws ServerQueryException + * @throws HelperException + */ + public function unset_play_test_servergroup($ts3_VirtualServer): void + { + $ts3_VirtualServer->serverGroupDelete($this->sgid); + } + + /** + * @throws AdapterException + * @throws NodeException + * @throws TransportException + * @throws ServerQueryException + */ + public function dev_rest_servergroup(): void + { + $servergrouplist = $this->ts3_VirtualServer->serverGroupList(['type' => 1]); + foreach ($servergrouplist as $servergroup) { + if ($servergroup['name'] != 'Server Admin' && $servergroup['name'] != 'Guest') { + $this->ts3_VirtualServer->serverGroupDelete($servergroup['sgid']); + } + } + } +} From 6f22ae258f5272c37b53de67002b8a513aacbb79 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 18:33:32 +0200 Subject: [PATCH 48/62] add test_can_get_servergroup_by_name --- tests/DevLiveServer/ServerGroupTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index a878dbfd..788be909 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -59,6 +59,31 @@ public function setUp(): void '&timeout=30'; } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws NodeException + * @throws HelperException + */ + public function test_can_get_servergroup_by_name() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_servergroup($this->ts3_VirtualServer); + + $serverGroupName = $this->ts3_VirtualServer->serverGroupGetByName('UnitTest'); + $this->assertEquals('UnitTest', $serverGroupName['name']); + $this->assertIsString($serverGroupName['name']); + + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + /** * @throws AdapterException * @throws TransportException From 7799136b6c4a65e92897c2b5b87f4acd23d438cb Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 19:01:56 +0200 Subject: [PATCH 49/62] fix more cover precision --- generate-coverage-badge.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generate-coverage-badge.php b/generate-coverage-badge.php index 86fcd48a..fc54f029 100644 --- a/generate-coverage-badge.php +++ b/generate-coverage-badge.php @@ -16,7 +16,8 @@ $elements = (int) $metrics['elements']; $covered = (int) $metrics['coveredelements']; -$coverage = $elements > 0 ? round($covered / $elements * 100) : 0; +$preciseCoverage = $elements > 0 ? ($covered / $elements * 100) : 0; +$coverage = round($preciseCoverage); if ($coverage >= 90) { $color = '#4c1'; From a1a50ff7193a407b3a3bead17e25fce116a87786 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 19:28:57 +0200 Subject: [PATCH 50/62] enhance test_can_copy_servergroup to validate via ServerGroup class --- tests/DevLiveServer/ServerGroupTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index 788be909..54c8d273 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -136,6 +136,23 @@ public function test_can_copy_servergroup() $this->assertEquals('invalid groupID', $e->getMessage()); } + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + + //test by ServerGroup Class + $this->set_play_test_servergroup($this->ts3_VirtualServer); + + $duplicatedSGIDChain = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->copy( 'UnitTest-Copy'); + $getDuplicatedServerGroupChain = $this->ts3_VirtualServer->serverGroupGetById($duplicatedSGIDChain); + $this->assertEquals('UnitTest-Copy', $getDuplicatedServerGroupChain['name']); + + $this->ts3_VirtualServer->serverGroupDelete($getDuplicatedServerGroupChain->getId()); + try { + $this->ts3_VirtualServer->serverGroupGetById($getDuplicatedServerGroupChain->getId()); + $this->fail('ServerGroup should not exist'); + } catch (ServerQueryException $e) { + $this->assertEquals('invalid groupID', $e->getMessage()); + } + $this->unset_play_test_servergroup($this->ts3_VirtualServer); $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); From 4e91e1c17f8be20620cac7afb86a0750cd5014fc Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 19:42:28 +0200 Subject: [PATCH 51/62] fix a type issue at ServerGroup->permAssing() --- src/Node/ServerGroup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Node/ServerGroup.php b/src/Node/ServerGroup.php index 90a85bf8..c6d77a70 100644 --- a/src/Node/ServerGroup.php +++ b/src/Node/ServerGroup.php @@ -97,7 +97,7 @@ public function permList(bool $permsid = false): array * Adds a set of specified permissions to the server group. Multiple permissions * can be added by providing the four parameters of each permission in separate arrays. * - * @param int $permid + * @param int|array $permid * @param int $permvalue * @param int $permnegated * @param int $permskip @@ -106,7 +106,7 @@ public function permList(bool $permsid = false): array * @throws ServerQueryException * @throws TransportException */ - public function permAssign(int $permid, int $permvalue, int $permnegated = 0, int $permskip = 0): void + public function permAssign(int|array $permid, int $permvalue, int $permnegated = 0, int $permskip = 0): void { $this->getParent()->serverGroupPermAssign($this->getId(), $permid, $permvalue, $permnegated, $permskip); } From 05b8d5e61cbfb139249f085cf4e5c95b3cb73024 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 19:43:48 +0200 Subject: [PATCH 52/62] add test to validate type issue at ServerGroup->permAssing() --- tests/DevLiveServer/ServerGroupTest.php | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index 54c8d273..61f3c16b 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -158,6 +158,35 @@ public function test_can_copy_servergroup() $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws NodeException + * @throws HelperException + */ + public function test_can_assign_permissions_to_servergroup() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_servergroup($this->ts3_VirtualServer); + + $this->ts3_VirtualServer->serverGroupPermAssign($this->sgid, ['i_client_private_textmessage_power'], [75],[0],[0]); + $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permAssign(['i_client_talk_power'], 75,0,0); + + $permList = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permList(true); + $this->assertEquals(75, $permList['i_client_talk_power']['permvalue']); + $this->assertEquals(75, $permList['i_client_private_textmessage_power']['permvalue']); + + + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + /** * @throws AdapterException * @throws ServerQueryException From ee99a1f9b9f7ba31d816acff07901881be8ab44f Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 19:48:34 +0200 Subject: [PATCH 53/62] rename and enhance test_can_assign_remove_permissions_to_servergroup --- tests/DevLiveServer/ServerGroupTest.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index 61f3c16b..a2755144 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -165,7 +165,7 @@ public function test_can_copy_servergroup() * @throws NodeException * @throws HelperException */ - public function test_can_assign_permissions_to_servergroup() + public function test_can_assign_remove_permissions_to_servergroup() { if ($this->active == 'false') { $this->markTestSkipped('DevLiveServer ist not active'); @@ -175,12 +175,19 @@ public function test_can_assign_permissions_to_servergroup() $this->set_play_test_servergroup($this->ts3_VirtualServer); $this->ts3_VirtualServer->serverGroupPermAssign($this->sgid, ['i_client_private_textmessage_power'], [75],[0],[0]); - $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permAssign(['i_client_talk_power'], 75,0,0); + $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permAssign(['i_client_talk_power'], 75); $permList = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permList(true); $this->assertEquals(75, $permList['i_client_talk_power']['permvalue']); $this->assertEquals(75, $permList['i_client_private_textmessage_power']['permvalue']); + $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permRemove(['i_client_private_textmessage_power']); + $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permRemove(['i_client_talk_power']); + + $permListKeyRemoved = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permList(true); + + $this->assertArrayNotHasKey('i_client_talk_power',$permListKeyRemoved); + $this->assertArrayNotHasKey('i_client_private_textmessage_power',$permListKeyRemoved); $this->unset_play_test_servergroup($this->ts3_VirtualServer); $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); From 9774846574fee51a68c6e25710d87ec940f6692b Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 19:50:35 +0200 Subject: [PATCH 54/62] fix a type issue at ServerGroup->permRemove() --- src/Node/ServerGroup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Node/ServerGroup.php b/src/Node/ServerGroup.php index c6d77a70..d3cc4f0f 100644 --- a/src/Node/ServerGroup.php +++ b/src/Node/ServerGroup.php @@ -115,13 +115,13 @@ public function permAssign(int|array $permid, int $permvalue, int $permnegated = * Removes a set of specified permissions from the server group. Multiple * permissions can be removed at once. * - * @param int $permid + * @param int|array $permid * @return void * @throws AdapterException * @throws ServerQueryException * @throws TransportException */ - public function permRemove(int $permid): void + public function permRemove(int|array $permid): void { $this->getParent()->serverGroupPermRemove($this->getId(), $permid); } From 82fb35e374c883e373de0a465824a821152cbfbb Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 19:51:18 +0200 Subject: [PATCH 55/62] enhance test_can_rename_servergroup to validate ServerGroup->rename --- tests/DevLiveServer/ServerGroupTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index a2755144..1a4c2ee5 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -103,6 +103,14 @@ public function test_can_rename_servergroup() $renamedServerGroup = $this->ts3_VirtualServer->serverGroupGetById($this->sgid); $this->assertEquals('UnitTest-Renamed', $renamedServerGroup['name']); + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + + //test by ServerGroup Class + $this->set_play_test_servergroup($this->ts3_VirtualServer); + $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->rename('UnitTest-Renamed'); + $renamedByChain = $this->ts3_VirtualServer->serverGroupGetById($this->sgid); + $this->assertEquals('UnitTest-Renamed', $renamedByChain['name']); + $this->unset_play_test_servergroup($this->ts3_VirtualServer); $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); From cb4385663ae154bae23e7775ba0378fb40ba90a0 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 20:29:01 +0200 Subject: [PATCH 56/62] add test_can_add_list_del_client_to_servergroup --- tests/DevLiveServer/ServerGroupTest.php | 53 ++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index 1a4c2ee5..e0726c9a 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -36,6 +36,10 @@ class ServerGroupTest extends TestCase private int $sgid; + private string $user_test_active; + + private string $ts3_unit_test_userName; + private Server|Adapter|Node|Host $ts3_VirtualServer; public function setUp(): void @@ -49,6 +53,8 @@ public function setUp(): void $this->queryPort = str_replace('DEV_LIVE_SERVER_QUERY_PORT=', '', preg_replace('#\n(?!\n)#', '', $env[4])); $this->user = str_replace('DEV_LIVE_SERVER_QUERY_USER=', '', preg_replace('#\n(?!\n)#', '', $env[5])); $this->password = str_replace('DEV_LIVE_SERVER_QUERY_USER_PASSWORD=', '', preg_replace('#\n(?!\n)#', '', $env[6])); + $this->user_test_active = str_replace('DEV_LIVE_SERVER_UNIT_TEST_USER_ACTIVE=', '', preg_replace('#\n(?!\n)#', '', $env[8])); + $this->ts3_unit_test_userName = str_replace('DEV_LIVE_SERVER_UNIT_TEST_USER=', '', preg_replace('#\n(?!\n)#', '', $env[9])); } else { $this->active = 'false'; } @@ -149,7 +155,7 @@ public function test_can_copy_servergroup() //test by ServerGroup Class $this->set_play_test_servergroup($this->ts3_VirtualServer); - $duplicatedSGIDChain = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->copy( 'UnitTest-Copy'); + $duplicatedSGIDChain = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->copy('UnitTest-Copy'); $getDuplicatedServerGroupChain = $this->ts3_VirtualServer->serverGroupGetById($duplicatedSGIDChain); $this->assertEquals('UnitTest-Copy', $getDuplicatedServerGroupChain['name']); @@ -182,7 +188,7 @@ public function test_can_assign_remove_permissions_to_servergroup() $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); $this->set_play_test_servergroup($this->ts3_VirtualServer); - $this->ts3_VirtualServer->serverGroupPermAssign($this->sgid, ['i_client_private_textmessage_power'], [75],[0],[0]); + $this->ts3_VirtualServer->serverGroupPermAssign($this->sgid, ['i_client_private_textmessage_power'], [75], [0], [0]); $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permAssign(['i_client_talk_power'], 75); $permList = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permList(true); @@ -194,9 +200,44 @@ public function test_can_assign_remove_permissions_to_servergroup() $permListKeyRemoved = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->permList(true); - $this->assertArrayNotHasKey('i_client_talk_power',$permListKeyRemoved); - $this->assertArrayNotHasKey('i_client_private_textmessage_power',$permListKeyRemoved); + $this->assertArrayNotHasKey('i_client_talk_power', $permListKeyRemoved); + $this->assertArrayNotHasKey('i_client_private_textmessage_power', $permListKeyRemoved); + + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + * @throws NodeException + */ + public function test_can_add_list_del_client_to_servergroup() + { + if ($this->active == 'false' || $this->user_test_active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_servergroup($this->ts3_VirtualServer); + + $user = $this->ts3_VirtualServer->clientGetByName($this->ts3_unit_test_userName); + $clidDB = $this->ts3_VirtualServer->clientGetByUid($user['client_unique_identifier']); + + $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->clientAdd($clidDB['client_database_id']); + $clientList = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->clientList(); + + foreach ($clientList as $client) { + $this->assertEquals($this->ts3_unit_test_userName, $client['client_nickname']); + } + + $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->clientDel($clidDB['client_database_id']); + //remember at this point the test will fail if the user is still in the servergroup + // unset will not force delete the user from the servergroup $this->unset_play_test_servergroup($this->ts3_VirtualServer); $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); @@ -228,12 +269,12 @@ public function unset_play_test_servergroup($ts3_VirtualServer): void * @throws TransportException * @throws ServerQueryException */ - public function dev_rest_servergroup(): void + public function dev_reset_servergroup(): void { $servergrouplist = $this->ts3_VirtualServer->serverGroupList(['type' => 1]); foreach ($servergrouplist as $servergroup) { if ($servergroup['name'] != 'Server Admin' && $servergroup['name'] != 'Guest') { - $this->ts3_VirtualServer->serverGroupDelete($servergroup['sgid']); + $this->ts3_VirtualServer->serverGroupDelete($servergroup['sgid'], true); } } } From 9c28ef0d9f6018a85ff7a94be190240a60d971b6 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 20:41:52 +0200 Subject: [PATCH 57/62] noms nom ordering tests are important --- doc/coverage/coverage-badge.svg | 4 +-- tests/DevLiveServer/ClientTest.php | 35 +++++++++++++++++++++ tests/DevLiveServer/ServerGroupTest.php | 41 ------------------------- 3 files changed, 37 insertions(+), 43 deletions(-) diff --git a/doc/coverage/coverage-badge.svg b/doc/coverage/coverage-badge.svg index 027e25c4..66360ebe 100644 --- a/doc/coverage/coverage-badge.svg +++ b/doc/coverage/coverage-badge.svg @@ -8,12 +8,12 @@ - + coverage - 49% + 50% \ No newline at end of file diff --git a/tests/DevLiveServer/ClientTest.php b/tests/DevLiveServer/ClientTest.php index ebe13255..cd963e85 100644 --- a/tests/DevLiveServer/ClientTest.php +++ b/tests/DevLiveServer/ClientTest.php @@ -331,6 +331,41 @@ public function test_can_get_clientCount() $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); } + /** + * @throws TransportException + * @throws ServerQueryException + * @throws AdapterException + * @throws HelperException + * @throws NodeException + */ + public function test_can_add_list_del_client_to_servergroup() + { + if ($this->active == 'false' || $this->user_test_active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $sgid = $ts3_VirtualServer->serverGroupCreate('UnitTest', 1); + + $user = $ts3_VirtualServer->clientGetByName($this->ts3_unit_test_userName); + $clidDB = $ts3_VirtualServer->clientGetByUid($user['client_unique_identifier']); + + $ts3_VirtualServer->serverGroupGetById($sgid)->clientAdd($clidDB['client_database_id']); + $clientList = $ts3_VirtualServer->serverGroupGetById($sgid)->clientList(); + + foreach ($clientList as $client) { + $this->assertEquals($this->ts3_unit_test_userName, $client['client_nickname']); + } + + $ts3_VirtualServer->serverGroupGetById($sgid)->clientDel($clidDB['client_database_id']); + + //remember at this point the test will fail if the user is still in the servergroup + // unset will not force delete the user from the servergroup + $ts3_VirtualServer->serverGroupDelete($sgid); + $ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + /** * @throws AdapterException * @throws ServerQueryException diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index e0726c9a..73538510 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -36,10 +36,6 @@ class ServerGroupTest extends TestCase private int $sgid; - private string $user_test_active; - - private string $ts3_unit_test_userName; - private Server|Adapter|Node|Host $ts3_VirtualServer; public function setUp(): void @@ -53,8 +49,6 @@ public function setUp(): void $this->queryPort = str_replace('DEV_LIVE_SERVER_QUERY_PORT=', '', preg_replace('#\n(?!\n)#', '', $env[4])); $this->user = str_replace('DEV_LIVE_SERVER_QUERY_USER=', '', preg_replace('#\n(?!\n)#', '', $env[5])); $this->password = str_replace('DEV_LIVE_SERVER_QUERY_USER_PASSWORD=', '', preg_replace('#\n(?!\n)#', '', $env[6])); - $this->user_test_active = str_replace('DEV_LIVE_SERVER_UNIT_TEST_USER_ACTIVE=', '', preg_replace('#\n(?!\n)#', '', $env[8])); - $this->ts3_unit_test_userName = str_replace('DEV_LIVE_SERVER_UNIT_TEST_USER=', '', preg_replace('#\n(?!\n)#', '', $env[9])); } else { $this->active = 'false'; } @@ -208,41 +202,6 @@ public function test_can_assign_remove_permissions_to_servergroup() $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); } - /** - * @throws TransportException - * @throws ServerQueryException - * @throws AdapterException - * @throws HelperException - * @throws NodeException - */ - public function test_can_add_list_del_client_to_servergroup() - { - if ($this->active == 'false' || $this->user_test_active == 'false') { - $this->markTestSkipped('DevLiveServer ist not active'); - } - - $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); - $this->set_play_test_servergroup($this->ts3_VirtualServer); - - $user = $this->ts3_VirtualServer->clientGetByName($this->ts3_unit_test_userName); - $clidDB = $this->ts3_VirtualServer->clientGetByUid($user['client_unique_identifier']); - - $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->clientAdd($clidDB['client_database_id']); - $clientList = $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->clientList(); - - foreach ($clientList as $client) { - $this->assertEquals($this->ts3_unit_test_userName, $client['client_nickname']); - } - - $this->ts3_VirtualServer->serverGroupGetById($this->sgid)->clientDel($clidDB['client_database_id']); - - //remember at this point the test will fail if the user is still in the servergroup - // unset will not force delete the user from the servergroup - $this->unset_play_test_servergroup($this->ts3_VirtualServer); - $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); - $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); - } - /** * @throws AdapterException * @throws ServerQueryException From 44bb780c098ffe36c343901cad4dd6483b1477b1 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 20:54:29 +0200 Subject: [PATCH 58/62] add info grant level (important). The serveradmin sets grant 100 automatically. That is the big boss, they should do it --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6a9d6f5e..66fc54a1 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ Before you start UnitTests, make sure that you have set the environment variable The best way to test all functionalities is to use the serveradmin query user.
The serveradmin is != Server Admin there you can find in your Teamspeak Client UI.
-| serveradmin (Query) | Server Admin (GUI) | -|----------------------------|---------------------------| -| Max. permission value: 100 | Max. permission value: 75 | +| serveradmin (Query) | Server Admin (GUI) | +|-------------------------------------|------------------------------------| +| Max. permission value: 100 (=grant) | Max. permission value: 75 (=grant) | You can find more information in the Documentation [testing-live-server](doc/testing-live-server.md) From b83a0d0937b55061741c9b0e45d9d3ea6c4d32f1 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Wed, 22 Oct 2025 20:57:02 +0200 Subject: [PATCH 59/62] README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 66fc54a1..9f695442 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ![Coverage](doc/coverage/coverage-badge.svg) > [!CAUTION] > **_IMPORTANT CHANGE_**
-> With Version 3.0.0 we refactored to integrate phpseclib3. This changes affects how TCP connections are established. -> The "raw" mode was removed, and the support for only ssh mode was established to handel Teamspeak 3 and Teamspeak 6 Server connections. +> With Version 3.x we refactored to integrate phpseclib3. This changes affects how TCP connections are established. +> The "raw" mode was removed, and the support for only ssh mode was established to handel Teamspeak 3 and Teamspeak 6 Server API connections. The X stands for a non-specific Teamspeak Server Version. So we would handle all current and future Versions from a Teamspeak Server. @@ -17,7 +17,7 @@ The ideal is that this version can be integrated into your own project and the m --- # Installation -With the Refactoring at Version 3.0.0, the Framework has a lot of changes. But most functionalities and namespaces are the same. +With the Refactoring at Version 3.x, the Framework has a lot of changes. But most functionalities and namespaces are the same. **PHP Required Extensions**
``apt install php8.3 php8.3-{common,mbstring,ssh2} -y`` From e379a7daba62878781c85c26e24db2d3e4402316 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Fri, 24 Oct 2025 22:36:44 +0200 Subject: [PATCH 60/62] add new function iconList() --- src/Node/Node.php | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/Node/Node.php b/src/Node/Node.php index edf16210..49288d7a 100644 --- a/src/Node/Node.php +++ b/src/Node/Node.php @@ -148,6 +148,49 @@ public function iconGetName(string $key): StringHelper return new StringHelper('/icon_'.$iconid); } + public function iconList(): array + { + $iconBasePath = '/icons'; + + try { + $files = $this->execute('ftgetfilelist', [ + 'cid' => 0, + 'cpw' => '', + 'path' => $iconBasePath, + ])->toArray(); + } catch (\Exception $e) { + return []; + } + + $result = []; + + foreach ($files as $file) { + // Skip meta entry (“ftgetfilelist” exists) + if (isset($file['ftgetfilelist']) || ! isset($file['name'])) { + continue; + } + + // Set a fallback path + $file['path'] = $file['path'] ?? $iconBasePath; + $file['cid'] = 0; + $file['sid'] = $this->getId(); + + // Create StringHelper + $src = new StringHelper('/'.ltrim($file['path'], '/')); + if (! $src->endsWith('/')) { + $src->append('/'); + } + if (! empty($file['name'])) { + $src->append($file['name']); + } + $file['src'] = $src; + + $result[] = $file; + } + + return $result; + } + /** * Returns a possible classname for the node which can be used as an HTML property. * From 329f8c72b109c4c796a94447e2769e5912f925fa Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Fri, 24 Oct 2025 22:39:37 +0200 Subject: [PATCH 61/62] add test_can_get_iconList to validate new function iconlist() --- tests/DevLiveServer/ServerGroupTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/DevLiveServer/ServerGroupTest.php b/tests/DevLiveServer/ServerGroupTest.php index 73538510..5c8f3e2b 100644 --- a/tests/DevLiveServer/ServerGroupTest.php +++ b/tests/DevLiveServer/ServerGroupTest.php @@ -202,6 +202,30 @@ public function test_can_assign_remove_permissions_to_servergroup() $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); } + /** + * @throws AdapterException + * @throws HelperException + * @throws ServerQueryException + * @throws TransportException + */ + public function test_can_get_iconList() + { + if ($this->active == 'false') { + $this->markTestSkipped('DevLiveServer ist not active'); + } + + $this->ts3_VirtualServer = TeamSpeak3::factory($this->ts3_server_uri); + $this->set_play_test_servergroup($this->ts3_VirtualServer); + + //memo: an array can be empty if no icons uploaded + $iconList = $this->ts3_VirtualServer->iconList(); + $this->assertIsarray($iconList); + + $this->unset_play_test_servergroup($this->ts3_VirtualServer); + $this->ts3_VirtualServer->getAdapter()->getTransport()->disconnect(); + $this->assertFalse($this->ts3_VirtualServer->getAdapter()->getTransport()->isConnected()); + } + /** * @throws AdapterException * @throws ServerQueryException From 582799642a56c668c1975952efc4dce2b5b70528 Mon Sep 17 00:00:00 2001 From: Oliver Nitzsche Date: Fri, 24 Oct 2025 22:42:05 +0200 Subject: [PATCH 62/62] for what is this return? --- src/Node/ServerGroup.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Node/ServerGroup.php b/src/Node/ServerGroup.php index d3cc4f0f..1ae5f0db 100644 --- a/src/Node/ServerGroup.php +++ b/src/Node/ServerGroup.php @@ -216,6 +216,7 @@ public function getUniqueId(): string */ public function getIcon(): string { + //TODO: HÄ whats this? return 'group_server'; } }