@@ -3070,9 +3070,10 @@ func TestRunLocalScopedPlaybook(t *testing.T) {
30703070 t .Errorf ("Failed to detect not able to get installed app name: err" )
30713071 }
30723072
3073- // Test4: get installed app command passes but installing app fails
3074- mockPodExecReturnContexts [2 ].StdOut = "1" //app is not yet installed or it is not enabled
3075- mockPodExecReturnContexts [2 ].StdErr = "" //no error thrown
3073+ // Test4: get installed app command passes but installing app fails (non-FIPS error)
3074+ mockPodExecReturnContexts [2 ].StdOut = "1" //app is not yet installed or it is not enabled
3075+ mockPodExecReturnContexts [2 ].StdErr = "" //no error thrown
3076+ mockPodExecReturnContexts [3 ].StdErr = "real installation error" // Non-FIPS error should still fail
30763077
30773078 localInstallCtxt .sem <- struct {}{}
30783079 waiter .Add (1 )
@@ -3091,15 +3092,14 @@ func TestRunLocalScopedPlaybook(t *testing.T) {
30913092 t .Errorf ("Expected app install failed" )
30923093 }
30933094
3094- // Test5: install app should be successful
3095-
3096- mockPodExecReturnContexts [3 ].StdErr = "" //no error for app install
3095+ // Test5: FIPS message should be handled gracefully during install
3096+ mockPodExecReturnContexts [3 ].StdErr = "FIPS provider enabled. name: OpenSSL FIPS Provider, version: 3.0.9, buildinfo: 3.0.9, status: Success" // FIPS message should be ignored
30973097
30983098 localInstallCtxt .sem <- struct {}{}
30993099 waiter .Add (1 )
31003100 err = localInstallCtxt .runPlaybook (ctx )
31013101 if err == nil {
3102- t .Errorf ("Expected app install succeeded but app arhive deletion failed" )
3102+ t .Errorf ("Expected app install succeeded but app archive deletion failed" )
31033103 }
31043104
31053105 // Test6: successful scenario where everything succeeds
@@ -3301,19 +3301,23 @@ func TestPremiumAppScopedPlaybook(t *testing.T) {
33013301 t .Errorf ("Failed to detect that steps to get installed app failed" )
33023302 }
33033303
3304- // Test3: get installed app name passes but getting installed app name failed
3304+ // Test3: get installed app name passes but isAppAlreadyInstalled fails with non-FIPS error
33053305 mockPodExecReturnContexts [1 ].StdErr = ""
3306+ mockPodExecReturnContexts [2 ].StdErr = "Could not find object id=app1" // Non-FIPS error
3307+ mockPodExecReturnContexts [2 ].Err = fmt .Errorf ("exit status 2" ) // Real error, not grep exit code 1
33063308 localInstallCtxt .sem <- struct {}{}
33073309 waiter .Add (1 )
33083310 err = pCtx .runPlaybook (ctx )
33093311 if err == nil {
3310- t .Errorf ("Failed to detect not able to get installed app name: err " )
3312+ t .Errorf ("Failed to detect isAppAlreadyInstalled error " )
33113313 }
33123314
3313- // Test4: get installed app command passes, it returns app is not enabled
3314- // so app install will run and it should fail
3315- mockPodExecReturnContexts [2 ].StdOut = "1" //app is not yet installed or it is not enabled
3316- mockPodExecReturnContexts [2 ].StdErr = "" //no error thrown
3315+ // Test4: isAppAlreadyInstalled returns app is not enabled (grep exit code 1)
3316+ // so app install will run and it should fail with non-FIPS error
3317+ mockPodExecReturnContexts [2 ].StdOut = "" // No stdout means grep didn't find ENABLED
3318+ mockPodExecReturnContexts [2 ].StdErr = "" // No stderr
3319+ mockPodExecReturnContexts [2 ].Err = fmt .Errorf ("exit status 1" ) // grep exit code 1 = pattern not found
3320+ mockPodExecReturnContexts [3 ].StdErr = "real installation error" // Non-FIPS error should still fail
33173321
33183322 localInstallCtxt .sem <- struct {}{}
33193323 waiter .Add (1 )
@@ -3322,9 +3326,9 @@ func TestPremiumAppScopedPlaybook(t *testing.T) {
33223326 t .Errorf ("Expected app install failed" )
33233327 }
33243328
3325- // Test5: install app should be successful but es post install fails
3326-
3327- mockPodExecReturnContexts [3 ].StdErr = "" //no error for app install
3329+ // Test5: FIPS message during install should be handled gracefully, but es post install fails
3330+ mockPodExecReturnContexts [ 3 ]. StdErr = "FIPS provider enabled. name: OpenSSL FIPS Provider, version: 3.0.9, buildinfo: 3.0.9, status: Success" // FIPS message should be ignored
3331+ mockPodExecReturnContexts [3 ].Err = nil // No actual error for install
33283332
33293333 localInstallCtxt .sem <- struct {}{}
33303334 waiter .Add (1 )
@@ -3343,7 +3347,24 @@ func TestPremiumAppScopedPlaybook(t *testing.T) {
33433347 t .Errorf ("Expected es post install succeeded but app arhive deletion failed" )
33443348 }
33453349
3346- // Test7: successful scenario where everything succeeds
3350+ // Test7: App already installed with FIPS message - should skip installation
3351+ // Reset all mock contexts for this test
3352+ mockPodExecReturnContexts [0 ].Err = nil // File exists check passes
3353+ mockPodExecReturnContexts [1 ].StdErr = "" // Get app name passes
3354+ mockPodExecReturnContexts [1 ].StdOut = "app1" // App name is found
3355+ mockPodExecReturnContexts [2 ].StdOut = "app1 CONFIGURED ENABLED VISIBLE" // App is already enabled
3356+ mockPodExecReturnContexts [2 ].StdErr = "FIPS provider enabled. name: OpenSSL FIPS Provider, version: 3.0.9, buildinfo: 3.0.9, status: Success"
3357+ mockPodExecReturnContexts [2 ].Err = nil // No error - app is found and enabled
3358+ // Install step should be skipped, but cleanup should still work
3359+ mockPodExecReturnContexts [5 ].StdErr = "" // Cleanup should succeed
3360+ localInstallCtxt .sem <- struct {}{}
3361+ waiter .Add (1 )
3362+ err = pCtx .runPlaybook (ctx )
3363+ if err != nil {
3364+ t .Errorf ("runPlayBook should not have returned error when app is already installed with FIPS message. err=%s" , err .Error ())
3365+ }
3366+
3367+ // Test8: successful scenario where everything succeeds
33473368 mockPodExecReturnContexts [5 ].StdErr = ""
33483369 localInstallCtxt .sem <- struct {}{}
33493370 waiter .Add (1 )
@@ -4462,3 +4483,126 @@ func TestAddTelAppCManager(t *testing.T) {
44624483 // Negative testing
44634484 addTelApp (ctx , mockPodExecClient , 2 , & crNew )
44644485}
4486+
4487+ func TestIsAppAlreadyInstalled (t * testing.T ) {
4488+ ctx := context .TODO ()
4489+
4490+ tests := []struct {
4491+ name string
4492+ stdOut string
4493+ stdErr string
4494+ err error
4495+ expectedResult bool
4496+ expectedError bool
4497+ description string
4498+ }{
4499+ {
4500+ name : "App is enabled - success case" ,
4501+ stdOut : "myapp CONFIGURED ENABLED VISIBLE" ,
4502+ stdErr : "" ,
4503+ err : nil ,
4504+ expectedResult : true ,
4505+ expectedError : false ,
4506+ description : "App is found and enabled" ,
4507+ },
4508+ {
4509+ name : "App not found - grep exit code 1" ,
4510+ stdOut : "" ,
4511+ stdErr : "" ,
4512+ err : fmt .Errorf ("command terminated with exit code 1" ),
4513+ expectedResult : false ,
4514+ expectedError : false ,
4515+ description : "App not enabled - grep pattern not found" ,
4516+ },
4517+ {
4518+ name : "App not found - Could not find object" ,
4519+ stdOut : "" ,
4520+ stdErr : "Could not find object id=myapp" ,
4521+ err : nil ,
4522+ expectedResult : false ,
4523+ expectedError : false ,
4524+ description : "App not installed at all" ,
4525+ },
4526+ {
4527+ name : "FIPS provider message with app enabled" ,
4528+ stdOut : "myapp CONFIGURED ENABLED VISIBLE" ,
4529+ stdErr : "FIPS provider enabled. name: OpenSSL FIPS Provider, version: 3.0.9, buildinfo: 3.0.9, status: Success" ,
4530+ err : nil ,
4531+ expectedResult : true ,
4532+ expectedError : false ,
4533+ description : "FIPS message should be ignored when app is enabled" ,
4534+ },
4535+ {
4536+ name : "FIPS provider message with app not enabled" ,
4537+ stdOut : "" ,
4538+ stdErr : "FIPS provider enabled. name: OpenSSL FIPS Provider, version: 3.0.9, buildinfo: 3.0.9, status: Success" ,
4539+ err : fmt .Errorf ("exit status 1" ),
4540+ expectedResult : false ,
4541+ expectedError : false ,
4542+ description : "FIPS message should be ignored, grep exit code 1 means not enabled" ,
4543+ },
4544+ {
4545+ name : "Real error - exit code 2" ,
4546+ stdOut : "" ,
4547+ stdErr : "Some real error occurred" ,
4548+ err : fmt .Errorf ("exit status 2" ),
4549+ expectedResult : false ,
4550+ expectedError : true ,
4551+ description : "Real error should be returned" ,
4552+ },
4553+ {
4554+ name : "Command succeeded but no output" ,
4555+ stdOut : "" ,
4556+ stdErr : "" ,
4557+ err : nil ,
4558+ expectedResult : false ,
4559+ expectedError : true ,
4560+ description : "Should error if command succeeds but no output" ,
4561+ },
4562+ }
4563+
4564+ for _ , tt := range tests {
4565+ t .Run (tt .name , func (t * testing.T ) {
4566+ // Create a test CR
4567+ cr := & enterpriseApi.Standalone {
4568+ ObjectMeta : metav1.ObjectMeta {
4569+ Name : "test-standalone" ,
4570+ Namespace : "test" ,
4571+ },
4572+ }
4573+
4574+ // Create mock pod exec client with CR
4575+ mockPodExecClient := & spltest.MockPodExecClient {Cr : cr }
4576+ mockPodExecClient .SetTargetPodName (ctx , "test-pod" )
4577+
4578+ // Set up the mock return context
4579+ mockReturnContext := & spltest.MockPodExecReturnContext {
4580+ StdOut : tt .stdOut ,
4581+ StdErr : tt .stdErr ,
4582+ Err : tt .err ,
4583+ }
4584+
4585+ // Add the mock command and return context - use the exact command pattern
4586+ command := "/opt/splunk/bin/splunk list app testapp -auth admin:`cat /mnt/splunk-secrets/password`| grep ENABLED"
4587+ mockPodExecClient .AddMockPodExecReturnContexts (ctx , []string {command }, mockReturnContext )
4588+
4589+ // Call the function
4590+ result , err := isAppAlreadyInstalled (ctx , cr , mockPodExecClient , "testapp" )
4591+
4592+ // Check results
4593+ if tt .expectedError {
4594+ if err == nil {
4595+ t .Errorf ("Expected error but got none for test: %s" , tt .description )
4596+ }
4597+ } else {
4598+ if err != nil {
4599+ t .Errorf ("Unexpected error for test '%s': %v" , tt .description , err )
4600+ }
4601+ }
4602+
4603+ if result != tt .expectedResult {
4604+ t .Errorf ("Expected result %v but got %v for test: %s" , tt .expectedResult , result , tt .description )
4605+ }
4606+ })
4607+ }
4608+ }
0 commit comments