mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
167 lines
6.1 KiB
JavaScript
167 lines
6.1 KiB
JavaScript
import { test, describe, before, after, beforeEach, afterEach, mock } from "node:test";
|
|
import assert from "node:assert";
|
|
import "../test-helpers/setup-localstorage.mjs";
|
|
import * as attachmentService from "../../js/services/attachmentService.js";
|
|
|
|
// Mock URL.createObjectURL and revokeObjectURL
|
|
const originalURL = globalThis.URL;
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
describe("attachmentService", () => {
|
|
before(() => {
|
|
if (!globalThis.URL) {
|
|
globalThis.URL = {};
|
|
}
|
|
});
|
|
|
|
beforeEach(() => {
|
|
globalThis.URL.createObjectURL = mock.fn(() => "blob:mock-url");
|
|
globalThis.URL.revokeObjectURL = mock.fn();
|
|
globalThis.fetch = mock.fn();
|
|
|
|
// Clear cache before each test to ensure isolation,
|
|
// but we need to be careful not to trigger revokeObjectURL calls that we count?
|
|
// Actually, we should just clear it.
|
|
attachmentService.clearAttachmentCache();
|
|
// Then reset calls
|
|
globalThis.URL.revokeObjectURL.mock.resetCalls();
|
|
});
|
|
|
|
after(() => {
|
|
if (originalURL) {
|
|
globalThis.URL = originalURL;
|
|
} else {
|
|
delete globalThis.URL;
|
|
}
|
|
if (originalFetch) {
|
|
globalThis.fetch = originalFetch;
|
|
} else {
|
|
delete globalThis.fetch;
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
mock.reset();
|
|
attachmentService.clearAttachmentCache();
|
|
});
|
|
|
|
describe("prepareAttachmentUpload", () => {
|
|
test("should prepare upload without encryption", async () => {
|
|
const file = new File(["test content"], "test.txt", { type: "text/plain" });
|
|
const result = await attachmentService.prepareAttachmentUpload({ file, encrypt: false });
|
|
|
|
assert.strictEqual(result.name, "test.txt");
|
|
assert.strictEqual(result.type, "text/plain");
|
|
assert.strictEqual(result.size, 12);
|
|
assert.strictEqual(result.encrypted, false);
|
|
assert.strictEqual(result.key, "");
|
|
// sha256 of "test content"
|
|
assert.strictEqual(result.sha256, "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72");
|
|
});
|
|
|
|
test("should prepare upload with encryption", async () => {
|
|
const file = new File(["test content"], "test.txt", { type: "text/plain" });
|
|
const result = await attachmentService.prepareAttachmentUpload({ file, encrypt: true });
|
|
|
|
assert.strictEqual(result.name, "test.txt");
|
|
assert.strictEqual(result.type, "text/plain"); // Metadata preserves original type
|
|
assert.strictEqual(result.encrypted, true);
|
|
assert.ok(result.key.length > 0, "Should return a key");
|
|
assert.notStrictEqual(result.uploadBlob, file, "Should return a new blob");
|
|
assert.strictEqual(result.uploadBlob.type, "application/octet-stream"); // Actual blob is generic binary
|
|
});
|
|
});
|
|
|
|
describe("downloadAttachment", () => {
|
|
test("should download attachment", async () => {
|
|
const mockResponse = {
|
|
ok: true,
|
|
blob: async () => new Blob(["test content"]),
|
|
headers: new Map(),
|
|
};
|
|
globalThis.fetch.mock.mockImplementation(async () => mockResponse);
|
|
|
|
const result = await attachmentService.downloadAttachment({ url: "https://example.com/file" });
|
|
|
|
assert.strictEqual(globalThis.fetch.mock.callCount(), 1);
|
|
assert.strictEqual(result.objectUrl, "blob:mock-url");
|
|
assert.strictEqual(result.size, 12);
|
|
});
|
|
|
|
test("should verify hash", async () => {
|
|
const mockResponse = {
|
|
ok: true,
|
|
blob: async () => new Blob(["test content"]),
|
|
headers: new Map(),
|
|
};
|
|
globalThis.fetch.mock.mockImplementation(async () => mockResponse);
|
|
|
|
const expectedHash = "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72";
|
|
await attachmentService.downloadAttachment({ url: "https://example.com/file", expectedHash });
|
|
|
|
// Should not throw
|
|
});
|
|
|
|
test("should throw on hash mismatch", async () => {
|
|
const mockResponse = {
|
|
ok: true,
|
|
blob: async () => new Blob(["test content"]),
|
|
headers: new Map(),
|
|
};
|
|
globalThis.fetch.mock.mockImplementation(async () => mockResponse);
|
|
|
|
const expectedHash = "wronghash";
|
|
|
|
await assert.rejects(
|
|
async () => {
|
|
await attachmentService.downloadAttachment({ url: "https://example.com/file", expectedHash });
|
|
},
|
|
/Attachment hash mismatch/
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("caching", () => {
|
|
test("should cache downloaded attachments", async () => {
|
|
const mockResponse = {
|
|
ok: true,
|
|
blob: async () => new Blob(["test content"]),
|
|
headers: new Map(),
|
|
};
|
|
globalThis.fetch.mock.mockImplementation(async () => mockResponse);
|
|
|
|
const expectedHash = "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72";
|
|
|
|
// First download
|
|
await attachmentService.downloadAttachment({ url: "https://example.com/file1", expectedHash });
|
|
assert.strictEqual(globalThis.fetch.mock.callCount(), 1);
|
|
|
|
// Second download (should be cached)
|
|
const result = await attachmentService.downloadAttachment({ url: "https://example.com/file1", expectedHash });
|
|
// Fetch should NOT be called again
|
|
assert.strictEqual(globalThis.fetch.mock.callCount(), 1);
|
|
assert.strictEqual(result.cached, true);
|
|
});
|
|
|
|
test("should clear cache", async () => {
|
|
const mockResponse = {
|
|
ok: true,
|
|
blob: async () => new Blob(["test content"]),
|
|
headers: new Map(),
|
|
};
|
|
globalThis.fetch.mock.mockImplementation(async () => mockResponse);
|
|
const expectedHash = "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72";
|
|
|
|
await attachmentService.downloadAttachment({ url: "https://example.com/file", expectedHash });
|
|
assert.strictEqual(attachmentService.getAttachmentCacheStats().size, 1);
|
|
|
|
// Clear call count for revokeObjectURL before clearing cache to isolate the count
|
|
globalThis.URL.revokeObjectURL.mock.resetCalls();
|
|
|
|
attachmentService.clearAttachmentCache();
|
|
assert.strictEqual(attachmentService.getAttachmentCacheStats().size, 0);
|
|
assert.strictEqual(globalThis.URL.revokeObjectURL.mock.callCount(), 1);
|
|
});
|
|
});
|
|
});
|