'b:1;', 'private.enable' => 'b:0;', 'private.msg' => 's:71:"Things are happening. Check back later for something new... eventually.";', 'private.perm.cat' => 's:4:"user";', 'private.perm.val' => 'i:1;', 'site.desc' => 's:38:"The internet\'s last convenience store.";', 'site.ext_logo' => 's:51:"https://static.flash.moe/images/flashii-logo-v3.png";', 'site.name' => 's:5:"Edgii";', 'site.social.bsky' => 's:36:"https://bsky.app/profile/flashii.net";', 'site.url' => 's:18:"https://edgii.net/";', 'test.array' => 'a:5:{i:0;i:1234;i:1;d:56.789;i:2;s:6:"Mewow!";i:3;b:1;i:4;s:4:"jeff";}', 'test.bool' => 'b:1;', 'test.float' => 'd:9876.4321;', 'test.int' => 'i:243230;', ]; private const USER_VALUES = [ 'mobile.left_handed' => ['b:0;', 'b:1;'], 'profile.allow_indexing' => ['b:1;', 'b:0;'], ]; protected function setUp(): void { $this->dbConn = DbBackends::create('sqlite::memory:'); $this->dbConn->execute('CREATE TABLE skh_config (config_name TEXT NOT NULL COLLATE NOCASE, config_value BLOB NOT NULL, PRIMARY KEY (config_name))'); $this->dbConn->execute('CREATE TABLE skh_user_settings (user_id INTEGER NOT NULL, setting_name TEXT NOT NULL COLLATE NOCASE, setting_value BLOB NOT NULL, PRIMARY KEY (user_id, setting_name))'); $stmt = $this->dbConn->prepare('INSERT INTO skh_config (config_name, config_value) VALUES (?, ?)'); foreach(self::VALUES as $name => $value) { $stmt->addParameter(1, $name); $stmt->addParameter(2, $value); $stmt->execute(); } $stmt = $this->dbConn->prepare('INSERT INTO skh_user_settings (user_id, setting_name, setting_value) VALUES (?, ?, ?)'); for($i = 1; $i <= 10; ++$i) foreach(self::USER_VALUES as $name => $value) { $stmt->addParameter(1, $i); $stmt->addParameter(2, $name); $stmt->addParameter(3, $value[$i % 2]); $stmt->execute(); } $this->config = new DbConfig($this->dbConn, 'skh_config'); } public function testScoping(): void { $this->assertEquals('user', $this->config->getString('private.perm.cat')); $this->assertEquals('Edgii', $this->config->getString('site.name')); $scoped = $this->config->scopeTo('private', 'perm'); $this->assertEquals('user', $scoped->getString('cat')); } public function testHasValues(): void { // hasValues should always return true when the list is empty $this->assertTrue($this->config->hasValues([])); $this->assertFalse($this->config->hasValues('meow')); $this->assertTrue($this->config->hasValues('site.desc')); $this->assertTrue($this->config->hasValues(['site.ext_logo', 'site.url'])); $this->assertFalse($this->config->hasValues(['site.ext_logo', 'site.url', 'site.gun'])); } public function testGetAllValueInfos(): void { $all = $this->config->getAllValueInfos(); $expected = array_keys(self::VALUES); $values = []; foreach($all as $info) $values[] = $info->getName(); $this->assertEquals($expected, $values); $subset = $this->config->getAllValueInfos(2, 3); $expected = [ 'private.perm.cat', 'private.perm.val', ]; $values = []; foreach($subset as $info) $values[] = $info->getName(); $this->assertEquals($expected, $values); } public function testGetValues(): void { $this->assertNull($this->config->getValueInfo('doesnotexist')); $scoped = $this->config->scopeTo('private', 'perm'); $expected = ['private.perm.cat' => 'user', 'private.perm.val' => 1]; $values = []; $valueInfos = $scoped->getValueInfos(['cat', 'val', 'poop']); foreach($valueInfos as $valueInfo) { $this->assertInstanceOf(ScopedConfigValueInfo::class, $valueInfo); $values[$valueInfo->getRealName()] = $valueInfo->getValue(); } $this->assertEquals($expected, $values); $scoped = $this->config->scopeTo('site')->scopeTo('social'); $expected = [ 'bsky' => 'https://bsky.app/profile/flashii.net', 'bsky_show' => true, 'twitter' => '', 'twitterShow' => false, ]; $values = $scoped->getValues([ 'bsky', ['bsky_show:b', true], 'twitter:s', ['twitter_show:b', false, 'twitterShow'], ]); $this->assertEquals($expected, $values); $this->assertEquals('', $this->config->getString('none.string')); $this->assertEquals('test', $this->config->getString('none.string', 'test')); $this->assertEquals('https://edgii.net/', $this->config->getString('site.url')); $this->assertEquals(0, $this->config->getInteger('site.url')); $this->assertEquals(0, $this->config->getInteger('none.int')); $this->assertEquals(10, $this->config->getInteger('none.int', 10)); $this->assertEquals(243230, $this->config->getInteger('test.int')); $this->assertEquals('', $this->config->getString('test.int')); $this->assertEquals(0, $this->config->getFloat('none.float')); $this->assertEquals(0.1, $this->config->getFloat('none.float', 0.1)); $this->assertEquals(9876.4321, $this->config->getFloat('test.float')); $this->assertEmpty($this->config->getArray('test.float')); $this->assertEquals(false, $this->config->getBoolean('none.bool')); $this->assertEquals(true, $this->config->getBoolean('none.bool', true)); $this->assertEquals(true, $this->config->getBoolean('test.bool')); $this->assertEquals(false, $this->config->getBoolean('private.msg')); $this->assertEquals(0, $this->config->getFloat('test.bool')); $this->assertEmpty($this->config->getArray('none.array')); $this->assertEquals(['de', 'het', 'een'], $this->config->getArray('none.array', ['de', 'het', 'een'])); $this->assertEquals([1234, 56.789, 'Mewow!', true, 'jeff'], $this->config->getArray('test.array')); $this->assertEquals(false, $this->config->getBoolean('test.array')); } public function testNameValidation(): void { $this->assertTrue(DbConfig::validateName('th1s.iS.vAL1d')); $this->assertFalse(DbConfig::validateName('')); $this->assertFalse(DbConfig::validateName('this..is.not.valid')); $this->assertFalse(DbConfig::validateName('this..is.not.valid')); $this->assertFalse(DbConfig::validateName('First.may.Not.be.uppercase')); } public function testUserSettings(): void { for($i = 1; $i <= 10; ++$i) { $config = new DbConfig($this->dbConn, 'skh_user_settings', 'setting_name', 'setting_value', ['user_id' => $i]); foreach(self::USER_VALUES as $name => $value) $this->assertEquals(unserialize($value[$i % 2]), $config->getBoolean($name)); } } }