@bsv/sdk
Version:
BSV Blockchain Software Development Kit
163 lines • 7.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const StorageUploader_js_1 = require("../StorageUploader.js");
const StorageUtils = __importStar(require("../StorageUtils.js"));
const WalletClient_js_1 = __importDefault(require("../../wallet/WalletClient.js"));
/**
* A helper for converting a string to a number[] of UTF-8 bytes
*/
function stringToUtf8Array(str) {
return Array.from(new TextEncoder().encode(str));
}
describe('StorageUploader Tests', () => {
let uploader;
let walletClient;
// We'll have TWO spies:
let authFetchSpy;
let globalFetchSpy;
beforeEach(() => {
walletClient = new WalletClient_js_1.default('json-api', 'non-admin.com');
uploader = new StorageUploader_js_1.StorageUploader({
storageURL: 'https://example.test.system',
wallet: walletClient
});
// 1) Spy on the "authFetch.fetch" calls for /find, /list, /renew
authFetchSpy = jest
.spyOn(uploader['authFetch'], 'fetch')
.mockResolvedValue(new Response(null, { status: 200 }));
// 2) Spy on the global "fetch" calls for file upload (uploadFile)
globalFetchSpy = jest
.spyOn(global, 'fetch')
.mockResolvedValue(new Response(null, { status: 200 }));
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should upload a file, produce a valid UHRP URL, and decode it to the known SHA-256', async () => {
const data = stringToUtf8Array('Hello, world!');
// Mock out getUploadInfo so we can control the returned upload/public URLs
jest.spyOn(uploader, 'getUploadInfo').mockResolvedValue({
uploadURL: 'https://example-upload.com/put'
});
const result = await uploader.publishFile({
file: { data, type: 'text/plain' },
retentionPeriod: 7
});
// This direct upload uses global.fetch, not authFetch
expect(globalFetchSpy).toHaveBeenCalledTimes(1);
// Check the result
expect(StorageUtils.isValidURL(result.uhrpURL)).toBe(true);
expect(result.published).toBe(true);
const url = StorageUtils.getHashFromURL(result.uhrpURL);
const firstFour = url.slice(0, 4)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
expect(firstFour).toHaveLength(8);
});
it('should throw if the upload fails with HTTP 500', async () => {
// Force the direct upload (global fetch) to fail
globalFetchSpy.mockResolvedValueOnce(new Response(null, { status: 500 }));
// Also mock getUploadInfo
jest.spyOn(uploader, 'getUploadInfo').mockResolvedValue({
uploadURL: 'https://example-upload.com/put'
});
const failingData = stringToUtf8Array('failing data');
await expect(uploader.publishFile({
file: { data: failingData, type: 'text/plain' },
retentionPeriod: 30
})).rejects.toThrow('File upload failed: HTTP 500');
});
it('should find a file and return metadata', async () => {
// This route goes through authFetch, not global fetch
authFetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({
status: 'success',
data: {
name: 'cdn/abc123',
size: '1024',
mimeType: 'text/plain',
expiryTime: 123456
}
}), { status: 200 }));
const fileData = await uploader.findFile('uhrp://some-hash');
expect(authFetchSpy).toHaveBeenCalledTimes(1);
expect(fileData.name).toBe('cdn/abc123');
expect(fileData.size).toBe('1024');
expect(fileData.mimeType).toBe('text/plain');
expect(fileData.expiryTime).toBe(123456);
});
it('should throw an error if findFile returns an error status', async () => {
authFetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({ status: 'error', code: 'ERR_NOT_FOUND', description: 'File not found' }), { status: 200 }));
await expect(uploader.findFile('uhrp://unknown-hash'))
.rejects
.toThrow('findFile returned an error: ERR_NOT_FOUND - File not found');
});
it('should list user uploads successfully', async () => {
// /list uses authFetch
const mockUploads = [
{ uhrpUrl: 'uhrp://hash1', expiryTime: 111111 },
{ uhrpUrl: 'uhrp://hash2', expiryTime: 222222 }
];
authFetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({ status: 'success', uploads: mockUploads }), { status: 200 }));
const result = await uploader.listUploads();
expect(authFetchSpy).toHaveBeenCalledTimes(1);
expect(result).toEqual(mockUploads);
});
it('should throw an error if listUploads returns an error', async () => {
authFetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({ status: 'error', code: 'ERR_INTERNAL', description: 'Something broke' }), { status: 200 }));
await expect(uploader.listUploads()).rejects.toThrow('listUploads returned an error: ERR_INTERNAL - Something broke');
});
it('should renew a file and return the new expiry info', async () => {
// /renew uses authFetch
authFetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({
status: 'success',
prevExpiryTime: 123,
newExpiryTime: 456,
amount: 99
}), { status: 200 }));
const renewal = await uploader.renewFile('uhrp://some-hash', 30);
expect(authFetchSpy).toHaveBeenCalledTimes(1);
expect(renewal.status).toBe('success');
expect(renewal.prevExpiryTime).toBe(123);
expect(renewal.newExpiryTime).toBe(456);
expect(renewal.amount).toBe(99);
});
it('should throw an error if renewFile returns error status JSON', async () => {
authFetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({ status: 'error', code: 'ERR_CANT_RENEW', description: 'Failed to renew' }), { status: 200 }));
await expect(uploader.renewFile('uhrp://some-other-hash', 15))
.rejects
.toThrow('renewFile returned an error: ERR_CANT_RENEW - Failed to renew');
});
it('should throw if renewFile request fails with non-200 status', async () => {
authFetchSpy.mockResolvedValueOnce(new Response(null, { status: 404 }));
await expect(uploader.renewFile('uhrp://ghost', 10))
.rejects
.toThrow('renewFile request failed: HTTP 404');
});
});
//# sourceMappingURL=StorageUploader.test.js.map