1+ import fs from 'node:fs/promises'
2+ import os from 'node:os'
3+ import path from 'node:path'
14import { ApiError } from '@deepnote/database-integrations'
25import { Command } from 'commander'
36import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
@@ -94,6 +97,46 @@ describe('notebooks rename command', () => {
9497 )
9598 } )
9699
100+ it ( 'reads the token from a .env file in the working directory' , async ( ) => {
101+ const dir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'deepnote-rename-' ) )
102+ try {
103+ await fs . writeFile ( path . join ( dir , '.env' ) , 'DEEPNOTE_TOKEN=dotenv-token\n' )
104+ vi . spyOn ( process , 'cwd' ) . mockReturnValue ( dir )
105+
106+ await createNotebooksRenameAction ( new Command ( ) ) ( NOTEBOOK_ID , 'Renamed' , options ( { token : undefined } ) )
107+
108+ expect ( cloudMock . updateNotebook ) . toHaveBeenCalledWith (
109+ expect . any ( String ) ,
110+ 'dotenv-token' ,
111+ NOTEBOOK_ID ,
112+ expect . anything ( )
113+ )
114+ expect ( process . exitCode ) . toBeUndefined ( )
115+ } finally {
116+ await fs . rm ( dir , { recursive : true , force : true } )
117+ }
118+ } )
119+
120+ it ( 'prefers a real environment variable over the .env file' , async ( ) => {
121+ const dir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'deepnote-rename-' ) )
122+ try {
123+ await fs . writeFile ( path . join ( dir , '.env' ) , 'DEEPNOTE_TOKEN=dotenv-token\n' )
124+ vi . spyOn ( process , 'cwd' ) . mockReturnValue ( dir )
125+ process . env . DEEPNOTE_TOKEN = 'env-token'
126+
127+ await createNotebooksRenameAction ( new Command ( ) ) ( NOTEBOOK_ID , 'Renamed' , options ( { token : undefined } ) )
128+
129+ expect ( cloudMock . updateNotebook ) . toHaveBeenCalledWith (
130+ expect . any ( String ) ,
131+ 'env-token' ,
132+ NOTEBOOK_ID ,
133+ expect . anything ( )
134+ )
135+ } finally {
136+ await fs . rm ( dir , { recursive : true , force : true } )
137+ }
138+ } )
139+
97140 it ( 'emits the stable JSON contract on success' , async ( ) => {
98141 await createNotebooksRenameAction ( new Command ( ) ) ( NOTEBOOK_ID , 'Renamed' , options ( { output : 'json' } ) )
99142
@@ -117,11 +160,19 @@ describe('notebooks rename command', () => {
117160 } )
118161
119162 it ( 'exits with invalid usage when no token is available' , async ( ) => {
120- await createNotebooksRenameAction ( new Command ( ) ) ( NOTEBOOK_ID , 'Renamed' , options ( { token : undefined } ) )
121-
122- expect ( cloudMock . updateNotebook ) . not . toHaveBeenCalled ( )
123- expect ( process . exitCode ) . toEqual ( ExitCode . InvalidUsage )
124- expect ( errorSpy . mock . calls . flat ( ) . join ( '\n' ) ) . not . toEqual ( '' )
163+ // Run from an empty directory so a developer's own .env cannot supply a token.
164+ const dir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'deepnote-rename-' ) )
165+ try {
166+ vi . spyOn ( process , 'cwd' ) . mockReturnValue ( dir )
167+
168+ await createNotebooksRenameAction ( new Command ( ) ) ( NOTEBOOK_ID , 'Renamed' , options ( { token : undefined } ) )
169+
170+ expect ( cloudMock . updateNotebook ) . not . toHaveBeenCalled ( )
171+ expect ( process . exitCode ) . toEqual ( ExitCode . InvalidUsage )
172+ expect ( errorSpy . mock . calls . flat ( ) . join ( '\n' ) ) . not . toEqual ( '' )
173+ } finally {
174+ await fs . rm ( dir , { recursive : true , force : true } )
175+ }
125176 } )
126177
127178 it ( 'treats API conflicts as invalid usage and surfaces the server message' , async ( ) => {
0 commit comments