@@ -20,6 +20,20 @@ public sealed class ChannelsConfigViewModelTests : IDisposable
2020 private readonly DisposableTempDir _dir = new ( ) ;
2121 private readonly NetclawPaths _paths ;
2222
23+ public static TheoryData < ChannelType , string , string [ ] > ResetConnectionCases { get ; } = new ( )
24+ {
25+ { ChannelType . Slack , "Slack" , [ "Slack.BotToken" , "Slack.AppToken" ] } ,
26+ { ChannelType . Discord , "Discord" , [ "Discord.BotToken" ] } ,
27+ { ChannelType . Mattermost , "Mattermost" , [ "Mattermost.BotToken" ] }
28+ } ;
29+
30+ public static TheoryData < ChannelType > ChannelTypes { get ; } = new ( )
31+ {
32+ ChannelType . Slack ,
33+ ChannelType . Discord ,
34+ ChannelType . Mattermost
35+ } ;
36+
2337 public ChannelsConfigViewModelTests ( )
2438 {
2539 _paths = new NetclawPaths ( _dir . Path ) ;
@@ -291,29 +305,45 @@ public void Rotate_credentials_preserves_blank_secret_and_updates_nonblank_secre
291305 Assert . Equal ( "xapp-test" , ConfigFileHelper . DecryptIfEncrypted ( _paths , appToken ? . ToString ( ) ) ) ;
292306 }
293307
294- [ Fact ]
295- public void Reset_connection_deletes_config_section_and_secrets_on_save ( )
308+ [ Theory ]
309+ [ MemberData ( nameof ( ResetConnectionCases ) ) ]
310+ public void Reset_connection_deletes_config_section_and_secrets_immediately (
311+ ChannelType type ,
312+ string configSection ,
313+ string [ ] secretPaths )
296314 {
297- WriteChannelConfig ( ) ;
298- WriteChannelSecrets ( ) ;
315+ WriteAllChannelConfig ( ) ;
316+ WriteAllChannelSecrets ( ) ;
299317 using var vm = CreateViewModel ( ) ;
300- vm . OpenAdapterManagement ( ChannelType . Slack ) ;
301- var resetIndex = vm . GetManagementMenuItems ( )
302- . Select ( ( item , index ) => ( item , index ) )
303- . Single ( entry => entry . item . Action == ChannelsManagementAction . ResetConnection )
304- . index ;
305- vm . MoveManagementMenu ( resetIndex ) ;
306- vm . ActivateManagementMenuItem ( ) ;
307- vm . MoveResetConfirmation ( 1 ) ;
308318
309- vm . ApplyResetConfirmation ( ) ;
310- vm . Save ( ) ;
319+ ConfirmReset ( vm , type ) ;
311320
312321 var config = ConfigFileHelper . LoadJsonDict ( _paths . NetclawConfigPath ) ;
313- Assert . False ( ConfigFileHelper . TryGetPathValue ( config , "Slack" , out _ ) ) ;
322+ Assert . False ( ConfigFileHelper . TryGetPathValue ( config , configSection , out _ ) ) ;
314323 var secrets = ConfigFileHelper . LoadJsonDict ( _paths . SecretsPath ) ;
315- Assert . False ( ConfigFileHelper . TryGetPathValue ( secrets , "Slack.BotToken" , out _ ) ) ;
316- Assert . False ( ConfigFileHelper . TryGetPathValue ( secrets , "Slack.AppToken" , out _ ) ) ;
324+ foreach ( var secretPath in secretPaths )
325+ Assert . False ( ConfigFileHelper . TryGetPathValue ( secrets , secretPath , out _ ) ) ;
326+ Assert . True ( vm . IsSaved . Value ) ;
327+ Assert . Equal ( $ "{ type } reset saved.", vm . Status . Value . Text ) ;
328+ }
329+
330+ [ Theory ]
331+ [ MemberData ( nameof ( ChannelTypes ) ) ]
332+ public void Reset_connection_survives_reopening_channels_editor_without_outer_save (
333+ ChannelType type )
334+ {
335+ WriteAllChannelConfig ( ) ;
336+ WriteAllChannelSecrets ( ) ;
337+ using ( var vm = CreateViewModel ( ) )
338+ {
339+ ConfirmReset ( vm , type ) ;
340+ }
341+
342+ using var reopened = CreateViewModel ( ) ;
343+
344+ Assert . False ( reopened . Step . IsAdapterKnown ( type ) ) ;
345+ Assert . False ( reopened . Step . IsAdapterEnabled ( type ) ) ;
346+ Assert . Null ( reopened . Step . GetAdapterSummary ( GetAdapterIndex ( reopened , type ) ) ) ;
317347 }
318348
319349 [ Theory ]
@@ -345,6 +375,25 @@ public void Add_channel_management_is_generic_for_discord_and_mattermost(
345375 private ChannelsConfigViewModel CreateViewModel ( )
346376 => new ( _paths , new FakeSlackProbe ( ) , new FakeDiscordProbe ( ) ) ;
347377
378+ private static void ConfirmReset ( ChannelsConfigViewModel vm , ChannelType type )
379+ {
380+ vm . OpenAdapterManagement ( type ) ;
381+ var resetIndex = vm . GetManagementMenuItems ( )
382+ . Select ( ( item , index ) => ( item , index ) )
383+ . Single ( entry => entry . item . Action == ChannelsManagementAction . ResetConnection )
384+ . index ;
385+ vm . MoveManagementMenu ( resetIndex ) ;
386+ vm . ActivateManagementMenuItem ( ) ;
387+ vm . MoveResetConfirmation ( 1 ) ;
388+ vm . ApplyResetConfirmation ( ) ;
389+ }
390+
391+ private static int GetAdapterIndex ( ChannelsConfigViewModel vm , ChannelType type )
392+ => vm . Step . Adapters
393+ . Select ( ( adapter , index ) => ( adapter . Type , index ) )
394+ . Single ( entry => entry . Type == type )
395+ . index ;
396+
348397 private static string [ ] ToStringArray ( object ? raw )
349398 => Assert . IsType < object [ ] > ( raw ) . Select ( static value => value switch
350399 {
0 commit comments