Add nonblocking/asyn I/O operations
This commit is contained in:
@@ -1,14 +1,23 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { Readable } from 'node:stream';
|
||||
|
||||
const { mockQuery } = vi.hoisted(() => ({
|
||||
const { mockQuery, mockConnect } = vi.hoisted(() => ({
|
||||
mockQuery: vi.fn(),
|
||||
mockConnect: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../db/index.js', () => ({
|
||||
query: mockQuery,
|
||||
getPool: () => ({ connect: mockConnect }),
|
||||
}));
|
||||
|
||||
import { getAllNotes, getNoteById, createNote, createNotes } from '../db/notes.dao.js';
|
||||
import {
|
||||
getAllNotes,
|
||||
streamAllNotes,
|
||||
getNoteById,
|
||||
createNote,
|
||||
createNotes,
|
||||
} from '../db/notes.dao.js';
|
||||
|
||||
const MOCK_ROWS = [
|
||||
{ id: 'note_001', text: 'Login flow feels confusing', x: 193, y: 191, author: 'user_5', color: 'yellow' },
|
||||
@@ -48,6 +57,58 @@ describe('notes.dao', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('streamAllNotes', () => {
|
||||
const mockClient = (rows) => {
|
||||
const release = vi.fn();
|
||||
const client = {
|
||||
release,
|
||||
query: vi.fn(() => Readable.from(rows, { objectMode: true })),
|
||||
};
|
||||
mockConnect.mockResolvedValue(client);
|
||||
return { client, release };
|
||||
};
|
||||
|
||||
it('should stream rows without buffering them into an array', async () => {
|
||||
mockClient(MOCK_ROWS);
|
||||
|
||||
const stream = await streamAllNotes();
|
||||
const received = [];
|
||||
for await (const row of stream) received.push(row);
|
||||
|
||||
expect(received).toEqual(MOCK_ROWS);
|
||||
});
|
||||
|
||||
it('should release the pooled client once the stream ends', async () => {
|
||||
const { release } = mockClient(MOCK_ROWS);
|
||||
|
||||
const stream = await streamAllNotes();
|
||||
for await (const _row of stream) { /* drain */ }
|
||||
|
||||
expect(release).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should release the pooled client when a consumer destroys the stream early', async () => {
|
||||
const { release } = mockClient(MOCK_ROWS);
|
||||
|
||||
const stream = await streamAllNotes();
|
||||
stream.destroy();
|
||||
await new Promise((resolve) => stream.once('close', resolve));
|
||||
|
||||
expect(release).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should release the pooled client when starting the query throws', async () => {
|
||||
const release = vi.fn();
|
||||
mockConnect.mockResolvedValue({
|
||||
release,
|
||||
query: vi.fn(() => { throw new Error('cursor failed'); }),
|
||||
});
|
||||
|
||||
await expect(streamAllNotes()).rejects.toThrow('cursor failed');
|
||||
expect(release).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNoteById', () => {
|
||||
it('should return a single note when found', async () => {
|
||||
mockQuery.mockResolvedValue({ rows: [MOCK_ROWS[0]] });
|
||||
@@ -109,5 +170,31 @@ describe('notes.dao', () => {
|
||||
expect(sql).toContain('INSERT INTO notes');
|
||||
expect(params).toHaveLength(12);
|
||||
});
|
||||
|
||||
it('should return an empty array without querying when given no notes', async () => {
|
||||
const result = await createNotes([]);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(mockQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should split large inputs into multiple statements under the bind-parameter limit', async () => {
|
||||
const many = Array.from({ length: 2500 }, (_, i) => ({
|
||||
id: `note_${i}`,
|
||||
text: `text ${i}`,
|
||||
author: 'user_1',
|
||||
}));
|
||||
mockQuery.mockImplementation(async (_sql, params) => ({
|
||||
rows: new Array(params.length / 6).fill(null).map((_, i) => ({ i })),
|
||||
}));
|
||||
|
||||
const result = await createNotes(many);
|
||||
|
||||
expect(mockQuery).toHaveBeenCalledTimes(3);
|
||||
expect(result).toHaveLength(2500);
|
||||
for (const [, params] of mockQuery.mock.calls) {
|
||||
expect(params.length).toBeLessThan(65535);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user