@@ -5,6 +5,7 @@ import { emptyUpdateInstallState } from '#/cli/update/install-state';
55import type { UpdateInstallLockHandle } from '#/cli/update/install-lock' ;
66import type { InstallPromptChoiceValue } from '#/cli/update/prompt' ;
77import type { InstallSource , UpdateCache , UpdateInstallState } from '#/cli/update/types' ;
8+ import type { InstallVerification } from '#/cli/update/verify-install' ;
89
910function cacheWith (
1011 version : string | null ,
@@ -69,7 +70,11 @@ function createDeps(overrides: {
6970 readonly source ?: InstallSource ;
7071 readonly isInteractive ?: boolean ;
7172 readonly promptForInstallChoice ?: ( ) => Promise < InstallPromptChoiceValue > ;
72- readonly installUpdate ?: ( source : InstallSource , version : string , platform : NodeJS . Platform ) => Promise < void > ;
73+ readonly installUpdate ?: (
74+ source : InstallSource ,
75+ version : string ,
76+ platform : NodeJS . Platform ,
77+ ) => Promise < InstallVerification > ;
7378 readonly readUpdateInstallState ?: ( ) => Promise < UpdateInstallState > ;
7479 readonly writeUpdateInstallState ?: ( state : UpdateInstallState ) => Promise < void > ;
7580 readonly tryAcquireUpdateInstallLock ?: ( ) => Promise < UpdateInstallLockHandle | null > ;
@@ -80,7 +85,7 @@ function createDeps(overrides: {
8085 source : InstallSource ,
8186 version : string ,
8287 platform : NodeJS . Platform ,
83- ) => Promise < void > > ( ) . mockResolvedValue ( undefined ) ;
88+ ) => Promise < InstallVerification > > ( ) . mockResolvedValue ( { ok : true } ) ;
8489
8590 return {
8691 refreshUpdateCache : vi
@@ -212,6 +217,29 @@ describe('handleUpgrade', () => {
212217 expect ( stdout . join ( '' ) ) . toContain ( 'To update manually, run: npm install -g @pythoughts/pythinker-code@0.5.0' ) ;
213218 } ) ;
214219
220+ it ( 'records why a manual install could not be verified' , async ( ) => {
221+ const { writable } = captureOutput ( ) ;
222+ const writeUpdateInstallState = vi . fn ( ) . mockResolvedValue ( undefined ) ;
223+ const deps = createDeps ( {
224+ latest : '0.5.0' ,
225+ source : 'native' ,
226+ installUpdate : vi . fn ( ) . mockResolvedValue ( {
227+ ok : true ,
228+ unverified : '/usr/local/bin/pythinker could not be run: ETIMEDOUT' ,
229+ } ) ,
230+ writeUpdateInstallState,
231+ } ) ;
232+
233+ await expect ( handleUpgrade ( '0.4.0' , { ...deps , ...writable } ) ) . resolves . toBe ( 0 ) ;
234+
235+ expect ( writeUpdateInstallState ) . toHaveBeenCalledWith ( expect . objectContaining ( {
236+ lastSuccess : expect . objectContaining ( {
237+ version : '0.5.0' ,
238+ unverified : expect . stringContaining ( 'ETIMEDOUT' ) ,
239+ } ) ,
240+ } ) ) ;
241+ } ) ;
242+
215243 it ( 'returns a failing exit code when the foreground install fails' , async ( ) => {
216244 const { stderr, writable } = captureOutput ( ) ;
217245 const deps = createDeps ( {
0 commit comments