@@ -2,9 +2,18 @@ import * as system from './system.js'
22import { execa } from 'execa'
33import { describe , expect , test , vi } from 'vitest'
44import which from 'which'
5+ import { Readable } from 'stream'
6+ import * as fs from 'fs'
57
68vi . mock ( 'which' )
79vi . mock ( 'execa' )
10+ vi . mock ( 'fs' , async ( importOriginal ) => {
11+ const actual = await importOriginal < typeof fs > ( )
12+ return {
13+ ...actual ,
14+ fstatSync : vi . fn ( ) ,
15+ }
16+ } )
817
918describe ( 'captureOutput' , ( ) => {
1019 test ( 'runs the command when it is not found in the current directory' , async ( ) => {
@@ -30,3 +39,77 @@ describe('captureOutput', () => {
3039 await expect ( got ) . rejects . toThrowError ( 'Skipped run of unsecure binary command found in the current directory.' )
3140 } )
3241} )
42+
43+ describe ( 'isStdinPiped' , ( ) => {
44+ test ( 'returns true when stdin is a FIFO (pipe)' , ( ) => {
45+ // Given
46+ vi . mocked ( fs . fstatSync ) . mockReturnValue ( { isFIFO : ( ) => true , isFile : ( ) => false } as fs . Stats )
47+
48+ // When
49+ const got = system . isStdinPiped ( )
50+
51+ // Then
52+ expect ( got ) . toBe ( true )
53+ } )
54+
55+ test ( 'returns true when stdin is a file redirect' , ( ) => {
56+ // Given
57+ vi . mocked ( fs . fstatSync ) . mockReturnValue ( { isFIFO : ( ) => false , isFile : ( ) => true } as fs . Stats )
58+
59+ // When
60+ const got = system . isStdinPiped ( )
61+
62+ // Then
63+ expect ( got ) . toBe ( true )
64+ } )
65+
66+ test ( 'returns false when stdin is a TTY (interactive)' , ( ) => {
67+ // Given
68+ vi . mocked ( fs . fstatSync ) . mockReturnValue ( { isFIFO : ( ) => false , isFile : ( ) => false } as fs . Stats )
69+
70+ // When
71+ const got = system . isStdinPiped ( )
72+
73+ // Then
74+ expect ( got ) . toBe ( false )
75+ } )
76+
77+ test ( 'returns false when fstatSync throws (e.g., CI with no stdin)' , ( ) => {
78+ // Given
79+ vi . mocked ( fs . fstatSync ) . mockImplementation ( ( ) => {
80+ throw new Error ( 'EBADF' )
81+ } )
82+
83+ // When
84+ const got = system . isStdinPiped ( )
85+
86+ // Then
87+ expect ( got ) . toBe ( false )
88+ } )
89+ } )
90+
91+ describe ( 'readStdin' , ( ) => {
92+ test ( 'returns undefined when stdin is not piped' , async ( ) => {
93+ // Given
94+ vi . mocked ( fs . fstatSync ) . mockReturnValue ( { isFIFO : ( ) => false , isFile : ( ) => false } as fs . Stats )
95+
96+ // When
97+ const got = await system . readStdin ( )
98+
99+ // Then
100+ expect ( got ) . toBeUndefined ( )
101+ } )
102+
103+ test ( 'returns trimmed content when stdin is piped' , async ( ) => {
104+ // Given
105+ vi . mocked ( fs . fstatSync ) . mockReturnValue ( { isFIFO : ( ) => true , isFile : ( ) => false } as fs . Stats )
106+ const mockStdin = Readable . from ( [ ' hello world ' ] )
107+ vi . spyOn ( process , 'stdin' , 'get' ) . mockReturnValue ( mockStdin as unknown as typeof process . stdin )
108+
109+ // When
110+ const got = await system . readStdin ( )
111+
112+ // Then
113+ expect ( got ) . toBe ( 'hello world' )
114+ } )
115+ } )
0 commit comments