Skip to content

Commit 58ac02b

Browse files
authored
Use non-blocking IO in StorageObject (#651)
1 parent 4c7b069 commit 58ac02b

2 files changed

Lines changed: 18 additions & 20 deletions

File tree

src/sdk/storage-object.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
ObjectDownloadURLView,
77
ObjectListParams,
88
} from '../resources/objects';
9-
import * as fs from 'node:fs';
9+
import * as fs from 'node:fs/promises';
1010
import * as path from 'node:path';
1111

1212
// Extract the content type from the API types
@@ -293,7 +293,7 @@ export class StorageObject {
293293

294294
// Check if file exists and get stats
295295
try {
296-
const stats = fs.statSync(filePath);
296+
const stats = await fs.stat(filePath);
297297
if (!stats.isFile()) {
298298
throw new Error(`Path is not a file: ${filePath}`);
299299
}
@@ -306,7 +306,7 @@ export class StorageObject {
306306
// Read file content immediately to fail fast if file doesn't exist
307307
let fileBuffer: Buffer;
308308
try {
309-
fileBuffer = fs.readFileSync(filePath);
309+
fileBuffer = await fs.readFile(filePath);
310310
} catch (error) {
311311
throw new Error(
312312
`Failed to read file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`,

tests/objects/storage-object.test.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ jest.mock('../../src/index');
88
(global as any).fetch = jest.fn();
99

1010
// Mock fs and path modules
11-
jest.mock('node:fs', () => ({
12-
statSync: jest.fn(),
13-
readFileSync: jest.fn(),
11+
jest.mock('node:fs/promises', () => ({
12+
stat: jest.fn(),
13+
readFile: jest.fn(),
1414
}));
1515

1616
jest.mock('node:path', () => ({
@@ -29,7 +29,7 @@ describe('StorageObject (New API)', () => {
2929

3030
beforeEach(() => {
3131
// Get mocked modules
32-
mockFs = require('node:fs');
32+
mockFs = require('node:fs/promises');
3333
mockPath = require('node:path');
3434

3535
// Create mock client instance with proper structure
@@ -462,8 +462,8 @@ describe('StorageObject (New API)', () => {
462462

463463
it('should upload a text file with auto-detected content-type', async () => {
464464
const mockFileBuffer = Buffer.from('test content');
465-
mockFs.statSync.mockReturnValue({ isFile: () => true });
466-
mockFs.readFileSync.mockReturnValue(mockFileBuffer);
465+
mockFs.stat.mockResolvedValue({ isFile: () => true });
466+
mockFs.readFile.mockResolvedValue(mockFileBuffer);
467467

468468
const mockObjectData = { id: 'file-123', upload_url: 'https://upload.example.com/file' };
469469
const mockObjectInfo = { ...mockObjectData, name: 'test.txt', state: 'UPLOADING' };
@@ -485,15 +485,15 @@ describe('StorageObject (New API)', () => {
485485
{ name: 'test.txt', content_type: 'text', metadata: null },
486486
undefined,
487487
);
488-
expect(mockFs.readFileSync).toHaveBeenCalledWith('./test.txt');
488+
expect(mockFs.readFile).toHaveBeenCalledWith('./test.txt');
489489
expect(result).toBeInstanceOf(StorageObject);
490490
expect(result.id).toBe('file-123');
491491
});
492492

493493
it('should upload a file with explicit content-type and custom name', async () => {
494494
const mockFileBuffer = Buffer.from('binary content');
495-
mockFs.statSync.mockReturnValue({ isFile: () => true });
496-
mockFs.readFileSync.mockReturnValue(mockFileBuffer);
495+
mockFs.stat.mockResolvedValue({ isFile: () => true });
496+
mockFs.readFile.mockResolvedValue(mockFileBuffer);
497497

498498
const mockObjectData = { id: 'file-456', upload_url: 'https://upload.example.com/file' };
499499
const mockObjectInfo = { ...mockObjectData, name: 'custom.bin', state: 'UPLOADING' };
@@ -535,9 +535,7 @@ describe('StorageObject (New API)', () => {
535535
});
536536

537537
it('should handle file read errors gracefully', async () => {
538-
mockFs.statSync.mockImplementation(() => {
539-
throw new Error('File not found');
540-
});
538+
mockFs.stat.mockRejectedValue(new Error('File not found'));
541539

542540
await expect(
543541
StorageObject.uploadFromFile(mockClient, './nonexistent.txt', 'nonexistent.txt', {}),
@@ -546,8 +544,8 @@ describe('StorageObject (New API)', () => {
546544

547545
it('should handle upload failures gracefully', async () => {
548546
const mockFileBuffer = Buffer.from('test content');
549-
mockFs.statSync.mockReturnValue({ isFile: () => true });
550-
mockFs.readFileSync.mockReturnValue(mockFileBuffer);
547+
mockFs.stat.mockResolvedValue({ isFile: () => true });
548+
mockFs.readFile.mockResolvedValue(mockFileBuffer);
551549

552550
const mockObjectData = { id: 'file-789', upload_url: 'https://upload.example.com/file' };
553551
const mockObjectInfo = { ...mockObjectData, name: 'test.txt', state: 'UPLOADING' };
@@ -568,8 +566,8 @@ describe('StorageObject (New API)', () => {
568566

569567
it('should upload an archive file with auto-detected content-type', async () => {
570568
const mockArchiveBuffer = Buffer.from('compressed archive content');
571-
mockFs.statSync.mockReturnValue({ isFile: () => true });
572-
mockFs.readFileSync.mockReturnValue(mockArchiveBuffer);
569+
mockFs.stat.mockResolvedValue({ isFile: () => true });
570+
mockFs.readFile.mockResolvedValue(mockArchiveBuffer);
573571

574572
const mockObjectData = { id: 'archive-123', upload_url: 'https://upload.example.com/archive' };
575573
const mockObjectInfo = { ...mockObjectData, name: 'project.tar.gz', state: 'UPLOADING' };
@@ -595,7 +593,7 @@ describe('StorageObject (New API)', () => {
595593
{ name: 'test-archive.tar.gz', content_type: 'tgz', metadata: null },
596594
undefined,
597595
);
598-
expect(mockFs.readFileSync).toHaveBeenCalledWith('./files/test-archive.tar.gz');
596+
expect(mockFs.readFile).toHaveBeenCalledWith('./files/test-archive.tar.gz');
599597
expect(result).toBeInstanceOf(StorageObject);
600598
expect(result.id).toBe('archive-123');
601599
});

0 commit comments

Comments
 (0)