@@ -15,10 +15,13 @@ import (
1515// package-level state (cfg, configPath) and environment variables.
1616
1717// saveCfgState saves the current cfg state and restores it on cleanup.
18+ // Installs a mock keyring that errors by default so existing tests that
19+ // expect tokens in config continue to work.
1820func saveCfgState (t * testing.T ) {
1921 t .Helper ()
2022 oldCfg := cfg
2123 oldPath := configPath
24+ keyringMockInitWithError (errors .New ("keyring disabled in test" ))
2225 t .Cleanup (func () {
2326 cfg = oldCfg
2427 configPath = oldPath
@@ -642,3 +645,97 @@ func TestDetectServerFromDSLNoMatch(T *testing.T) {
642645 got := DetectServerFromDSL ()
643646 assert .Empty (T , got )
644647}
648+
649+ func TestGetTokenPriority (T * testing.T ) {
650+ saveCfgState (T )
651+ keyringMockInit ()
652+
653+ serverURL := "https://tc.example.com"
654+ require .NoError (T , keyringSet ("tc:" + serverURL , "admin" , "keyring-token" ))
655+
656+ cfg = & Config {
657+ DefaultServer : serverURL ,
658+ Servers : map [string ]ServerConfig {
659+ serverURL : {Token : "config-token" , User : "admin" },
660+ },
661+ }
662+
663+ T .Run ("env wins over keyring" , func (t * testing.T ) {
664+ t .Setenv (EnvToken , "env-token" )
665+ t .Setenv (EnvServerURL , serverURL )
666+
667+ token , source := GetTokenWithSource ()
668+ assert .Equal (t , "env-token" , token )
669+ assert .Equal (t , "env" , source )
670+ })
671+
672+ T .Run ("keyring wins over config" , func (t * testing.T ) {
673+ t .Setenv (EnvToken , "" )
674+ t .Setenv (EnvServerURL , serverURL )
675+
676+ token , source := GetTokenWithSource ()
677+ assert .Equal (t , "keyring-token" , token )
678+ assert .Equal (t , "keyring" , source )
679+ })
680+
681+ T .Run ("config used when keyring empty" , func (t * testing.T ) {
682+ t .Setenv (EnvToken , "" )
683+ t .Setenv (EnvServerURL , serverURL )
684+ require .NoError (t , keyringDelete ("tc:" + serverURL , "admin" ))
685+
686+ token , source := GetTokenWithSource ()
687+ assert .Equal (t , "config-token" , token )
688+ assert .Equal (t , "config" , source )
689+ })
690+ }
691+
692+ func TestSetServerWithKeyring (T * testing.T ) {
693+ saveCfgState (T )
694+ keyringMockInit ()
695+ tmpDir := T .TempDir ()
696+ configPath = tmpDir + "/config.yml"
697+ cfg = & Config {Servers : make (map [string ]ServerConfig )}
698+
699+ insecure , err := SetServerWithKeyring ("https://tc.example.com" , "my-token" , "admin" , false )
700+ require .NoError (T , err )
701+ assert .False (T , insecure )
702+
703+ // Token in keyring, not in config
704+ assert .Empty (T , cfg .Servers ["https://tc.example.com" ].Token )
705+ assert .Equal (T , "admin" , cfg .Servers ["https://tc.example.com" ].User )
706+ val , err := keyringGet ("tc:https://tc.example.com" , "admin" )
707+ require .NoError (T , err )
708+ assert .Equal (T , "my-token" , val )
709+ }
710+
711+ func TestSetServerKeyringFallback (T * testing.T ) {
712+ saveCfgState (T )
713+ tmpDir := T .TempDir ()
714+ configPath = tmpDir + "/config.yml"
715+ cfg = & Config {Servers : make (map [string ]ServerConfig )}
716+
717+ insecure , err := SetServerWithKeyring ("https://tc.example.com" , "my-token" , "admin" , false )
718+ require .NoError (T , err )
719+ assert .True (T , insecure )
720+
721+ assert .Equal (T , "my-token" , cfg .Servers ["https://tc.example.com" ].Token )
722+ }
723+
724+ func TestRemoveServerCleansKeyring (T * testing.T ) {
725+ saveCfgState (T )
726+ keyringMockInit ()
727+ tmpDir := T .TempDir ()
728+ configPath = tmpDir + "/config.yml"
729+ cfg = & Config {Servers : make (map [string ]ServerConfig )}
730+
731+ _ , err := SetServerWithKeyring ("https://tc.example.com" , "my-token" , "admin" , false )
732+ require .NoError (T , err )
733+
734+ err = RemoveServer ("https://tc.example.com" )
735+ require .NoError (T , err )
736+
737+ _ , ok := cfg .Servers ["https://tc.example.com" ]
738+ assert .False (T , ok )
739+ _ , err = keyringGet ("tc:https://tc.example.com" , "admin" )
740+ assert .ErrorIs (T , err , errKeyringNotFound )
741+ }
0 commit comments