Skip to content
Open
8 changes: 8 additions & 0 deletions plist
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,14 @@
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/VirtualIPFieldTest.php
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Base/FieldTypes/VirtualIPFieldTest/config.xml
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Dnsmasq/FieldTypes/HostnameFieldTest.php
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/Base/JsonSampleTestCase.php
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/KeaCtrlAgentTest.php
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/KeaDhcpv4Test.php
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/KeaDhcpv6Test.php
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/Snapshots/KeaCtrlAgentTest.json
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/Snapshots/KeaDhcpv4Test.json
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/Snapshots/KeaDhcpv6Test.json
/usr/local/opnsense/mvc/tests/app/models/OPNsense/Kea/Snapshots/config.xml
/usr/local/opnsense/mvc/tests/phpunit.xml
/usr/local/opnsense/mvc/tests/setup.php
/usr/local/opnsense/scripts/auth/add_user.php
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

/*
* Copyright (C) 2025 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

namespace tests\OPNsense\Kea;

use OPNsense\Core\Config;
use OPNsense\Core\AppConfig;
use PHPUnit\Framework\TestCase;

abstract class JsonSampleTestCase extends TestCase
{
abstract protected function getSnapshotFile(): string;
abstract protected function getModelInstance();
abstract protected function getJsonRootKey(): string;

private function getSnapshotPath(): string
{
return dirname(__DIR__) . '/Snapshots/' . $this->getSnapshotFile();
}

private function loadSnapshotConfig(): void
{
$snapDir = dirname(__DIR__) . '/Snapshots';

(new AppConfig())->update('application.configDir', $snapDir);

Config::getInstance()->forceReload();
}

private function loadExpectedSnapshot(): array
{
$file = $this->getSnapshotPath();
$json = json_decode(file_get_contents($file), true);

$this->assertNotNull($json, 'Failed to decode expected JSON: ' . $file);
return $json;
}

private function assertJsonEqualWithPath(array $expected, array $actual, string $path = ''): void
{
// expected keys
foreach ($expected as $key => $expValue) {
$current = $path === '' ? $key : "$path.$key";

$this->assertArrayHasKey($key, $actual, "Missing key at: $current");
$actValue = $actual[$key];

if (is_array($expValue)) {
$this->assertIsArray($actValue, "Type mismatch at: $current");
$this->assertJsonEqualWithPath($expValue, $actValue, $current);
} else {
$this->assertSame($expValue, $actValue, "Value mismatch at: $current");
}
}

// unexpected keys
foreach ($actual as $key => $actValue) {
$current = $path === '' ? $key : "$path.$key";
$this->assertArrayHasKey($key, $expected, "Unexpected key at: $current");
}
}

protected function runJsonSampleTest(): void
{
$this->loadSnapshotConfig();
$model = $this->getModelInstance();

$tmp = tempnam('/var/lib/php/tmp/', 'json_sample_');
$model->generateConfig($tmp);

$expected = $this->loadExpectedSnapshot();
$actual = json_decode(file_get_contents($tmp), true);

$this->assertNotNull($actual, 'Generated JSON is invalid.');

$root = $this->getJsonRootKey();
$this->assertArrayHasKey($root, $expected);
$this->assertArrayHasKey($root, $actual);

$this->assertJsonEqualWithPath($expected[$root], $actual[$root], $root);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/*
* Copyright (C) 2025 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

namespace tests\OPNsense\Kea;

require_once __DIR__ . '/Base/JsonSampleTestCase.php';

use OPNsense\Kea\KeaCtrlAgent;

class KeaCtrlAgentTest extends JsonSampleTestCase
{
protected function getSnapshotFile(): string
{
return 'KeaCtrlAgentTest.json';
}

protected function getModelInstance()
{
return new KeaCtrlAgent();
}

protected function getJsonRootKey(): string
{
return 'Control-agent';
}

public function testJsonSample(): void
{
$this->runJsonSampleTest();
}
}
107 changes: 107 additions & 0 deletions src/opnsense/mvc/tests/app/models/OPNsense/Kea/KeaDhcpv4Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

/*
* Copyright (C) 2025 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

namespace tests\OPNsense\Kea;

require_once __DIR__ . '/Base/JsonSampleTestCase.php';

use OPNsense\Kea\KeaDhcpv4;

class KeaDhcpv4Test extends JsonSampleTestCase
{
protected function getSnapshotFile(): string
{
return 'KeaDhcpv4Test.json';
}

protected function getModelInstance()
{
return new KeaDhcpv4();
}

protected function getJsonRootKey(): string
{
return 'Dhcp4';
}

public function testJsonSample(): void
{
$this->runJsonSampleTest();
}

public function testIsEnabled(): void
{
$m = $this->getModelInstance();

$m->general->enabled = "0";
$m->general->interfaces = "";

$this->assertFalse($m->isEnabled());

$m->general->enabled = "1";
$this->assertFalse($m->isEnabled());

$m->general->interfaces = "lan";
$this->assertTrue($m->isEnabled());
}

public function testFwRulesEnabled(): void
{
$m = $this->getModelInstance();

$m->general->enabled = "0";
$m->general->fwrules = "0";
$m->general->interfaces = "";

$this->assertFalse($m->fwrulesEnabled());

$m->general->enabled = "1";
$this->assertFalse($m->fwrulesEnabled());

$m->general->fwrules = "1";
$this->assertFalse($m->fwrulesEnabled());

$m->general->interfaces = "lan";
$this->assertTrue($m->fwrulesEnabled());
}

public function testPhysicalInterfacesParsing(): void
{
// config.xml already loaded by JsonSampleTestCase::loadSnapshotConfig()

$m = $this->getModelInstance();
$m->general->interfaces = "lan,opt1";

$ifs = (new \ReflectionClass($m))
->getMethod('getConfigPhysicalInterfaces')
->invoke($m);

$this->assertSame(['igc0','igc2'], $ifs);
}

}
106 changes: 106 additions & 0 deletions src/opnsense/mvc/tests/app/models/OPNsense/Kea/KeaDhcpv6Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

/*
* Copyright (C) 2025 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

namespace tests\OPNsense\Kea;

require_once __DIR__ . '/Base/JsonSampleTestCase.php';

use OPNsense\Kea\KeaDhcpv6;

class KeaDhcpv6Test extends JsonSampleTestCase
{
protected function getSnapshotFile(): string
{
return 'KeaDhcpv6Test.json';
}

protected function getModelInstance()
{
return new KeaDhcpv6();
}

protected function getJsonRootKey(): string
{
return 'Dhcp6';
}

public function testJsonSample(): void
{
$this->runJsonSampleTest();
}

public function testIsEnabled(): void
{
$m = $this->getModelInstance();

$m->general->enabled = "0";
$m->general->interfaces = "";

$this->assertFalse($m->isEnabled());

$m->general->enabled = "1";
$this->assertFalse($m->isEnabled());

$m->general->interfaces = "lan";
$this->assertTrue($m->isEnabled());
}

public function testFwRulesEnabled(): void
{
$m = $this->getModelInstance();

$m->general->enabled = "0";
$m->general->fwrules = "0";
$m->general->interfaces = "";

$this->assertFalse($m->fwrulesEnabled());

$m->general->enabled = "1";
$this->assertFalse($m->fwrulesEnabled());

$m->general->fwrules = "1";
$this->assertFalse($m->fwrulesEnabled());

$m->general->interfaces = "lan";
$this->assertTrue($m->fwrulesEnabled());
}

public function testPhysicalInterfacesParsing(): void
{
// config.xml already loaded by JsonSampleTestCase::loadSnapshotConfig()

$m = $this->getModelInstance();
$m->general->interfaces = "lan,opt1";

$ifs = (new \ReflectionClass($m))
->getMethod('getConfigPhysicalInterfaces')
->invoke($m);

$this->assertSame(['igc0','igc2'], $ifs);
}
}
Loading