@@ -262,3 +262,168 @@ func TestGetConnectorByName(t *testing.T) {
262262 require .NotNil (t , diags )
263263 require .Nil (t , fail )
264264}
265+
266+ func TestConnectorConfigWithDefaults (t * testing.T ) {
267+ tests := []struct {
268+ name string
269+ connectorTypeID string
270+ planConfig string
271+ expectedError bool
272+ errorContains string
273+ validateResult func (t * testing.T , result string )
274+ }{
275+ {
276+ name : "bedrock connector with valid config and explicit defaultModel" ,
277+ connectorTypeID : ".bedrock" ,
278+ planConfig : `{"apiUrl":"https://bedrock.us-east-1.amazonaws.com","defaultModel":"anthropic.claude-v2"}` ,
279+ expectedError : false ,
280+ validateResult : func (t * testing.T , result string ) {
281+ expected := `{"apiUrl":"https://bedrock.us-east-1.amazonaws.com","defaultModel":"anthropic.claude-v2"}`
282+ require .JSONEq (t , expected , result )
283+ },
284+ },
285+ {
286+ name : "bedrock connector without defaultModel gets default value" ,
287+ connectorTypeID : ".bedrock" ,
288+ planConfig : `{"apiUrl":"https://bedrock.us-east-1.amazonaws.com"}` ,
289+ expectedError : false ,
290+ validateResult : func (t * testing.T , result string ) {
291+ expected := `{"apiUrl":"https://bedrock.us-east-1.amazonaws.com","defaultModel":"us.anthropic.claude-sonnet-4-5-20250929-v1:0"}`
292+ require .JSONEq (t , expected , result )
293+ },
294+ },
295+ {
296+ name : "gen-ai connector with OpenAI provider with defaultModel" ,
297+ connectorTypeID : ".gen-ai" ,
298+ planConfig : `{"apiProvider":"OpenAI","apiUrl":"https://api.openai.com/v1","defaultModel":"gpt-4"}` ,
299+ expectedError : false ,
300+ validateResult : func (t * testing.T , result string ) {
301+ expected := `{"apiProvider":"OpenAI","apiUrl":"https://api.openai.com/v1","defaultModel":"gpt-4"}`
302+ require .JSONEq (t , expected , result )
303+ },
304+ },
305+ {
306+ name : "gen-ai connector with Azure provider" ,
307+ connectorTypeID : ".gen-ai" ,
308+ planConfig : `{"apiProvider":"Azure OpenAI","apiUrl":"https://my-resource.openai.azure.com/openai/deployments/my-deployment"}` ,
309+ expectedError : false ,
310+ validateResult : func (t * testing.T , result string ) {
311+ expected := `{"apiProvider":"Azure OpenAI","apiUrl":"https://my-resource.openai.azure.com/openai/deployments/my-deployment"}`
312+ require .JSONEq (t , expected , result )
313+ },
314+ },
315+ {
316+ name : "gen-ai connector with Other provider and explicit verificationMode" ,
317+ connectorTypeID : ".gen-ai" ,
318+ planConfig : `{"apiProvider":"Other","apiUrl":"https://custom-llm.example.com/v1","defaultModel":"custom-model","verificationMode":"none"}` ,
319+ expectedError : false ,
320+ validateResult : func (t * testing.T , result string ) {
321+ expected := `{"apiProvider":"Other","apiUrl":"https://custom-llm.example.com/v1","defaultModel":"custom-model","verificationMode":"none"}`
322+ require .JSONEq (t , expected , result )
323+ },
324+ },
325+ {
326+ name : "gen-ai connector with Other provider without verificationMode gets default" ,
327+ connectorTypeID : ".gen-ai" ,
328+ planConfig : `{"apiProvider":"Other","apiUrl":"https://custom-llm.example.com/v1","defaultModel":"custom-model"}` ,
329+ expectedError : false ,
330+ validateResult : func (t * testing.T , result string ) {
331+ expected := `{"apiProvider":"Other","apiUrl":"https://custom-llm.example.com/v1","defaultModel":"custom-model","verificationMode":"full"}`
332+ require .JSONEq (t , expected , result )
333+ },
334+ },
335+ {
336+ name : "gen-ai connector with OpenAI provider without defaultModel" ,
337+ connectorTypeID : ".gen-ai" ,
338+ planConfig : `{"apiProvider":"OpenAI","apiUrl":"https://api.openai.com/v1"}` ,
339+ expectedError : false ,
340+ validateResult : func (t * testing.T , result string ) {
341+ // Verify no verificationMode is added (that's only for Other provider)
342+ expected := `{"apiProvider":"OpenAI","apiUrl":"https://api.openai.com/v1"}`
343+ require .JSONEq (t , expected , result )
344+ },
345+ },
346+ {
347+ name : "gemini connector with valid config" ,
348+ connectorTypeID : ".gemini" ,
349+ planConfig : `{"apiUrl":"https://us-central1-aiplatform.googleapis.com","gcpProjectId":"my-project","gcpRegion":"us-central1","defaultModel":"gemini-pro"}` ,
350+ expectedError : false ,
351+ validateResult : func (t * testing.T , result string ) {
352+ expected := `{"apiUrl":"https://us-central1-aiplatform.googleapis.com","gcpProjectId":"my-project","gcpRegion":"us-central1","defaultModel":"gemini-pro"}`
353+ require .JSONEq (t , expected , result )
354+ },
355+ },
356+ {
357+ name : "gen-ai OpenAI connector silently filters unknown fields" ,
358+ connectorTypeID : ".gen-ai" ,
359+ planConfig : `{"apiProvider":"OpenAI","apiUrl":"https://api.openai.com/v1","defaultModel":"gpt-4","unknownField":"should-be-filtered"}` ,
360+ expectedError : false ,
361+ validateResult : func (t * testing.T , result string ) {
362+ // Unknown field should be filtered out
363+ expected := `{"apiProvider":"OpenAI","apiUrl":"https://api.openai.com/v1","defaultModel":"gpt-4"}`
364+ require .JSONEq (t , expected , result )
365+ },
366+ },
367+ {
368+ name : "gen-ai Azure connector silently filters invalid PKI fields" ,
369+ connectorTypeID : ".gen-ai" ,
370+ planConfig : `{"apiProvider":"Azure OpenAI","apiUrl":"https://my.openai.azure.com","certificateData":"invalid-for-azure"}` ,
371+ expectedError : false ,
372+ validateResult : func (t * testing.T , result string ) {
373+ // certificateData is not valid for Azure provider, should be filtered
374+ expected := `{"apiProvider":"Azure OpenAI","apiUrl":"https://my.openai.azure.com"}`
375+ require .JSONEq (t , expected , result )
376+ },
377+ },
378+ {
379+ name : "gen-ai Other connector allows PKI fields" ,
380+ connectorTypeID : ".gen-ai" ,
381+ planConfig : `{"apiProvider":"Other","apiUrl":"https://custom.com","defaultModel":"custom","certificateData":"pem-data","verificationMode":"full"}` ,
382+ expectedError : false ,
383+ validateResult : func (t * testing.T , result string ) {
384+ expected := `{"apiProvider":"Other","apiUrl":"https://custom.com","defaultModel":"custom","certificateData":"pem-data","verificationMode":"full"}`
385+ require .JSONEq (t , expected , result )
386+ },
387+ },
388+ {
389+ name : "gen-ai connector without apiProvider returns error" ,
390+ connectorTypeID : ".gen-ai" ,
391+ planConfig : `{"apiUrl":"https://api.example.com"}` ,
392+ expectedError : true ,
393+ errorContains : "apiProvider is required" ,
394+ },
395+ {
396+ name : "gen-ai connector with unknown apiProvider returns error" ,
397+ connectorTypeID : ".gen-ai" ,
398+ planConfig : `{"apiProvider":"UnknownProvider","apiUrl":"https://api.example.com"}` ,
399+ expectedError : true ,
400+ errorContains : "unsupported apiProvider" ,
401+ },
402+ {
403+ name : "unknown connector type returns error" ,
404+ connectorTypeID : ".unknown-type" ,
405+ planConfig : `{"key":"value"}` ,
406+ expectedError : true ,
407+ errorContains : "unknown connector type ID" ,
408+ },
409+ }
410+
411+ for _ , tt := range tests {
412+ t .Run (tt .name , func (t * testing.T ) {
413+ result , err := kibana_oapi .ConnectorConfigWithDefaults (tt .connectorTypeID , tt .planConfig )
414+
415+ if tt .expectedError {
416+ require .Error (t , err )
417+ if tt .errorContains != "" {
418+ require .Contains (t , err .Error (), tt .errorContains )
419+ }
420+ } else {
421+ require .NoError (t , err )
422+ require .NotEmpty (t , result )
423+ if tt .validateResult != nil {
424+ tt .validateResult (t , result )
425+ }
426+ }
427+ })
428+ }
429+ }
0 commit comments