@@ -108,7 +108,9 @@ describe('scanUsage', () => {
108108 vi . mocked ( determineComparisonFile ) . mockResolvedValue ( { type : 'none' } ) ;
109109 vi . mocked ( printScanResult ) . mockReturnValue ( { exitWithError : false } ) ;
110110 vi . mocked ( printMissingExample ) . mockReturnValue ( false ) ;
111- vi . mocked ( promptNoEnvScenario ) . mockResolvedValue ( { compareFile : undefined } ) ;
111+ vi . mocked ( promptNoEnvScenario ) . mockResolvedValue ( {
112+ compareFile : undefined ,
113+ } ) ;
112114 } ) ;
113115
114116 it ( 'returns early when example missing in CI mode' , async ( ) => {
@@ -158,6 +160,24 @@ describe('scanUsage', () => {
158160 expect ( result . exitWithError ) . toBe ( true ) ;
159161 } ) ;
160162
163+ it ( 'does not return error in JSON mode for non-high example warnings when strict is false' , async ( ) => {
164+ vi . mocked ( scanCodebase ) . mockResolvedValue ( {
165+ ...baseScanResult ,
166+ exampleWarnings : [
167+ {
168+ key : 'EXAMPLE_KEY' ,
169+ value : 'placeholder-but-flagged' ,
170+ reason : 'Entropy' ,
171+ severity : 'medium' ,
172+ } ,
173+ ] ,
174+ } as any ) ;
175+
176+ const result = await scanUsage ( { ...baseOpts , json : true , strict : false } ) ;
177+
178+ expect ( result . exitWithError ) . toBe ( false ) ;
179+ } ) ;
180+
161181 it ( 'returns strict error in JSON mode when strict violations exist' , async ( ) => {
162182 vi . mocked ( scanCodebase ) . mockResolvedValue ( {
163183 ...baseScanResult ,
@@ -173,40 +193,56 @@ describe('scanUsage', () => {
173193 expect ( result . exitWithError ) . toBe ( true ) ;
174194 } ) ;
175195
176- it ( 'skips prompt when type is none and isCiMode is true' , async ( ) => {
177- vi . mocked ( determineComparisonFile ) . mockResolvedValue ( { type : 'none' } ) ;
196+ it ( 'returns error in JSON mode when expiration warning is urgent (<=7 days)' , async ( ) => {
197+ vi . mocked ( scanCodebase ) . mockResolvedValue ( {
198+ ...baseScanResult ,
199+ expireWarnings : [ { key : 'TOKEN' , date : '2026-03-10' , daysLeft : 7 } ] ,
200+ } ) ;
178201
179- const result = await scanUsage ( { ...baseOpts , isCiMode : true } ) ;
202+ const result = await scanUsage ( { ...baseOpts , json : true , strict : false } ) ;
180203
181- expect ( promptNoEnvScenario ) . not . toHaveBeenCalled ( ) ;
182- expect ( result . exitWithError ) . toBe ( false ) ;
183- } ) ;
204+ expect ( result . exitWithError ) . toBe ( true ) ;
205+ } ) ;
184206
185- it ( 'skips prompt when type is none and json is true' , async ( ) => {
186- vi . mocked ( determineComparisonFile ) . mockResolvedValue ( { type : 'none' } ) ;
207+ it ( 'skips prompt when type is none and isCiMode is true' , async ( ) => {
208+ vi . mocked ( determineComparisonFile ) . mockResolvedValue ( { type : 'none' } ) ;
187209
188- const result = await scanUsage ( { ...baseOpts , json : true } ) ;
210+ const result = await scanUsage ( { ...baseOpts , isCiMode : true } ) ;
189211
190- expect ( promptNoEnvScenario ) . not . toHaveBeenCalled ( ) ;
191- expect ( result . exitWithError ) . toBe ( false ) ;
192- } ) ;
212+ expect ( promptNoEnvScenario ) . not . toHaveBeenCalled ( ) ;
213+ expect ( result . exitWithError ) . toBe ( false ) ;
214+ } ) ;
193215
194- it ( 'calls promptNoEnvScenario when type is none and not CI/json' , async ( ) => {
195- vi . mocked ( promptNoEnvScenario ) . mockResolvedValue ( {
196- compareFile : { path : '/env/.env' , name : '.env' } ,
216+ it ( 'skips prompt when type is none and json is true' , async ( ) => {
217+ vi . mocked ( determineComparisonFile ) . mockResolvedValue ( { type : 'none' } ) ;
218+
219+ const result = await scanUsage ( { ...baseOpts , json : true } ) ;
220+
221+ expect ( promptNoEnvScenario ) . not . toHaveBeenCalled ( ) ;
222+ expect ( result . exitWithError ) . toBe ( false ) ;
197223 } ) ;
198- vi . mocked ( determineComparisonFile ) . mockResolvedValue ( { type : 'none' } ) ;
199- vi . mocked ( processComparisonFile ) . mockReturnValue ( {
200- scanResult : { ...baseScanResult } ,
201- comparedAgainst : '.env' ,
202- fix : { fixApplied : false , removedDuplicates : [ ] , addedEnv : [ ] , gitignoreUpdated : false } ,
203- } as any ) ;
204224
205- await scanUsage ( { ...baseOpts , isCiMode : false , json : false } ) ;
225+ it ( 'calls promptNoEnvScenario when type is none and not CI/json' , async ( ) => {
226+ vi . mocked ( promptNoEnvScenario ) . mockResolvedValue ( {
227+ compareFile : { path : '/env/.env' , name : '.env' } ,
228+ } ) ;
229+ vi . mocked ( determineComparisonFile ) . mockResolvedValue ( { type : 'none' } ) ;
230+ vi . mocked ( processComparisonFile ) . mockReturnValue ( {
231+ scanResult : { ...baseScanResult } ,
232+ comparedAgainst : '.env' ,
233+ fix : {
234+ fixApplied : false ,
235+ removedDuplicates : [ ] ,
236+ addedEnv : [ ] ,
237+ gitignoreUpdated : false ,
238+ } ,
239+ } as any ) ;
240+
241+ await scanUsage ( { ...baseOpts , isCiMode : false , json : false } ) ;
206242
207- expect ( promptNoEnvScenario ) . toHaveBeenCalled ( ) ;
208- expect ( processComparisonFile ) . toHaveBeenCalled ( ) ;
209- } ) ;
243+ expect ( promptNoEnvScenario ) . toHaveBeenCalled ( ) ;
244+ expect ( processComparisonFile ) . toHaveBeenCalled ( ) ;
245+ } ) ;
210246
211247 it ( 'sets frameworkWarnings on scanResult when frameworkValidator returns results' , async ( ) => {
212248 const { frameworkValidator } =
0 commit comments