diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..682f991 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,61 @@ +name: PHP test + +on: [ push, pull_request ] + +jobs: + test: + name: Test + runs-on: ${{ matrix.os }} + + strategy: + matrix: + unifi-version: [ stable-5, stable-6, v7.0, v7.1, v7.2, v7.3, v7.4, v7.5, v8.0 ] + php-version: [ 7.4, "8.0", 8.1, 8.2, 8.3 ] + mongo-version: [ 3.6 ] + os: [ ubuntu-latest ] + + services: + mongo: + image: mongo:${{ matrix.mongo-version }} + ports: + - "27017:27017/tcp" + unifi: + image: jacobalberty/unifi:${{ matrix.unifi-version }} + env: + DB_URI: mongodb://mongo/unifi + STATDB_URI: mongodb://mongo/unifi_stat + DB_NAME: unifi + ports: + - "8443:8443/tcp" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: curl, json + + - name: Get composer cache directory + id: composercache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-php-${{ matrix.php-version }}- + + - name: Install dependencies + run: | + composer install --prefer-dist + + - name: Unit tests + run: | + composer run-script test + + - name: Integration tests + run: | + composer run-script integration-test diff --git a/.gitignore b/.gitignore index 33306ea..acecdef 100755 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ *.xml # ignore PHPStorm files -.idea/* \ No newline at end of file +.idea/* + +# PHPUnit +/phpunit.xml +.phpunit.result.cache diff --git a/composer.json b/composer.json index 4043a24..a0ff5a1 100755 --- a/composer.json +++ b/composer.json @@ -28,5 +28,16 @@ "psr-4": { "UniFi_API\\": "src/" } + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "scripts": { + "test" : [ + "vendor/bin/phpunit --testdox --testsuite \"Unit tests\"" + ], + "integration-test" : [ + "vendor/bin/phpunit --testdox --testsuite \"Integration tests\"" + ] } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..e5eeffb --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,13 @@ + + + + + tests/Unit + + + tests/Integration + + + diff --git a/src/Client.php b/src/Client.php index 277a9ac..4fd33ad 100755 --- a/src/Client.php +++ b/src/Client.php @@ -1835,6 +1835,22 @@ public function set_site_connectivity(string $connectivity_id, $payload): bool $payload); } + /** + * Creates default admin account for controller in setup mode + * + * @return bool true on success + */ + public function add_default_admin() + { + $payload = [ + 'cmd' => 'add-default-admin', + 'name' => $this->user, + 'x_password' => $this->password, + ]; + + return $this->fetch_results_boolean('/api/cmd/sitemgr', $payload, false); + } + /** * Fetch admins * diff --git a/tests/Integration/ClientTest.php b/tests/Integration/ClientTest.php new file mode 100644 index 0000000..c4ee3f7 --- /dev/null +++ b/tests/Integration/ClientTest.php @@ -0,0 +1,83 @@ +add_default_admin(); + } + + public function testLogin() + { + $this->assertTrue(self::$client->login()); + } + + public function testStatSysinfo() + { + $stat_sysinfo = self::$client->stat_sysinfo(); + $this->assertCount(1, $stat_sysinfo); + $this->assertObjectHasAttribute('timezone', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('autobackup', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('build', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('version', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('data_retention_days', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('data_retention_time_in_hours_for_5minutes_scale', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('data_retention_time_in_hours_for_hourly_scale', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('data_retention_time_in_hours_for_daily_scale', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('data_retention_time_in_hours_for_monthly_scale', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('data_retention_time_in_hours_for_others', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('update_available', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('update_downloaded', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('live_chat', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('store_enabled', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('hostname', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('name', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('ip_addrs', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('inform_port', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('https_port', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('override_inform_host', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('image_maps_use_google_engine', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('radius_disconnect_running', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('facebook_wifi_registered', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('sso_app_id', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('sso_app_sec', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('unsupported_device_count', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('unsupported_device_list', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('unifi_go_enabled', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('default_site_device_auth_password_alert', $stat_sysinfo[0]); + + if (\version_compare($stat_sysinfo[0]->version, '6') >= 0) { + $this->assertObjectHasAttribute('uptime', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('anonymous_controller_id', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('has_webrtc_support', $stat_sysinfo[0]); + } + + if (\version_compare($stat_sysinfo[0]->version, '7') >= 0) { + $this->assertObjectHasAttribute('debug_setting_preference', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('debug_mgmt', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('debug_system', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('debug_device', $stat_sysinfo[0]); + $this->assertObjectHasAttribute('debug_sdn', $stat_sysinfo[0]); + } + } + + public function testListCountryCodes() + { + $country_codes = self::$client->list_country_codes(); + $this->assertGreaterThanOrEqual(168, $country_codes); + + foreach ($country_codes as $country_code) { + $this->assertObjectHasAttribute('code', $country_code); + $this->assertObjectHasAttribute('name', $country_code); + $this->assertObjectHasAttribute('key', $country_code); + } + } +} diff --git a/tests/Unit/ClientTest.php b/tests/Unit/ClientTest.php new file mode 100644 index 0000000..3ba4040 --- /dev/null +++ b/tests/Unit/ClientTest.php @@ -0,0 +1,36 @@ +client = new Client('unifi', 'unifi'); + } + + public function testSetSite() + { + // default + $this->assertEquals('default', $this->client->get_site()); + + // custom + $this->client->set_site('foobar'); + $this->assertEquals('foobar', $this->client->get_site()); + + // whitespace + $this->client->set_site(' foobar '); + $this->assertEquals('foobar', $this->client->get_site()); + + // whitespace (debug mode) + $this->client->set_debug(true); + $this->expectNotice(); + $this->expectNoticeMessage('The provided (short) site name may not contain any spaces'); + $this->client->set_site(' foobar '); + } +}