@@ -2,8 +2,13 @@ package server
22
33import (
44 "fmt"
5+ "os"
6+ "strings"
57 "testing"
68
9+ "github.com/spf13/cobra"
10+ "github.com/spf13/viper"
11+ "github.com/stretchr/testify/assert"
712 "github.com/stretchr/testify/require"
813)
914
@@ -83,3 +88,206 @@ func TestParseAgentType(t *testing.T) {
8388 require .Error (t , err )
8489 })
8590}
91+
92+ // Test helper to isolate viper config between tests
93+ func isolateViper (t * testing.T ) {
94+ // Save current state
95+ oldConfig := viper .AllSettings ()
96+
97+ // Reset viper
98+ viper .Reset ()
99+
100+ // Clear AGENTAPI_ env vars
101+ var agentapiEnvs []string
102+ for _ , env := range os .Environ () {
103+ if strings .HasPrefix (env , "AGENTAPI_" ) {
104+ parts := strings .SplitN (env , "=" , 2 )
105+ agentapiEnvs = append (agentapiEnvs , parts [0 ])
106+ if err := os .Unsetenv (parts [0 ]); err != nil {
107+ t .Fatalf ("Failed to unset env var %s: %v" , parts [0 ], err )
108+ }
109+ }
110+ }
111+
112+ t .Cleanup (func () {
113+ // Restore state
114+ viper .Reset ()
115+ for key , value := range oldConfig {
116+ viper .Set (key , value )
117+ }
118+
119+ // Restore env vars
120+ for _ , key := range agentapiEnvs {
121+ if val := os .Getenv (key ); val != "" {
122+ if err := os .Setenv (key , val ); err != nil {
123+ t .Fatalf ("Failed to set env var %s: %v" , key , err )
124+ }
125+ }
126+ }
127+ })
128+ }
129+
130+ // Test configuration values via ServerCmd execution
131+ func TestServerCmd_AllArgs_Defaults (t * testing.T ) {
132+ tests := []struct {
133+ name string
134+ flag string
135+ expected any
136+ getter func () any
137+ }{
138+ {"type default" , FlagType , "" , func () any { return viper .GetString (FlagType ) }},
139+ {"port default" , FlagPort , 3284 , func () any { return viper .GetInt (FlagPort ) }},
140+ {"print-openapi default" , FlagPrintOpenAPI , false , func () any { return viper .GetBool (FlagPrintOpenAPI ) }},
141+ {"chat-base-path default" , FlagChatBasePath , "/chat" , func () any { return viper .GetString (FlagChatBasePath ) }},
142+ {"term-width default" , FlagTermWidth , uint16 (80 ), func () any { return viper .GetUint16 (FlagTermWidth ) }},
143+ {"term-height default" , FlagTermHeight , uint16 (1000 ), func () any { return viper .GetUint16 (FlagTermHeight ) }},
144+ }
145+
146+ for _ , tt := range tests {
147+ t .Run (tt .name , func (t * testing.T ) {
148+ isolateViper (t )
149+ serverCmd := CreateServerCmd ()
150+ cmd := & cobra.Command {}
151+ cmd .AddCommand (serverCmd )
152+
153+ // Execute with no args to get defaults
154+ serverCmd .SetArgs ([]string {"--help" }) // Use help to avoid actual execution
155+ if err := serverCmd .Execute (); err != nil {
156+ t .Fatalf ("Failed to execute server command: %v" , err )
157+ }
158+
159+ assert .Equal (t , tt .expected , tt .getter ())
160+ })
161+ }
162+ }
163+
164+ func TestServerCmd_AllEnvVars (t * testing.T ) {
165+ tests := []struct {
166+ name string
167+ envVar string
168+ envValue string
169+ expected any
170+ getter func () any
171+ }{
172+ {"AGENTAPI_TYPE" , "AGENTAPI_TYPE" , "claude" , "claude" , func () any { return viper .GetString (FlagType ) }},
173+ {"AGENTAPI_PORT" , "AGENTAPI_PORT" , "8080" , 8080 , func () any { return viper .GetInt (FlagPort ) }},
174+ {"AGENTAPI_PRINT_OPENAPI" , "AGENTAPI_PRINT_OPENAPI" , "true" , true , func () any { return viper .GetBool (FlagPrintOpenAPI ) }},
175+ {"AGENTAPI_CHAT_BASE_PATH" , "AGENTAPI_CHAT_BASE_PATH" , "/api" , "/api" , func () any { return viper .GetString (FlagChatBasePath ) }},
176+ {"AGENTAPI_TERM_WIDTH" , "AGENTAPI_TERM_WIDTH" , "120" , uint16 (120 ), func () any { return viper .GetUint16 (FlagTermWidth ) }},
177+ {"AGENTAPI_TERM_HEIGHT" , "AGENTAPI_TERM_HEIGHT" , "500" , uint16 (500 ), func () any { return viper .GetUint16 (FlagTermHeight ) }},
178+ }
179+
180+ for _ , tt := range tests {
181+ t .Run (tt .name , func (t * testing.T ) {
182+ isolateViper (t )
183+ t .Setenv (tt .envVar , tt .envValue )
184+
185+ serverCmd := CreateServerCmd ()
186+ cmd := & cobra.Command {}
187+ cmd .AddCommand (serverCmd )
188+
189+ serverCmd .SetArgs ([]string {"--help" })
190+ if err := serverCmd .Execute (); err != nil {
191+ t .Fatalf ("Failed to execute server command: %v" , err )
192+ }
193+
194+ assert .Equal (t , tt .expected , tt .getter ())
195+ })
196+ }
197+ }
198+
199+ func TestServerCmd_ArgsPrecedenceOverEnv (t * testing.T ) {
200+ tests := []struct {
201+ name string
202+ envVar string
203+ envValue string
204+ args []string
205+ expected any
206+ getter func () any
207+ }{
208+ {
209+ "type: CLI overrides env" ,
210+ "AGENTAPI_TYPE" , "goose" ,
211+ []string {"--type" , "claude" },
212+ "claude" ,
213+ func () any { return viper .GetString (FlagType ) },
214+ },
215+ {
216+ "port: CLI overrides env" ,
217+ "AGENTAPI_PORT" , "8080" ,
218+ []string {"--port" , "9090" },
219+ 9090 ,
220+ func () any { return viper .GetInt (FlagPort ) },
221+ },
222+ {
223+ "print-openapi: CLI overrides env" ,
224+ "AGENTAPI_PRINT_OPENAPI" , "false" ,
225+ []string {"--print-openapi" },
226+ true ,
227+ func () any { return viper .GetBool (FlagPrintOpenAPI ) },
228+ },
229+ {
230+ "chat-base-path: CLI overrides env" ,
231+ "AGENTAPI_CHAT_BASE_PATH" , "/env-path" ,
232+ []string {"--chat-base-path" , "/cli-path" },
233+ "/cli-path" ,
234+ func () any { return viper .GetString (FlagChatBasePath ) },
235+ },
236+ {
237+ "term-width: CLI overrides env" ,
238+ "AGENTAPI_TERM_WIDTH" , "100" ,
239+ []string {"--term-width" , "150" },
240+ uint16 (150 ),
241+ func () any { return viper .GetUint16 (FlagTermWidth ) },
242+ },
243+ {
244+ "term-height: CLI overrides env" ,
245+ "AGENTAPI_TERM_HEIGHT" , "500" ,
246+ []string {"--term-height" , "600" },
247+ uint16 (600 ),
248+ func () any { return viper .GetUint16 (FlagTermHeight ) },
249+ },
250+ }
251+
252+ for _ , tt := range tests {
253+ t .Run (tt .name , func (t * testing.T ) {
254+ isolateViper (t )
255+ t .Setenv (tt .envVar , tt .envValue )
256+
257+ // Mock execution to test arg parsing without running server
258+ args := append (tt .args , "--help" )
259+ serverCmd := CreateServerCmd ()
260+ serverCmd .SetArgs (args )
261+ if err := serverCmd .Execute (); err != nil {
262+ t .Fatalf ("Failed to execute server command: %v" , err )
263+ }
264+
265+ assert .Equal (t , tt .expected , tt .getter ())
266+ })
267+ }
268+ }
269+
270+ func TestMixed_ConfigurationScenarios (t * testing.T ) {
271+ t .Run ("some env, some cli, some defaults" , func (t * testing.T ) {
272+ isolateViper (t )
273+
274+ // Set some env vars
275+ t .Setenv ("AGENTAPI_TYPE" , "goose" )
276+ t .Setenv ("AGENTAPI_TERM_WIDTH" , "120" )
277+
278+ // Set some CLI args
279+ serverCmd := CreateServerCmd ()
280+ serverCmd .SetArgs ([]string {"--port" , "9999" , "--print-openapi" , "--help" })
281+ if err := serverCmd .Execute (); err != nil {
282+ t .Fatalf ("Failed to execute server command: %v" , err )
283+ }
284+
285+ // Verify mixed configuration
286+ assert .Equal (t , "goose" , viper .GetString (FlagType )) // from env
287+ assert .Equal (t , 9999 , viper .GetInt (FlagPort )) // from CLI
288+ assert .Equal (t , true , viper .GetBool (FlagPrintOpenAPI )) // from CLI
289+ assert .Equal (t , "/chat" , viper .GetString (FlagChatBasePath )) // default
290+ assert .Equal (t , uint16 (120 ), viper .GetUint16 (FlagTermWidth )) // from env
291+ assert .Equal (t , uint16 (1000 ), viper .GetUint16 (FlagTermHeight )) // default
292+ })
293+ }
0 commit comments