Files
XC_VM/tests/Unit/CategoryTemplateApplyCustomDataTest.php
T
Divarion_D a403b8b6af feat(category-templates): apply templates on line/mag/enigma & reseller vouchers
Add category-template selection to the line, mag and enigma edit forms and to
the reseller active-code (voucher) form, plus a reseller navbar entry. Extract
the shared custom_data resolver into CategoryTemplateService::applyCustomData()
and route the Line/Mag/Enigma saves through it, removing the triplicated inline
block; covered by a new unit test.

Review fixes folded in:
- getRecentBatchNames filtered a non-existent `user_id` column and used
  DISTINCT + ORDER BY id (rejected by MySQL ONLY_FULL_GROUP_BY). Restore the
  correct created_by + GROUP BY MAX(created_at) query (kept the empty-batch
  guard).
- routes/admin.php: import ActiveCodeDetailsController (fatal short-name use
  without it) for the active_code_details route.
- Revert the accidental re-inline of the ancestorOwnerIds() helper in
  getTemplatesForUser and the getResellersWithCodes all-users fallback.

Also: settings save only zeroes checkbox flags on the full settings form
(submit_settings), and reseller bootstrap tolerates a missing language.
2026-09-21 18:37:33 +03:00

88 lines
2.9 KiB
PHP

<?php
namespace XcVm\Tests\Unit;
use PHPUnit\Framework\TestCase;
use TestDb;
use XcVm\Domain\Stream\CategoryTemplateService;
use XcVm\Infrastructure\Database\DatabaseFactory;
/**
* Coverage for the shared custom_data resolver used by line, mag and enigma
* saves (CategoryTemplateService::applyCustomData). The column must only be
* touched when the request carries `category_template_id` or `custom_data`,
* so an untouched edit preserves whatever layout is already stored.
*/
final class CategoryTemplateApplyCustomDataTest extends TestCase {
private TestDb $db;
protected function setUp(): void {
$this->db = new TestDb();
$this->db->exec(
'CREATE TABLE `category_template_items` (
`id` INTEGER PRIMARY KEY,
`template_id` INTEGER,
`category_type` TEXT,
`category_id` INTEGER,
`custom_name` TEXT,
`is_visible` INTEGER DEFAULT 1,
`sort_order` INTEGER DEFAULT 0
);
INSERT INTO `category_template_items`
(`id`,`template_id`,`category_type`,`category_id`,`custom_name`,`is_visible`,`sort_order`) VALUES
(1,5,"live",10,"Renamed",1,1),
(2,5,"live",11,NULL,0,2);'
);
DatabaseFactory::set($this->db);
}
public function testTemplateIdZeroOrNoneClearsCustomData(): void {
$this->assertNull(
CategoryTemplateService::applyCustomData(['category_template_id' => '0'], [])['custom_data']
);
$this->assertNull(
CategoryTemplateService::applyCustomData(['category_template_id' => 'none'], [])['custom_data']
);
}
public function testPositiveTemplateIdBuildsCustomDataJson(): void {
$row = CategoryTemplateService::applyCustomData(['category_template_id' => '5'], []);
$this->assertArrayHasKey('custom_data', $row);
$decoded = json_decode((string) $row['custom_data'], true);
$this->assertSame(5, $decoded['template_id']);
// Hidden category (is_visible=0) lands in the live hide list.
$this->assertStringContainsString('11', (string) $decoded['live_cat']['hide_ids']);
$this->assertSame('10,11', $decoded['live_cat']['order']);
}
public function testInvalidTemplateIdLeavesCustomDataUntouched(): void {
// Empty / non-numeric template id must not overwrite an existing layout.
$row = CategoryTemplateService::applyCustomData(
['category_template_id' => ''],
['custom_data' => 'KEEP']
);
$this->assertSame('KEEP', $row['custom_data']);
}
public function testRawCustomDataStringAndArrayAndEmpty(): void {
$this->assertSame(
'{"x":1}',
CategoryTemplateService::applyCustomData(['custom_data' => '{"x":1}'], [])['custom_data']
);
$this->assertSame(
'{"x":1}',
CategoryTemplateService::applyCustomData(['custom_data' => ['x' => 1]], [])['custom_data']
);
$this->assertNull(
CategoryTemplateService::applyCustomData(['custom_data' => ''], [])['custom_data']
);
}
public function testNeitherKeyPreservesTarget(): void {
$target = ['username' => 'bob', 'custom_data' => 'EXISTING'];
$this->assertSame($target, CategoryTemplateService::applyCustomData([], $target));
}
}