import assert from 'node:assert/strict'; import { mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import LibDashStore from '../src/index.js'; import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js'; async function run() { console.log('--- Testing CategoryProperty & CategoryAttributeValue ---'); const tempDir = mkdtempSync(join(tmpdir(), 'test-cat-prop-attr-')); const dbFile = join(tempDir, 'test.db'); const storageDir = join(tempDir, 'storage'); const provider = new BetterSqlite3Provider({ filename: dbFile, storageDir, }); await provider.initialize(); const store = await new LibDashStore(provider); try { const googleAuthId = await store.google_auth.add('tester@example.com'); const company = await store.company.create({ name: 'Test Corp', iso639: 'de', taxrate: 19, reducedtaxrate: 7, googleAuthId, }); await store.google_auth.setSelectedCompany(googleAuthId, company.id); // Create a subcategory under root const category = await store.category.createUnderParent({ googleAuthId, parentId: company.rootCategoryId, name: 'Test Category', }); assert.ok(category.id > 0); // 1. Test CategoryProperty console.log('1. Testing categoryproperty CRUD...'); const prop1 = await store.propertyname.create(googleAuthId, { name: 'Material Care', exclusive: true, }); const prop2 = await store.propertyname.create(googleAuthId, { name: 'Season', exclusive: false, }); // Set properties const setProps = await store.categoryproperty.setByCategoryId(googleAuthId, category.id, [ { propertyId: prop1.id, value: 'Dry Clean Only', sort: 10 }, { propertyId: prop2.id, value: 'Summer 2026', sort: 20 }, ]); assert.equal(setProps.length, 2); // List properties const listedProps = await store.categoryproperty.listByCategoryId(googleAuthId, category.id); assert.equal(listedProps.length, 2); assert.equal(listedProps[0].name, 'Material Care'); assert.equal(listedProps[0].value, 'Dry Clean Only'); assert.equal(listedProps[0].exclusive, true); assert.equal(listedProps[1].name, 'Season'); assert.equal(listedProps[1].value, 'Summer 2026'); assert.equal(listedProps[1].exclusive, false); // Test exclusive violation on categoryproperty await assert.rejects( async () => { await store.categoryproperty.setByCategoryId(googleAuthId, category.id, [ { propertyId: prop1.id, value: 'Val 1' }, { propertyId: prop1.id, value: 'Val 2' }, ]); }, /exclusive/i, 'Should reject multiple values for exclusive property' ); // Delete single property await store.categoryproperty.delete(googleAuthId, category.id, prop1.id); const afterDeleteProps = await store.categoryproperty.listByCategoryId(googleAuthId, category.id); assert.equal(afterDeleteProps.length, 1); assert.equal(afterDeleteProps[0].propertyId, prop2.id); console.log('CategoryProperty tests passed!'); // 2. Test CategoryAttributeValue console.log('2. Testing categoryattributevalue CRUD...'); const attrExclusive = await store.attributename.create(googleAuthId, { name: 'Department', exclusive: true, }); const valDeptMen = await store.attributevalue.create(googleAuthId, attrExclusive.id, { value: 'Men', }); const valDeptWomen = await store.attributevalue.create(googleAuthId, attrExclusive.id, { value: 'Women', }); const attrMulti = await store.attributename.create(googleAuthId, { name: 'Tags', exclusive: false, }); const valTagSale = await store.attributevalue.create(googleAuthId, attrMulti.id, { value: 'Sale', }); const valTagNew = await store.attributevalue.create(googleAuthId, attrMulti.id, { value: 'New', }); // Bulk set with numeric array const setAttrValues = await store.categoryattributevalue.setByCategoryId(googleAuthId, category.id, [ valDeptMen.id, valTagSale.id, valTagNew.id, ]); assert.equal(setAttrValues.length, 3); // List attribute values const listedAttrValues = await store.categoryattributevalue.listByCategoryId(googleAuthId, category.id); assert.equal(listedAttrValues.length, 3); const deptMatch = listedAttrValues.find((v) => v.attributeName === 'Department'); assert.ok(deptMatch); assert.equal(deptMatch.value, 'Men'); assert.equal(deptMatch.exclusive, true); // Test exclusive violation await assert.rejects( async () => { await store.categoryattributevalue.setByCategoryId(googleAuthId, category.id, [ valDeptMen.id, valDeptWomen.id, ]); }, /exclusive/i, 'Should reject multiple values for exclusive attribute' ); // Test flexible object shapes in setByCategoryId const setWithObjs = await store.categoryattributevalue.setByCategoryId(googleAuthId, category.id, [ { attributeValueId: valDeptWomen.id, sort: 5 }, { id: valTagSale.id, sort: 15 }, ]); assert.equal(setWithObjs.length, 2); const listedAfterObjSet = await store.categoryattributevalue.listByCategoryId(googleAuthId, category.id); assert.equal(listedAfterObjSet.length, 2); assert.equal(listedAfterObjSet[0].value, 'Women'); assert.equal(listedAfterObjSet[0].sort, 5); // 3. Test cascade deletion on category delete console.log('3. Testing cascade deletion when category is deleted...'); await store.category.delete({ googleAuthId, id: category.id }); // Directly query database to ensure categoryproperty and categoryattributevalue rows were deleted by FK cascade const remainingProps = await provider.query('SELECT * FROM categoryproperty WHERE category_id = @id', { id: category.id }); assert.equal(remainingProps.rows.length, 0); const remainingAttrValues = await provider.query('SELECT * FROM categoryattributevalue WHERE category_id = @id', { id: category.id }); assert.equal(remainingAttrValues.rows.length, 0); console.log('All CategoryProperty and CategoryAttributeValue tests passed successfully!'); } finally { try { rmSync(tempDir, { recursive: true, force: true }); } catch {} } } run().catch((err) => { console.error('Test failed:', err); process.exit(1); });