220 lines
8.1 KiB
JavaScript
220 lines
8.1 KiB
JavaScript
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 category recursive deletion ---');
|
|
|
|
const tempDir = mkdtempSync(join(tmpdir(), 'test-cat-del-rec-'));
|
|
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: 'Recursive Test Corp',
|
|
iso639: 'de',
|
|
taxrate: 19,
|
|
reducedtaxrate: 7,
|
|
googleAuthId,
|
|
});
|
|
await store.google_auth.setSelectedCompany(googleAuthId, company.id);
|
|
|
|
const rootId = company.rootCategoryId;
|
|
|
|
// 1. Refuse to delete root category, even with recursive: true
|
|
console.log('1. Testing refusal to delete root category...');
|
|
await assert.rejects(
|
|
() => store.category.delete({ googleAuthId, id: rootId, recursive: true }),
|
|
/Cannot delete root category/
|
|
);
|
|
|
|
// 2. Build category tree:
|
|
// Root
|
|
// ├── OutsideCat (for preserved articles)
|
|
// └── MainCat (to be recursively deleted)
|
|
// ├── SubCat1
|
|
// │ └── SubSubCat1
|
|
// └── SubCat2
|
|
console.log('2. Building category tree...');
|
|
const outsideCat = await store.category.createUnderParent({
|
|
googleAuthId,
|
|
parentId: rootId,
|
|
name: 'Outside Category',
|
|
});
|
|
const mainCat = await store.category.createUnderParent({
|
|
googleAuthId,
|
|
parentId: rootId,
|
|
name: 'Main Category',
|
|
});
|
|
const subCat1 = await store.category.createUnderParent({
|
|
googleAuthId,
|
|
parentId: mainCat.id,
|
|
name: 'Sub Category 1',
|
|
});
|
|
const subSubCat1 = await store.category.createUnderParent({
|
|
googleAuthId,
|
|
parentId: subCat1.id,
|
|
name: 'Sub Sub Category 1',
|
|
});
|
|
const subCat2 = await store.category.createUnderParent({
|
|
googleAuthId,
|
|
parentId: mainCat.id,
|
|
name: 'Sub Category 2',
|
|
});
|
|
|
|
// 3. Test non-recursive delete guards
|
|
console.log('3. Testing non-recursive delete guards...');
|
|
await assert.rejects(
|
|
() => store.category.delete({ googleAuthId, id: mainCat.id, recursive: false }),
|
|
/Category has subcategories/
|
|
);
|
|
|
|
// 4. Create articles:
|
|
// Article 1: Only in SubSubCat1 -> Should be deleted
|
|
// Article 2: In SubCat1 AND OutsideCat -> Should survive, removed from SubCat1
|
|
// Article 3: Parent in SubCat2 with a variant child -> Both should be deleted
|
|
// Article 4: Outside article in OutsideCat -> Untouched
|
|
console.log('4. Creating articles in categories...');
|
|
const artOnlySub = await store.article.createUnderParent({
|
|
googleAuthId,
|
|
parentId: subSubCat1.id,
|
|
name: 'Only SubSubCat Article',
|
|
});
|
|
|
|
const artMulti = await store.article.createUnderParent({
|
|
googleAuthId,
|
|
parentId: subCat1.id,
|
|
name: 'Multi Category Article',
|
|
});
|
|
// Add outsideCat to artMulti
|
|
await store.article.setCategoryIdsBySku(googleAuthId, artMulti.sku, [subCat1.id, outsideCat.id]);
|
|
|
|
const attrName = await store.attributename.create(googleAuthId, { name: 'Size' });
|
|
const artParent = await store.article.createUnderParent({
|
|
googleAuthId,
|
|
parentId: subCat2.id,
|
|
name: 'Parent Article In Sub2',
|
|
});
|
|
await store.article.toggleParent(googleAuthId, artParent.sku, {
|
|
isParent: true,
|
|
attributeNameId: attrName.id,
|
|
});
|
|
const artChild = await store.article.addChild(googleAuthId, artParent.sku, {
|
|
value: 'M',
|
|
});
|
|
|
|
const artOutside = await store.article.createUnderParent({
|
|
googleAuthId,
|
|
parentId: outsideCat.id,
|
|
name: 'Purely Outside Article',
|
|
});
|
|
|
|
// 5. Test POS terminal assignment unlinking on recursive delete
|
|
console.log('5. Setting up POS terminal assignment...');
|
|
const posTerminal = await store.posterminal.upsert(googleAuthId, {
|
|
name: 'POS Register 1',
|
|
pairingpin: '1234',
|
|
});
|
|
await store.posterminal.pair(posTerminal.id, {
|
|
token: 'pos-token-1',
|
|
clientserial: 'SN1',
|
|
pairedAt: new Date().toISOString(),
|
|
});
|
|
await store.posterminal.setOn(googleAuthId, posTerminal.id, subCat2.id);
|
|
await store.posterminal.setOn(googleAuthId, posTerminal.id, outsideCat.id);
|
|
|
|
const posCatsBefore = provider.db.prepare(
|
|
'SELECT category_id FROM posterminalcategory WHERE posterminal_id = ?'
|
|
).all(posTerminal.id).map(r => r.category_id);
|
|
assert.ok(posCatsBefore.includes(subCat2.id));
|
|
assert.ok(posCatsBefore.includes(outsideCat.id));
|
|
|
|
// 6. Execute recursive deletion of mainCat
|
|
console.log('6. Executing recursive category delete on mainCat...');
|
|
await store.category.delete({
|
|
googleAuthId,
|
|
id: mainCat.id,
|
|
recursive: true,
|
|
});
|
|
|
|
// 7. Verify category tree state
|
|
console.log('7. Verifying category tree...');
|
|
const catsRemaining = provider.db.prepare('SELECT id FROM category').all().map(r => r.id);
|
|
assert.ok(catsRemaining.includes(rootId));
|
|
assert.ok(catsRemaining.includes(outsideCat.id));
|
|
assert.ok(!catsRemaining.includes(mainCat.id));
|
|
assert.ok(!catsRemaining.includes(subCat1.id));
|
|
assert.ok(!catsRemaining.includes(subSubCat1.id));
|
|
assert.ok(!catsRemaining.includes(subCat2.id));
|
|
|
|
// 8. Verify POS terminal assignment was unlinked for deleted category
|
|
console.log('8. Verifying POS terminal categories...');
|
|
const posCatsAfter = provider.db.prepare(
|
|
'SELECT category_id FROM posterminalcategory WHERE posterminal_id = ?'
|
|
).all(posTerminal.id).map(r => r.category_id);
|
|
assert.ok(!posCatsAfter.includes(subCat2.id));
|
|
assert.ok(posCatsAfter.includes(outsideCat.id));
|
|
|
|
// 9. Verify article states:
|
|
console.log('9. Verifying article states...');
|
|
// artOnlySub: deleted
|
|
await assert.rejects(
|
|
() => store.article.getBySku(googleAuthId, artOnlySub.sku),
|
|
/Article not found/
|
|
);
|
|
// tombstone for artOnlySub exists
|
|
const t1 = provider.db.prepare("SELECT * FROM posterminaltombstone WHERE posterminal_id = ? AND entity_type = 'article' AND entity_id = ?").all(posTerminal.id, artOnlySub.id);
|
|
assert.equal(t1.length, 1);
|
|
|
|
// artParent and its child artChild: both deleted
|
|
await assert.rejects(
|
|
() => store.article.getBySku(googleAuthId, artParent.sku),
|
|
/Article not found/
|
|
);
|
|
await assert.rejects(
|
|
() => store.article.getBySku(googleAuthId, artChild.article.sku),
|
|
/Article not found/
|
|
);
|
|
const tParent = provider.db.prepare("SELECT * FROM posterminaltombstone WHERE posterminal_id = ? AND entity_type = 'article' AND entity_id = ?").all(posTerminal.id, artParent.id);
|
|
const tChild = provider.db.prepare("SELECT * FROM posterminaltombstone WHERE posterminal_id = ? AND entity_type = 'article' AND entity_id = ?").all(posTerminal.id, artChild.article.id);
|
|
assert.equal(tParent.length, 1);
|
|
assert.equal(tChild.length, 1);
|
|
|
|
// artMulti: continues to exist!
|
|
const artMultiDetail = await store.article.getBySku(googleAuthId, artMulti.sku);
|
|
assert.equal(artMultiDetail.article.sku, artMulti.sku);
|
|
// artMulti categoryarticle now ONLY contains outsideCat
|
|
const artMultiCats = provider.db.prepare(
|
|
'SELECT category_id FROM categoryarticle WHERE article_id = ?'
|
|
).all(artMultiDetail.article.id).map(r => r.category_id);
|
|
assert.deepEqual(artMultiCats, [outsideCat.id]);
|
|
|
|
// artOutside: completely untouched
|
|
const artOutsideDetail = await store.article.getBySku(googleAuthId, artOutside.sku);
|
|
assert.equal(artOutsideDetail.article.sku, artOutside.sku);
|
|
|
|
console.log('✅ ALL RECURSIVE CATEGORY DELETE TESTS PASSED');
|
|
} finally {
|
|
try {
|
|
rmSync(tempDir, { recursive: true, force: true });
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
run().catch((err) => {
|
|
console.error('Test failed:', err);
|
|
process.exit(1);
|
|
});
|