'use strict'; const assert = require('node:assert/strict'); const test = require('node:test'); const fs = require('node:fs'); const path = require('node:path'); const os = require('node:os'); const { findUnusedAssets, collectUuidReferences } = require('../lib/tools/assets-advanced'); function createMockAssetsDir() { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'funplay-unused-test-')); const assetsDir = path.join(tmpDir, 'assets'); fs.mkdirSync(assetsDir, { recursive: true }); return { tmpDir, assetsDir }; } function writeFile(dir, relPath, content) { const fullPath = path.join(dir, relPath); fs.mkdirSync(path.dirname(fullPath), { recursive: true }); fs.writeFileSync(fullPath, content, 'utf8'); } test('findUnusedAssets returns assets not referenced by any serialized file', async () => { const { tmpDir, assetsDir } = createMockAssetsDir(); try { const usedUuid = 'aaa11111-bbbb-cccc-dddd-eeeeeeee1111'; writeFile(assetsDir, 'textures/used.png', 'fake-png'); writeFile(assetsDir, 'textures/used.png.meta', JSON.stringify({ uuid: usedUuid })); const unusedUuid = 'aaa22222-bbbb-cccc-dddd-eeeeeeee2222'; writeFile(assetsDir, 'textures/unused.png', 'fake-png'); writeFile(assetsDir, 'textures/unused.png.meta', JSON.stringify({ uuid: unusedUuid })); writeFile(assetsDir, 'scenes/game.scene', JSON.stringify({ __type__: 'cc.SceneAsset', scene: { __uuid__: usedUuid }, })); const mockListAssets = async () => [ { uuid: usedUuid, url: 'db://assets/textures/used.png', type: 'cc.Texture2D' }, { uuid: unusedUuid, url: 'db://assets/textures/unused.png', type: 'cc.Texture2D' }, ]; const result = await findUnusedAssets(tmpDir, {}, mockListAssets); assert.equal(result.totalScanned, 2); assert.equal(result.unusedCount, 1); assert.equal(result.unused[0].uuid, unusedUuid); } finally { fs.rmSync(tmpDir, { recursive: true, force: true }); } }); test('findUnusedAssets skips .ts and .scene files from unused list', async () => { const { tmpDir, assetsDir } = createMockAssetsDir(); try { const scriptUuid = 'bbb11111-bbbb-cccc-dddd-eeeeeeee3333'; const sceneUuid = 'bbb22222-bbbb-cccc-dddd-eeeeeeee4444'; writeFile(assetsDir, 'scripts/MyScript.ts', 'export class MyScript {}'); writeFile(assetsDir, 'scripts/MyScript.ts.meta', JSON.stringify({ uuid: scriptUuid })); writeFile(assetsDir, 'scenes/level.scene', '{}'); writeFile(assetsDir, 'scenes/level.scene.meta', JSON.stringify({ uuid: sceneUuid })); const mockListAssets = async () => [ { uuid: scriptUuid, url: 'db://assets/scripts/MyScript.ts', type: 'cc.ScriptAsset' }, { uuid: sceneUuid, url: 'db://assets/scenes/level.scene', type: 'cc.SceneAsset' }, ]; const result = await findUnusedAssets(tmpDir, {}, mockListAssets); assert.equal(result.totalScanned, 2); assert.equal(result.unusedCount, 0); } finally { fs.rmSync(tmpDir, { recursive: true, force: true }); } }); test('findUnusedAssets respects excludeDirectories', async () => { const { tmpDir, assetsDir } = createMockAssetsDir(); try { const referencedUuid = 'ccc11111-bbbb-cccc-dddd-eeeeeeee5555'; const unreferencedUuid = 'ccc22222-bbbb-cccc-dddd-eeeeeeee6666'; writeFile(assetsDir, 'textures/a.png', 'fake'); writeFile(assetsDir, 'textures/a.png.meta', JSON.stringify({ uuid: referencedUuid })); writeFile(assetsDir, 'internal/b.png', 'fake'); writeFile(assetsDir, 'internal/b.png.meta', JSON.stringify({ uuid: unreferencedUuid })); writeFile(assetsDir, 'internal/hidden.scene', JSON.stringify({ __type__: 'cc.SceneAsset', tex: { __uuid__: referencedUuid }, })); const mockListAssets = async () => [ { uuid: referencedUuid, url: 'db://assets/textures/a.png', type: 'cc.Texture2D' }, { uuid: unreferencedUuid, url: 'db://assets/internal/b.png', type: 'cc.Texture2D' }, ]; const result = await findUnusedAssets(tmpDir, { excludeDirectories: ['internal'], }, mockListAssets); assert.equal(result.totalScanned, 2); assert.equal(result.unusedCount, 2); } finally { fs.rmSync(tmpDir, { recursive: true, force: true }); } }); test('findUnusedAssets respects limit option', async () => { const { tmpDir, assetsDir } = createMockAssetsDir(); try { const assets = []; for (let i = 0; i < 5; i++) { const uuid = `ddd${i}1111-bbbb-cccc-dddd-eeeeeeee777${i}`; writeFile(assetsDir, `textures/tex${i}.png`, 'fake'); writeFile(assetsDir, `textures/tex${i}.png.meta`, JSON.stringify({ uuid })); assets.push({ uuid, url: `db://assets/textures/tex${i}.png`, type: 'cc.Texture2D' }); } const mockListAssets = async () => assets; const result = await findUnusedAssets(tmpDir, { limit: 3 }, mockListAssets); assert.equal(result.totalScanned, 5); assert.equal(result.unusedCount, 5); assert.equal(result.unused.length, 3); } finally { fs.rmSync(tmpDir, { recursive: true, force: true }); } }); test('collectUuidReferences is re-exported and works', () => { const refs = collectUuidReferences(JSON.stringify({ __uuid__: 'abc12345-def6-7890-abcd-ef1234567890', })); assert.ok(refs.some((r) => r.uuid === 'abc12345-def6-7890-abcd-ef1234567890')); });