super-productivity/electron/image-cache.test.cjs

237 lines
7.6 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const os = require('node:os');
const path = require('node:path');
const { promises: fs } = require('node:fs');
const fsModule = require('node:fs');
const Module = require('node:module');
require('ts-node/register/transpile-only');
const originalModuleLoad = Module._load;
let userDataDir;
const modulePath = path.resolve(__dirname, 'image-cache.ts');
const installMocks = () => {
Module._load = function patchedLoad(request, parent, isMain) {
if (request === 'electron') {
return {
app: { getPath: () => userDataDir },
};
}
return originalModuleLoad.call(this, request, parent, isMain);
};
};
const reset = () => {
delete require.cache[modulePath];
};
const load = () => {
reset();
return require(modulePath);
};
test.beforeEach(async () => {
userDataDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-img-cache-')),
);
installMocks();
});
test.afterEach(async () => {
Module._load = originalModuleLoad;
reset();
await fs.rm(userDataDir, { recursive: true, force: true });
});
const mkPng = async (dir, name = 'bg.png', bytes = Buffer.from('fakepngdata')) => {
const p = path.join(dir, name);
await fs.writeFile(p, bytes);
return p;
};
test('importImage copies a valid image and returns an opaque id', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const src = await mkPng(sourceDir);
const result = await cache.importImage(src);
assert.ok(result);
assert.match(result.id, /^[a-f0-9]{32}$/);
assert.equal(result.mimeType, 'image/png');
const cached = path.join(userDataDir, 'bg-images', `${result.id}.png`);
assert.equal(fsModule.existsSync(cached), true);
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('importImage accepts avif and bmp (restored legacy formats)', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const avif = await mkPng(sourceDir, 'bg.avif', Buffer.from('fakeavif'));
const avifResult = await cache.importImage(avif);
assert.ok(avifResult);
assert.equal(avifResult.mimeType, 'image/avif');
const bmp = await mkPng(sourceDir, 'bg.bmp', Buffer.from('fakebmp'));
const bmpResult = await cache.importImage(bmp);
assert.ok(bmpResult);
assert.equal(bmpResult.mimeType, 'image/bmp');
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('importImage rejects svg (scriptable format deliberately excluded)', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const svg = await mkPng(sourceDir, 'bg.svg', Buffer.from('<svg></svg>'));
assert.equal(await cache.importImage(svg), null);
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('importImage returns null for a path inside userData (no laundering)', async () => {
const cache = load();
const evilPath = await mkPng(userDataDir, 'planted.png');
const result = await cache.importImage(evilPath);
assert.equal(result, null);
});
test('importImage returns null for an unsupported extension', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const src = path.join(sourceDir, 'bad.txt');
await fs.writeFile(src, 'hello');
const result = await cache.importImage(src);
assert.equal(result, null);
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('importImage returns null for a file over MAX_IMAGE_BYTES', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
// 6 MB > 5 MB cap
const huge = Buffer.alloc(6 * 1024 * 1024, 0xaa);
const src = await mkPng(sourceDir, 'big.png', huge);
const result = await cache.importImage(src);
assert.equal(result, null);
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('importImage returns null for an empty file', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const src = path.join(sourceDir, 'empty.png');
await fs.writeFile(src, '');
const result = await cache.importImage(src);
assert.equal(result, null);
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('importImage returns null for a non-existent source', async () => {
const cache = load();
const result = await cache.importImage('/does/not/exist/bg.png');
assert.equal(result, null);
});
test('importImage returns null for a non-string input (fail-closed)', async () => {
const cache = load();
assert.equal(await cache.importImage(undefined), null);
assert.equal(await cache.importImage(null), null);
assert.equal(await cache.importImage(42), null);
assert.equal(await cache.importImage(''), null);
});
test('getImageDataUrl returns the data url for a known id', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const src = await mkPng(sourceDir, 'bg.png', Buffer.from('imgbytes'));
const imported = await cache.importImage(src);
assert.ok(imported);
const url = await cache.getImageDataUrl(imported.id);
assert.match(url, /^data:image\/png;base64,/);
const base64 = url.split(',')[1];
assert.equal(Buffer.from(base64, 'base64').toString('utf8'), 'imgbytes');
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('getImageDataUrl returns null for unknown / malformed ids', async () => {
const cache = load();
assert.equal(await cache.getImageDataUrl('not-hex'), null);
assert.equal(await cache.getImageDataUrl(''), null);
// Right shape but never imported:
assert.equal(await cache.getImageDataUrl('a'.repeat(32)), null);
});
test('getImageDataUrl resolves across module reloads (persistence)', async () => {
let cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const src = await mkPng(sourceDir);
const imported = await cache.importImage(src);
assert.ok(imported);
cache = load(); // simulate app restart
const url = await cache.getImageDataUrl(imported.id);
assert.match(url, /^data:image\/png;base64,/);
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('removeCachedImage deletes the file; subsequent getDataUrl is null', async () => {
const cache = load();
const sourceDir = fsModule.realpathSync.native(
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
);
try {
const src = await mkPng(sourceDir);
const imported = await cache.importImage(src);
assert.ok(imported);
await cache.removeCachedImage(imported.id);
assert.equal(await cache.getImageDataUrl(imported.id), null);
} finally {
await fs.rm(sourceDir, { recursive: true, force: true });
}
});
test('removeCachedImage is a no-op for unknown ids', async () => {
const cache = load();
await cache.removeCachedImage('a'.repeat(32));
await cache.removeCachedImage('not-a-valid-id');
// No throw == pass.
});