@@ -14,7 +14,8 @@ use crate::services::config;
1414use crate :: services:: error:: { CliError , UserError } ;
1515use crate :: services:: output_format:: OutputFormat ;
1616use crate :: services:: style:: { label, prompt_label, prompt_value, success, value} ;
17- use crate :: services:: token_storage:: { self , StoredTokens } ;
17+ use crate :: services:: telemetry_credential_storage;
18+ use crate :: services:: token_storage:: { self , StoredTokens , TokenStorageError } ;
1819
1920pub const NAME : & str = "auth" ;
2021
@@ -84,10 +85,29 @@ pub async fn run_login(format: AuthFormat) -> Result<String, CliError> {
8485}
8586
8687pub async fn run_logout ( format : AuthFormat ) -> Result < String , CliError > {
87- let deleted = token_storage:: delete_tokens ( )
88- . await
89- . map_err ( auth_storage_error) ?;
90- render_logout_result ( deleted, format) . map_err ( unexpected_auth_command_error)
88+ run_logout_with (
89+ format,
90+ token_storage:: delete_tokens ( ) ,
91+ telemetry_credential_storage:: delete_telemetry_credential ( ) ,
92+ )
93+ . await
94+ }
95+
96+ async fn run_logout_with < W , T > (
97+ format : AuthFormat ,
98+ delete_workos : W ,
99+ delete_telemetry : T ,
100+ ) -> Result < String , CliError >
101+ where
102+ W : Future < Output = Result < bool , TokenStorageError > > ,
103+ T : Future < Output = Result < bool , TokenStorageError > > ,
104+ {
105+ let workos_result = delete_workos. await ;
106+ let telemetry_result = delete_telemetry. await ;
107+ let workos_deleted = workos_result. map_err ( auth_storage_error) ?;
108+ let telemetry_deleted = telemetry_result. map_err ( auth_storage_error) ?;
109+ render_logout_result ( workos_deleted || telemetry_deleted, format)
110+ . map_err ( unexpected_auth_command_error)
91111}
92112
93113pub async fn run_whoami ( format : AuthFormat ) -> Result < String , CliError > {
@@ -443,10 +463,57 @@ fn map_whoami_control_plane_error(error: &ControlPlaneError) -> CliError {
443463 )
444464}
445465
446- fn auth_storage_error ( error : crate :: services :: token_storage :: TokenStorageError ) -> CliError {
466+ fn auth_storage_error ( error : TokenStorageError ) -> CliError {
447467 CliError :: user_with_source ( UserError :: AuthStorageUnavailable , error)
448468}
449469
450470fn unexpected_auth_command_error ( error : anyhow:: Error ) -> CliError {
451471 CliError :: user_with_source ( UserError :: UnexpectedFailure , error)
452472}
473+
474+ #[ cfg( test) ]
475+ mod tests {
476+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
477+
478+ use super :: * ;
479+
480+ fn storage_failure ( ) -> TokenStorageError {
481+ TokenStorageError :: Database ( "failed" . to_string ( ) )
482+ }
483+
484+ #[ tokio:: test]
485+ async fn telemetry_credential_logout_reports_removal_when_only_telemetry_deleted ( ) {
486+ let output = run_logout_with ( AuthFormat :: Json , async { Ok ( false ) } , async { Ok ( true ) } )
487+ . await
488+ . expect ( "logout should succeed" ) ;
489+ assert ! ( output. contains( "\" credentials_removed\" : true" ) ) ;
490+ }
491+
492+ #[ tokio:: test]
493+ async fn telemetry_credential_logout_attempts_both_deletions_on_telemetry_failure ( ) {
494+ let workos_called = AtomicBool :: new ( false ) ;
495+ let result = run_logout_with (
496+ AuthFormat :: Json ,
497+ async {
498+ workos_called. store ( true , Ordering :: SeqCst ) ;
499+ Ok ( true )
500+ } ,
501+ async { Err ( storage_failure ( ) ) } ,
502+ )
503+ . await ;
504+ assert ! ( result. is_err( ) ) ;
505+ assert ! ( workos_called. load( Ordering :: SeqCst ) ) ;
506+ }
507+
508+ #[ tokio:: test]
509+ async fn telemetry_credential_logout_deletes_telemetry_on_workos_failure ( ) {
510+ let telemetry_called = AtomicBool :: new ( false ) ;
511+ let result = run_logout_with ( AuthFormat :: Json , async { Err ( storage_failure ( ) ) } , async {
512+ telemetry_called. store ( true , Ordering :: SeqCst ) ;
513+ Ok ( true )
514+ } )
515+ . await ;
516+ assert ! ( result. is_err( ) ) ;
517+ assert ! ( telemetry_called. load( Ordering :: SeqCst ) ) ;
518+ }
519+ }
0 commit comments