WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage from it! :)
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Test stdin reading in body-input via process mock

Replaces the skipped stdin placeholder with three real tests covering:
- reading a single data chunk from stdin
- concatenating multiple chunks
- propagating stdin error events

process.stdin is non-configurable on the real process object, so the
tests mock the entire node:process module using a Proxy that intercepts
the stdin property and returns a local EventEmitter fixture while
forwarding all other property accesses to the real module.

The readFromStdin Promise constructor registers handlers synchronously,
so events can be emitted immediately after readBodyInput returns its
pending Promise without needing setImmediate.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

+56 -9
+56 -9
tests/utils/body-input.test.ts
··· 1 + import { EventEmitter } from 'node:events'; 1 2 import * as fs from 'node:fs/promises'; 2 3 import * as path from 'node:path'; 3 - import * as process from 'node:process'; 4 - import { afterEach, beforeEach, describe, expect, it } from 'vitest'; 4 + import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; 5 5 import { readBodyInput } from '../../src/utils/body-input.js'; 6 + 7 + // Mutable reference updated per stdin test; null means fall through to real stdin 8 + let currentMockStdin: (EventEmitter & { resume: () => void }) | null = null; 9 + 10 + // Mock node:process so process.stdin can be swapped per test without needing 11 + // to redefine the non-configurable property on the real process object 12 + vi.mock('node:process', async (importOriginal) => { 13 + const actual = await importOriginal<typeof import('node:process')>(); 14 + return new Proxy(actual as object, { 15 + get(target, prop, receiver) { 16 + if (prop === 'stdin' && currentMockStdin !== null) { 17 + return currentMockStdin; 18 + } 19 + return Reflect.get(target, prop, receiver); 20 + }, 21 + }); 22 + }); 6 23 7 24 describe('readBodyInput', () => { 8 25 describe('direct string input', () => { ··· 82 99 }); 83 100 84 101 describe('stdin input', () => { 85 - // Note: Stdin reading is tested via integration tests 86 - // Mocking process.stdin is complex and unreliable in unit tests 87 - // The implementation is straightforward and covered by: 88 - // 1. File I/O tests (same event-driven patterns) 89 - // 2. Integration tests with real stdin 90 - it.skip('stdin reading is tested via integration tests', () => { 91 - // Placeholder to document testing approach 102 + afterEach(() => { 103 + currentMockStdin = null; 104 + }); 105 + 106 + it('should read content from stdin when - is provided', async () => { 107 + const mockStdin = Object.assign(new EventEmitter(), { resume: vi.fn() }); 108 + currentMockStdin = mockStdin; 109 + 110 + // readBodyInput registers handlers synchronously inside the Promise 111 + // constructor before returning, so we can emit immediately after 112 + const readPromise = readBodyInput(undefined, '-'); 113 + mockStdin.emit('data', Buffer.from('hello from stdin')); 114 + mockStdin.emit('end'); 115 + 116 + expect(await readPromise).toBe('hello from stdin'); 117 + }); 118 + 119 + it('should concatenate multiple chunks from stdin', async () => { 120 + const mockStdin = Object.assign(new EventEmitter(), { resume: vi.fn() }); 121 + currentMockStdin = mockStdin; 122 + 123 + const readPromise = readBodyInput(undefined, '-'); 124 + mockStdin.emit('data', Buffer.from('chunk1')); 125 + mockStdin.emit('data', Buffer.from(' chunk2')); 126 + mockStdin.emit('end'); 127 + 128 + expect(await readPromise).toBe('chunk1 chunk2'); 129 + }); 130 + 131 + it('should throw when stdin emits an error', async () => { 132 + const mockStdin = Object.assign(new EventEmitter(), { resume: vi.fn() }); 133 + currentMockStdin = mockStdin; 134 + 135 + const readPromise = readBodyInput(undefined, '-'); 136 + mockStdin.emit('error', new Error('read error')); 137 + 138 + await expect(readPromise).rejects.toThrow('Failed to read from stdin: read error'); 92 139 }); 93 140 }); 94 141