@@ -4,9 +4,10 @@ import (
44 "bytes"
55 "errors"
66 "io"
7+ "os"
8+ "path/filepath"
79 "testing"
810
9- "github.com/stretchr/testify/assert"
1011 "github.com/stretchr/testify/require"
1112
1213 "github.com/mozilla-ai/mcpd/v2/internal/cmd"
@@ -72,7 +73,7 @@ type fakeBuilder struct {
7273 err error
7374}
7475
75- func (f * fakeBuilder ) Build () (registry.PackageProvider , error ) {
76+ func (f * fakeBuilder ) Build (_ ... options. BuildOption ) (registry.PackageProvider , error ) {
7677 return f .reg , f .err
7778}
7879
@@ -115,10 +116,10 @@ func TestAddCmd_Success(t *testing.T) {
115116
116117 err = cmdObj .Execute ()
117118 require .NoError (t , err )
118- assert .Contains (t , buf .String (), "✓ Added server" )
119- assert .True (t , cfg .addCalled )
120- assert .Equal (t , "server1" , cfg .entry .Name )
121- assert .Equal (t , "uvx::mcp-server-1@1.2.3" , cfg .entry .Package )
119+ require .Contains (t , buf .String (), "✓ Added server" )
120+ require .True (t , cfg .addCalled )
121+ require .Equal (t , "server1" , cfg .entry .Name )
122+ require .Equal (t , "uvx::mcp-server-1@1.2.3" , cfg .entry .Package )
122123}
123124
124125func TestAddCmd_MissingArgs (t * testing.T ) {
@@ -132,7 +133,7 @@ func TestAddCmd_MissingArgs(t *testing.T) {
132133
133134 err = cmdObj .Execute ()
134135 require .Error (t , err )
135- assert .Contains (t , err .Error (), "server name is required" )
136+ require .Contains (t , err .Error (), "server name is required" )
136137}
137138
138139func TestAddCmd_RegistryFails (t * testing.T ) {
@@ -145,7 +146,7 @@ func TestAddCmd_RegistryFails(t *testing.T) {
145146 cmdObj .SetArgs ([]string {"server1" })
146147 err = cmdObj .Execute ()
147148 require .Error (t , err )
148- assert .Contains (t , err .Error (), "registry error" )
149+ require .Contains (t , err .Error (), "registry error" )
149150}
150151
151152func TestAddCmd_BasicServerAdd (t * testing.T ) {
@@ -187,14 +188,14 @@ func TestAddCmd_BasicServerAdd(t *testing.T) {
187188
188189 // Output assertions
189190 outStr := o .String ()
190- assert .Contains (t , outStr , "✓ Added server 'testserver'" )
191- assert .Contains (t , outStr , "version: latest" )
191+ require .Contains (t , outStr , "✓ Added server 'testserver'" )
192+ require .Contains (t , outStr , "version: latest" )
192193
193194 // Config assertions
194195 require .True (t , cfg .addCalled )
195- assert .Equal (t , "testserver" , cfg .entry .Name )
196- assert .Equal (t , "uvx::mcp-server-testserver@latest" , cfg .entry .Package )
197- assert .ElementsMatch (t , []string {"tool1" , "tool2" , "tool3" }, cfg .entry .Tools )
196+ require .Equal (t , "testserver" , cfg .entry .Name )
197+ require .Equal (t , "uvx::mcp-server-testserver@latest" , cfg .entry .Package )
198+ require .ElementsMatch (t , []string {"tool1" , "tool2" , "tool3" }, cfg .entry .Tools )
198199}
199200
200201func TestAddCmd_ServerWithArguments (t * testing.T ) {
@@ -396,11 +397,11 @@ func TestAddCmd_ServerWithArguments(t *testing.T) {
396397
397398 // Verify config was called with correct arguments
398399 require .True (t , cfg .addCalled )
399- assert .Equal (t , tc .pkg .ID , cfg .entry .Name )
400- assert .ElementsMatch (t , tc .expectedRequiredEnvs , cfg .entry .RequiredEnvVars )
401- assert .ElementsMatch (t , tc .expectedRequiredPositionals , cfg .entry .RequiredPositionalArgs )
402- assert .ElementsMatch (t , tc .expectedRequiredValues , cfg .entry .RequiredValueArgs )
403- assert .ElementsMatch (t , tc .expectedRequiredBools , cfg .entry .RequiredBoolArgs )
400+ require .Equal (t , tc .pkg .ID , cfg .entry .Name )
401+ require .ElementsMatch (t , tc .expectedRequiredEnvs , cfg .entry .RequiredEnvVars )
402+ require .ElementsMatch (t , tc .expectedRequiredPositionals , cfg .entry .RequiredPositionalArgs )
403+ require .ElementsMatch (t , tc .expectedRequiredValues , cfg .entry .RequiredValueArgs )
404+ require .ElementsMatch (t , tc .expectedRequiredBools , cfg .entry .RequiredBoolArgs )
404405 })
405406 }
406407}
@@ -476,7 +477,7 @@ func TestSelectRuntime(t *testing.T) {
476477 require .EqualError (t , err , "no supported runtimes found" )
477478 } else {
478479 require .NoError (t , err )
479- assert .Equal (t , tc .expectedRuntime , got )
480+ require .Equal (t , tc .expectedRuntime , got )
480481 }
481482 })
482483 }
@@ -761,3 +762,188 @@ func TestParseServerEntry(t *testing.T) {
761762 })
762763 }
763764}
765+
766+ func TestAddCmd_CacheTTL (t * testing.T ) {
767+ t .Parallel ()
768+
769+ tests := []struct {
770+ name string
771+ ttl string
772+ expectedError string
773+ }{
774+ {
775+ name : "valid cache TTL" ,
776+ ttl : "1h" ,
777+ },
778+ {
779+ name : "invalid cache TTL" ,
780+ ttl : "invalid" ,
781+ expectedError : "invalid cache TTL: time: invalid duration \" invalid\" " ,
782+ },
783+ }
784+
785+ for _ , tc := range tests {
786+ t .Run (tc .name , func (t * testing.T ) {
787+ t .Parallel ()
788+
789+ pkg := packages.Server {
790+ ID : "testserver" ,
791+ Name : "testserver" ,
792+ Tools : []packages.Tool {
793+ {Name : "tool1" },
794+ },
795+ Installations : map [runtime.Runtime ]packages.Installation {
796+ runtime .UVX : {
797+ Runtime : "uvx" ,
798+ Package : "mcp-server-testserver" ,
799+ Version : "latest" ,
800+ Recommended : true ,
801+ },
802+ },
803+ }
804+
805+ cfg := & fakeConfig {}
806+ cmdObj , err := NewAddCmd (
807+ & cmd.BaseCmd {},
808+ cmdopts .WithConfigLoader (& fakeLoader {cfg : cfg }),
809+ cmdopts .WithRegistryBuilder (& fakeBuilder {reg : & fakeRegistry {pkg : pkg }}),
810+ )
811+ require .NoError (t , err )
812+
813+ cmdObj .SetOut (io .Discard )
814+ cmdObj .SetErr (io .Discard )
815+ cmdObj .SetArgs ([]string {"testserver" , "--cache-ttl" , tc .ttl })
816+
817+ err = cmdObj .Execute ()
818+ if tc .expectedError != "" {
819+ require .Error (t , err )
820+ require .EqualError (t , err , tc .expectedError )
821+ } else {
822+ require .NoError (t , err )
823+ require .True (t , cfg .addCalled )
824+ require .Equal (t , "testserver" , cfg .entry .Name )
825+ }
826+ })
827+ }
828+ }
829+
830+ func TestAddCmd_CacheFlagsWithTempDir (t * testing.T ) {
831+ t .Parallel ()
832+
833+ tests := []struct {
834+ name string
835+ setupCmd func (t * testing.T , tempDir string ) []string
836+ }{
837+ {
838+ name : "custom cache directory" ,
839+ setupCmd : func (t * testing.T , tempDir string ) []string {
840+ return []string {"testserver" , "--cache-dir" , tempDir }
841+ },
842+ },
843+ {
844+ name : "both custom cache flags" ,
845+ setupCmd : func (t * testing.T , tempDir string ) []string {
846+ return []string {"testserver" , "--cache-dir" , tempDir , "--cache-ttl" , "30m" }
847+ },
848+ },
849+ {
850+ name : "cache disabled with custom settings" ,
851+ setupCmd : func (t * testing.T , tempDir string ) []string {
852+ return []string {"testserver" , "--no-cache" , "--cache-dir" , tempDir , "--cache-ttl" , "2h" }
853+ },
854+ },
855+ }
856+
857+ for _ , tc := range tests {
858+ t .Run (tc .name , func (t * testing.T ) {
859+ t .Parallel ()
860+
861+ tempDir := t .TempDir ()
862+ args := tc .setupCmd (t , tempDir )
863+
864+ pkg := packages.Server {
865+ ID : "testserver" ,
866+ Name : "testserver" ,
867+ Tools : []packages.Tool {
868+ {Name : "tool1" },
869+ },
870+ Installations : map [runtime.Runtime ]packages.Installation {
871+ runtime .UVX : {
872+ Runtime : "uvx" ,
873+ Package : "mcp-server-testserver" ,
874+ Version : "latest" ,
875+ Recommended : true ,
876+ },
877+ },
878+ }
879+
880+ cfg := & fakeConfig {}
881+ cmdObj , err := NewAddCmd (
882+ & cmd.BaseCmd {},
883+ cmdopts .WithConfigLoader (& fakeLoader {cfg : cfg }),
884+ cmdopts .WithRegistryBuilder (& fakeBuilder {reg : & fakeRegistry {pkg : pkg }}),
885+ )
886+ require .NoError (t , err )
887+
888+ cmdObj .SetOut (io .Discard )
889+ cmdObj .SetErr (io .Discard )
890+ cmdObj .SetArgs (args )
891+
892+ err = cmdObj .Execute ()
893+ require .NoError (t , err )
894+ require .True (t , cfg .addCalled )
895+ require .Equal (t , "testserver" , cfg .entry .Name )
896+
897+ // tempDir is available here for any cache directory verification
898+ })
899+ }
900+ }
901+
902+ func TestAddCmd_NoCacheDirectoryCreatedWhenDisabled (t * testing.T ) {
903+ t .Parallel ()
904+
905+ tempDir := t .TempDir ()
906+ cacheSubDir := filepath .Join (tempDir , "should-not-be-created" )
907+
908+ // Verify the cache directory doesn't exist initially.
909+ _ , err := os .Stat (cacheSubDir )
910+ require .True (t , os .IsNotExist (err ), "Cache directory should not exist initially" )
911+
912+ pkg := packages.Server {
913+ ID : "testserver" ,
914+ Name : "testserver" ,
915+ Tools : []packages.Tool {
916+ {Name : "tool1" },
917+ },
918+ Installations : map [runtime.Runtime ]packages.Installation {
919+ runtime .UVX : {
920+ Runtime : "uvx" ,
921+ Package : "mcp-server-testserver" ,
922+ Version : "latest" ,
923+ Recommended : true ,
924+ },
925+ },
926+ }
927+
928+ cfg := & fakeConfig {}
929+ cmdObj , err := NewAddCmd (
930+ & cmd.BaseCmd {},
931+ cmdopts .WithConfigLoader (& fakeLoader {cfg : cfg }),
932+ cmdopts .WithRegistryBuilder (& fakeBuilder {reg : & fakeRegistry {pkg : pkg }}),
933+ )
934+ require .NoError (t , err )
935+
936+ cmdObj .SetOut (io .Discard )
937+ cmdObj .SetErr (io .Discard )
938+ // Use --no-cache with custom cache directory - directory should NOT be created.
939+ cmdObj .SetArgs ([]string {"testserver" , "--no-cache" , "--cache-dir" , cacheSubDir })
940+
941+ err = cmdObj .Execute ()
942+ require .NoError (t , err )
943+ require .True (t , cfg .addCalled )
944+ require .Equal (t , "testserver" , cfg .entry .Name )
945+
946+ // Verify the cache directory was never created.
947+ _ , err = os .Stat (cacheSubDir )
948+ require .True (t , os .IsNotExist (err ), "Cache directory should not be created when --no-cache is used" )
949+ }
0 commit comments