Skip to content

Commit 2dbf5b4

Browse files
committed
Enhance macOS support in createCommandWithEnhancedPath and add related tests
1 parent 64ca580 commit 2dbf5b4

File tree

4 files changed

+266
-6
lines changed

4 files changed

+266
-6
lines changed

internal/services/realtimeengine/iacrealtime/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ const (
1515

1616
// engineVerifyTimeout is the timeout in seconds for verifying container engine availability
1717
engineVerifyTimeout = 5
18+
19+
// OS constants
20+
osLinux = "linux"
1821
)
1922

2023
// macOSDockerFallbackPaths contains additional paths to check for Docker on macOS

internal/services/realtimeengine/iacrealtime/container-manager.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"os"
55
"os/exec"
66
"path/filepath"
7-
"runtime"
87
"strings"
98

109
"github.com/checkmarx/ast-cli/internal/commands/util"
@@ -44,7 +43,7 @@ func createCommandWithEnhancedPath(enginePath string, args ...string) *exec.Cmd
4443
cmd := exec.Command(enginePath, args...)
4544

4645
// Only enhance PATH on macOS
47-
if runtime.GOOS != osDarwin {
46+
if getOS() != osDarwin {
4847
return cmd
4948
}
5049

internal/services/realtimeengine/iacrealtime/container-manager_test.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package iacrealtime
22

33
import (
4+
"os"
45
"os/exec"
6+
"path/filepath"
57
"strings"
68
"testing"
79

@@ -544,3 +546,133 @@ func TestCreateCommandWithEnhancedPath_EnvIsSet(t *testing.T) {
544546
}
545547
}
546548
}
549+
550+
// ============================================================================
551+
// Tests for createCommandWithEnhancedPath on macOS (mocked)
552+
// ============================================================================
553+
554+
func TestCreateCommandWithEnhancedPath_MacOS_EnhancesPath(t *testing.T) {
555+
// Mock OS to be macOS
556+
origGOOS := getOS
557+
defer func() { getOS = origGOOS }()
558+
getOS = func() string { return osDarwin }
559+
560+
// Create a temp directory that exists (to be added to PATH)
561+
tempDir := t.TempDir()
562+
enginePath := filepath.Join(tempDir, "docker")
563+
564+
cmd := createCommandWithEnhancedPath(enginePath, "run", "--rm")
565+
566+
if cmd == nil {
567+
t.Fatal("createCommandWithEnhancedPath should not return nil")
568+
}
569+
570+
// On macOS, Env should be set
571+
if cmd.Env == nil {
572+
t.Error("On macOS, cmd.Env should be set with enhanced PATH")
573+
}
574+
575+
// Verify PATH is in the environment
576+
foundPath := false
577+
for _, e := range cmd.Env {
578+
if strings.HasPrefix(e, "PATH=") {
579+
foundPath = true
580+
// Verify the engine directory is in the PATH
581+
if !strings.Contains(e, tempDir) {
582+
t.Errorf("Enhanced PATH should contain engine directory %s, got %s", tempDir, e)
583+
}
584+
break
585+
}
586+
}
587+
if !foundPath {
588+
t.Error("Enhanced PATH should contain PATH= entry")
589+
}
590+
}
591+
592+
func TestCreateCommandWithEnhancedPath_MacOS_AddsDockerPaths(t *testing.T) {
593+
// Mock OS to be macOS
594+
origGOOS := getOS
595+
defer func() { getOS = origGOOS }()
596+
getOS = func() string { return osDarwin }
597+
598+
cmd := createCommandWithEnhancedPath("/usr/local/bin/docker", "--version")
599+
600+
if cmd == nil {
601+
t.Fatal("createCommandWithEnhancedPath should not return nil")
602+
}
603+
604+
// On macOS, Env should be set
605+
if cmd.Env == nil {
606+
t.Error("On macOS, cmd.Env should be set")
607+
}
608+
}
609+
610+
func TestCreateCommandWithEnhancedPath_MacOS_DeduplicatesPaths(t *testing.T) {
611+
// Mock OS to be macOS
612+
origGOOS := getOS
613+
defer func() { getOS = origGOOS }()
614+
getOS = func() string { return osDarwin }
615+
616+
// Set PATH to include one of the fallback paths
617+
oldPath := os.Getenv("PATH")
618+
defer func() { _ = os.Setenv("PATH", oldPath) }()
619+
_ = os.Setenv("PATH", "/usr/local/bin:/usr/bin")
620+
621+
cmd := createCommandWithEnhancedPath("/usr/local/bin/docker", "--version")
622+
623+
if cmd == nil {
624+
t.Fatal("createCommandWithEnhancedPath should not return nil")
625+
}
626+
627+
// Verify PATH doesn't have duplicates
628+
for _, e := range cmd.Env {
629+
if strings.HasPrefix(e, "PATH=") {
630+
pathValue := strings.TrimPrefix(e, "PATH=")
631+
parts := strings.Split(pathValue, string(os.PathListSeparator))
632+
seen := make(map[string]int)
633+
for _, p := range parts {
634+
seen[p]++
635+
if seen[p] > 1 {
636+
t.Errorf("PATH contains duplicate entry: %s", p)
637+
}
638+
}
639+
break
640+
}
641+
}
642+
}
643+
644+
func TestCreateCommandWithEnhancedPath_NonMacOS_NoEnhancement(t *testing.T) {
645+
// Mock OS to be Linux
646+
origGOOS := getOS
647+
defer func() { getOS = origGOOS }()
648+
getOS = func() string { return osLinux }
649+
650+
cmd := createCommandWithEnhancedPath("/usr/bin/docker", "run")
651+
652+
if cmd == nil {
653+
t.Fatal("createCommandWithEnhancedPath should not return nil")
654+
}
655+
656+
// On non-macOS, Env should be nil (uses parent environment)
657+
if cmd.Env != nil {
658+
t.Error("On non-macOS, cmd.Env should be nil")
659+
}
660+
}
661+
662+
func TestCreateCommandWithEnhancedPath_Windows_NoEnhancement(t *testing.T) {
663+
// Mock OS to be Windows
664+
origGOOS := getOS
665+
defer func() { getOS = origGOOS }()
666+
getOS = func() string { return osWindows }
667+
668+
cmd := createCommandWithEnhancedPath("C:\\Program Files\\Docker\\docker.exe", "run")
669+
670+
if cmd == nil {
671+
t.Fatal("createCommandWithEnhancedPath should not return nil")
672+
}
673+
674+
// On Windows, Env should be nil (uses parent environment)
675+
if cmd.Env != nil {
676+
t.Error("On Windows, cmd.Env should be nil")
677+
}
678+
}

internal/services/realtimeengine/iacrealtime/iac-realtime_test.go

Lines changed: 130 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ func TestGetFallbackPaths_Podman_Darwin(t *testing.T) {
600600
func TestGetFallbackPaths_NonDarwin(t *testing.T) {
601601
origGOOS := getOS
602602
defer func() { getOS = origGOOS }()
603-
getOS = func() string { return "linux" }
603+
getOS = func() string { return osLinux }
604604

605605
fallbackDir := testFallbackDir
606606
paths := getFallbackPaths(engineDocker, fallbackDir)
@@ -778,7 +778,7 @@ func TestGetFallbackPaths_PrimaryPathFirst(t *testing.T) {
778778
func TestGetFallbackPaths_EmptyFallbackDir(t *testing.T) {
779779
origGOOS := getOS
780780
defer func() { getOS = origGOOS }()
781-
getOS = func() string { return "linux" }
781+
getOS = func() string { return osLinux }
782782

783783
paths := getFallbackPaths(engineDocker, "")
784784

@@ -876,7 +876,7 @@ func TestEngineNameResolution_FallbackPathsChecked(t *testing.T) {
876876

877877
origGOOS := getOS
878878
defer func() { getOS = origGOOS }()
879-
getOS = func() string { return "linux" }
879+
getOS = func() string { return osLinux }
880880

881881
// Save and clear PATH
882882
oldPath := os.Getenv("PATH")
@@ -899,7 +899,7 @@ func TestEngineNameResolution_FallbackPathsChecked(t *testing.T) {
899899
func TestEngineNameResolution_EmptyEngineName(t *testing.T) {
900900
origGOOS := getOS
901901
defer func() { getOS = origGOOS }()
902-
getOS = func() string { return "linux" }
902+
getOS = func() string { return osLinux }
903903

904904
// Save and clear PATH
905905
oldPath := os.Getenv("PATH")
@@ -1079,3 +1079,129 @@ func TestFilterIgnoredFindings_PreservesOrder(t *testing.T) {
10791079
}
10801080
}
10811081
}
1082+
1083+
// ============================================================================
1084+
// Tests for loadIgnoredIacFindings function
1085+
// ============================================================================
1086+
1087+
func TestLoadIgnoredIacFindings_ValidFile(t *testing.T) {
1088+
tempDir := t.TempDir()
1089+
testFile := filepath.Join(tempDir, "ignored.json")
1090+
1091+
// Create valid JSON file
1092+
content := `[
1093+
{"title": "Finding1", "similarityId": "id1"},
1094+
{"title": "Finding2", "similarityId": "id2"}
1095+
]`
1096+
err := os.WriteFile(testFile, []byte(content), 0644)
1097+
if err != nil {
1098+
t.Fatalf("Failed to create test file: %v", err)
1099+
}
1100+
1101+
findings, err := loadIgnoredIacFindings(testFile)
1102+
1103+
if err != nil {
1104+
t.Errorf("Expected no error, got %v", err)
1105+
}
1106+
1107+
if len(findings) != 2 {
1108+
t.Errorf("Expected 2 findings, got %d", len(findings))
1109+
}
1110+
1111+
if findings[0].Title != "Finding1" || findings[0].SimilarityID != "id1" {
1112+
t.Errorf("First finding mismatch: %+v", findings[0])
1113+
}
1114+
}
1115+
1116+
func TestLoadIgnoredIacFindings_EmptyArray(t *testing.T) {
1117+
tempDir := t.TempDir()
1118+
testFile := filepath.Join(tempDir, "ignored.json")
1119+
1120+
err := os.WriteFile(testFile, []byte("[]"), 0644)
1121+
if err != nil {
1122+
t.Fatalf("Failed to create test file: %v", err)
1123+
}
1124+
1125+
findings, err := loadIgnoredIacFindings(testFile)
1126+
1127+
if err != nil {
1128+
t.Errorf("Expected no error, got %v", err)
1129+
}
1130+
1131+
if len(findings) != 0 {
1132+
t.Errorf("Expected 0 findings, got %d", len(findings))
1133+
}
1134+
}
1135+
1136+
func TestLoadIgnoredIacFindings_InvalidJSON(t *testing.T) {
1137+
tempDir := t.TempDir()
1138+
testFile := filepath.Join(tempDir, "ignored.json")
1139+
1140+
err := os.WriteFile(testFile, []byte("not valid json"), 0644)
1141+
if err != nil {
1142+
t.Fatalf("Failed to create test file: %v", err)
1143+
}
1144+
1145+
_, err = loadIgnoredIacFindings(testFile)
1146+
1147+
if err == nil {
1148+
t.Error("Expected error for invalid JSON, got nil")
1149+
}
1150+
}
1151+
1152+
func TestLoadIgnoredIacFindings_FileNotFound(t *testing.T) {
1153+
_, err := loadIgnoredIacFindings("/non/existent/file.json")
1154+
1155+
if err == nil {
1156+
t.Error("Expected error for non-existent file, got nil")
1157+
}
1158+
}
1159+
1160+
func TestLoadIgnoredIacFindings_EmptyFile(t *testing.T) {
1161+
tempDir := t.TempDir()
1162+
testFile := filepath.Join(tempDir, "ignored.json")
1163+
1164+
err := os.WriteFile(testFile, []byte(""), 0644)
1165+
if err != nil {
1166+
t.Fatalf("Failed to create test file: %v", err)
1167+
}
1168+
1169+
_, err = loadIgnoredIacFindings(testFile)
1170+
1171+
if err == nil {
1172+
t.Error("Expected error for empty file, got nil")
1173+
}
1174+
}
1175+
1176+
// ============================================================================
1177+
// Additional tests for verifyEnginePath function - testing with real executable
1178+
// ============================================================================
1179+
1180+
func TestVerifyEnginePath_DirectoryInsteadOfFile(t *testing.T) {
1181+
tempDir := t.TempDir()
1182+
1183+
result := verifyEnginePath(tempDir)
1184+
if result {
1185+
t.Error("verifyEnginePath should return false for directory path")
1186+
}
1187+
}
1188+
1189+
func TestVerifyEnginePath_ValidSystemExecutable(t *testing.T) {
1190+
// Test with a known system executable
1191+
var execPath string
1192+
if runtime.GOOS == osWindows {
1193+
execPath = "C:\\Windows\\System32\\cmd.exe"
1194+
} else {
1195+
execPath = "/bin/sh"
1196+
}
1197+
1198+
// Check if the executable exists first
1199+
if _, err := os.Stat(execPath); err != nil {
1200+
t.Skipf("System executable not found: %s", execPath)
1201+
}
1202+
1203+
result := verifyEnginePath(execPath)
1204+
if !result {
1205+
t.Errorf("verifyEnginePath should return true for valid executable: %s", execPath)
1206+
}
1207+
}

0 commit comments

Comments
 (0)