diff --git a/test/integration/container_empty_folder_test.go b/test/integration/container_empty_folder_test.go new file mode 100644 index 000000000..07334373d --- /dev/null +++ b/test/integration/container_empty_folder_test.go @@ -0,0 +1,131 @@ +//go:build integration + +package integration + +import ( + "testing" + + "github.com/checkmarx/ast-cli/internal/commands/util/printer" + "github.com/checkmarx/ast-cli/internal/params" + "gotest.tools/assert" +) + +// TestContainerScan_EmptyFolderWithExternalImages tests scanning with empty folders and external container images +func TestContainerScan_EmptyFolderWithExternalImages(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/empty-folder.zip", + flag(params.ContainerImagesFlag), "nginx:alpine", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for empty folder with external images") + assert.Assert(t, projectID != "", "Project ID should not be empty for empty folder with external images") +} + +// TestContainerScan_EmptyFolderWithMultipleExternalImages tests scanning empty folder with multiple external images +func TestContainerScan_EmptyFolderWithMultipleExternalImages(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/empty-folder.zip", + flag(params.ContainerImagesFlag), "nginx:alpine,mysql:5.7,debian:9", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for empty folder with multiple external images") + assert.Assert(t, projectID != "", "Project ID should not be empty for empty folder with multiple external images") +} + +// TestContainerScan_EmptyFolderWithExternalImagesAndDebug tests with debug flag enabled +func TestContainerScan_EmptyFolderWithExternalImagesAndDebug(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/empty-folder.zip", + flag(params.ContainerImagesFlag), "mysql:5.7", + flag(params.BranchFlag), "dummy_branch", + flag(params.DebugFlag), + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for empty folder with debug flag") + assert.Assert(t, projectID != "", "Project ID should not be empty for empty folder with debug flag") +} + +// TestContainerScan_EmptyFolderWithComplexImageNames tests empty folder with complex image names +func TestContainerScan_EmptyFolderWithComplexImageNames(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/empty-folder.zip", + flag(params.ContainerImagesFlag), "docker.io/library/nginx:1.21.6,mysql:5.7.38", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for complex image names") + assert.Assert(t, projectID != "", "Project ID should not be empty for complex image names") +} + +// TestContainerScan_EmptyFolderWithRegistryImages tests empty folder with registry-prefixed images +func TestContainerScan_EmptyFolderWithRegistryImages(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/empty-folder.zip", + flag(params.ContainerImagesFlag), "checkmarx/kics:v2.1.11", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for registry images") + assert.Assert(t, projectID != "", "Project ID should not be empty for registry images") +} + +// TestContainerScan_EmptyFolderInvalidImageShouldFail tests that validation still works with empty folders +func TestContainerScan_EmptyFolderInvalidImageShouldFail(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/empty-folder.zip", + flag(params.ContainerImagesFlag), "invalid-image-without-tag", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for invalid image format with empty folder") + assertError(t, err, "image does not have a tag") +} + +// TestContainerScan_EmptyFolderMixedValidInvalidImages tests mixed valid/invalid images with empty folder +func TestContainerScan_EmptyFolderMixedValidInvalidImages(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/empty-folder.zip", + flag(params.ContainerImagesFlag), "nginx:alpine,invalid-image,mysql:5.7", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for mixed valid/invalid images with empty folder") + assertError(t, err, "image does not have a tag") +} diff --git a/test/integration/container_images_validation_test.go b/test/integration/container_images_validation_test.go new file mode 100644 index 000000000..217ca29f2 --- /dev/null +++ b/test/integration/container_images_validation_test.go @@ -0,0 +1,281 @@ +//go:build integration + +package integration + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/checkmarx/ast-cli/internal/commands/util/printer" + "github.com/checkmarx/ast-cli/internal/params" + "gotest.tools/assert" +) + +// TestContainerImageValidation_ValidFormats tests that valid container image formats are accepted +func TestContainerImageValidation_ValidFormats(t *testing.T) { + tests := []struct { + name string + imageFormat string + description string + }{ + { + name: "SimpleImageWithTag", + imageFormat: "nginx:alpine", + description: "Simple image with tag should be valid", + }, + { + name: "ImageWithRegistryAndTag", + imageFormat: "docker.io/nginx:1.21", + description: "Image with registry and tag should be valid", + }, + { + name: "ImageWithPortAndTag", + imageFormat: "localhost:5000/myapp:latest", + description: "Image with port and tag should be valid", + }, + { + name: "ImageWithNamespaceAndTag", + imageFormat: "checkmarx/kics:v2.1.11", + description: "Image with namespace and tag should be valid", + }, + { + name: "FullyQualifiedImageWithTag", + imageFormat: "registry.example.com:8080/namespace/myapp:v1.0.0", + description: "Fully qualified image with tag should be valid", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), tt.imageFormat, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for: "+tt.description) + assert.Assert(t, projectID != "", "Project ID should not be empty for: "+tt.description) + }) + } +} + +// TestContainerImageValidation_InvalidFormats tests that invalid container image formats are rejected +func TestContainerImageValidation_InvalidFormats(t *testing.T) { + tests := []struct { + name string + imageFormat string + description string + expectedError string + }{ + { + name: "ImageWithoutTag", + imageFormat: "nginx", + description: "Image without tag should be invalid", + expectedError: "image does not have a tag", + }, + { + name: "ImageWithEmptyTag", + imageFormat: "nginx:", + description: "Image with empty tag should be invalid", + expectedError: "Image name and tag cannot be empty", + }, + { + name: "ImageWithEmptyName", + imageFormat: ":alpine", + description: "Image with empty name should be invalid", + expectedError: "Image name and tag cannot be empty", + }, + { + name: "OnlyColon", + imageFormat: ":", + description: "Only colon should be invalid", + expectedError: "Image name and tag cannot be empty", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), tt.imageFormat, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for: "+tt.description) + if tt.expectedError != "" { + assertError(t, err, tt.expectedError) + } + }) + } +} + +// TestContainerImageValidation_MultipleImagesValidation tests validation with multiple container images +func TestContainerImageValidation_MultipleImagesValidation(t *testing.T) { + tests := []struct { + name string + imageList string + shouldSucceed bool + description string + }{ + { + name: "MultipleValidImages", + imageList: "nginx:alpine,mysql:5.7,debian:9", + shouldSucceed: true, + description: "Multiple valid images should pass", + }, + { + name: "OneInvalidAmongValid", + imageList: "nginx:alpine,invalid-image,mysql:5.7", + shouldSucceed: false, + description: "One invalid image among valid ones should fail", + }, + { + name: "LastImageInvalid", + imageList: "nginx:alpine,mysql:5.7,invalid:", + shouldSucceed: false, + description: "Last image being invalid should fail", + }, + { + name: "FirstImageInvalid", + imageList: ":invalid,nginx:alpine,mysql:5.7", + shouldSucceed: false, + description: "First image being invalid should fail", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), tt.imageList, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + + if tt.shouldSucceed { + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for: "+tt.description) + assert.Assert(t, projectID != "", "Project ID should not be empty for: "+tt.description) + } else { + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for: "+tt.description) + } + }) + } +} + +// TestContainerImageValidation_TarFiles tests validation of .tar file references +func TestContainerImageValidation_TarFiles(t *testing.T) { + // Create a temporary .tar file for testing + tempDir := t.TempDir() + validTarFile := filepath.Join(tempDir, "test-image.tar") + + // Create an empty .tar file for testing + f, err := os.Create(validTarFile) + assert.NilError(t, err, "Should create temp .tar file") + f.Close() + + tests := []struct { + name string + tarFile string + shouldSucceed bool + description string + }{ + { + name: "ValidTarFile", + tarFile: validTarFile, + shouldSucceed: true, + description: "Valid .tar file path should be accepted", + }, + { + name: "NonExistentTarFile", + tarFile: "/tmp/nonexistent-image.tar", + shouldSucceed: false, + description: "Non-existent .tar file should be rejected", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), tt.tarFile, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + + if tt.shouldSucceed { + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for: "+tt.description) + assert.Assert(t, projectID != "", "Project ID should not be empty for: "+tt.description) + } else { + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for: "+tt.description) + } + }) + } +} + +// TestContainerImageValidation_MixedTarAndRegularImages tests mixing .tar files with regular images +func TestContainerImageValidation_MixedTarAndRegularImages(t *testing.T) { + // Create a temporary .tar file for testing + tempDir := t.TempDir() + validTarFile := filepath.Join(tempDir, "test-image.tar") + + f, err := os.Create(validTarFile) + assert.NilError(t, err, "Should create temp .tar file") + f.Close() + + t.Run("ValidTarAndRegularImage", func(t *testing.T) { + createASTIntegrationTestCommand(t) + imageList := fmt.Sprintf("nginx:alpine,%s", validTarFile) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), imageList, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for mixed valid images") + assert.Assert(t, projectID != "", "Project ID should not be empty for mixed valid images") + }) + + t.Run("ValidTarAndInvalidRegularImage", func(t *testing.T) { + createASTIntegrationTestCommand(t) + imageList := fmt.Sprintf("nginx:,%s", validTarFile) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), imageList, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for mixed images with one invalid") + }) +} diff --git a/test/integration/container_scan_edge_cases_test.go b/test/integration/container_scan_edge_cases_test.go new file mode 100644 index 000000000..4d5054703 --- /dev/null +++ b/test/integration/container_scan_edge_cases_test.go @@ -0,0 +1,342 @@ +//go:build integration + +package integration + +import ( + "os" + "path/filepath" + "testing" + + "github.com/checkmarx/ast-cli/internal/commands/util/printer" + "github.com/checkmarx/ast-cli/internal/params" + "gotest.tools/assert" +) + +// TestContainerScan_ErrorHandling_InvalidAndValidScenarios tests error handling improvements +func TestContainerScan_ErrorHandling_InvalidAndValidScenarios(t *testing.T) { + t.Run("ValidScanWithAllParameters", func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/Dockerfile-mysql571.zip", + flag(params.ContainerImagesFlag), "nginx:alpine,mysql:5.7", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty") + assert.Assert(t, projectID != "", "Project ID should not be empty") + }) + + t.Run("InvalidImageInList", func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/Dockerfile-mysql571.zip", + flag(params.ContainerImagesFlag), "nginx:alpine,bad-image,mysql:5.7", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + } + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for invalid image in list") + }) +} + +// TestContainerScan_TarFileValidation tests .tar file validation scenarios +func TestContainerScan_TarFileValidation(t *testing.T) { + tempDir := t.TempDir() + + t.Run("ExistingTarFile", func(t *testing.T) { + // Create a dummy .tar file + tarFile := filepath.Join(tempDir, "test-container.tar") + f, err := os.Create(tarFile) + assert.NilError(t, err) + f.Close() + + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), tarFile, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for existing tar file") + assert.Assert(t, projectID != "", "Project ID should not be empty for existing tar file") + }) + + t.Run("NonExistentTarFile", func(t *testing.T) { + nonExistentTar := filepath.Join(tempDir, "nonexistent.tar") + + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), nonExistentTar, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + } + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for non-existent tar file") + }) + + t.Run("TarFileWithOtherImages", func(t *testing.T) { + // Create a dummy .tar file + tarFile := filepath.Join(tempDir, "another-test.tar") + f, err := os.Create(tarFile) + assert.NilError(t, err) + f.Close() + + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), "nginx:alpine," + tarFile, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for tar file with other images") + assert.Assert(t, projectID != "", "Project ID should not be empty for tar file with other images") + }) +} + +// TestContainerScan_SpecialCharactersInImageNames tests handling of special characters +func TestContainerScan_SpecialCharactersInImageNames(t *testing.T) { + tests := []struct { + name string + imageName string + shouldSucceed bool + description string + }{ + { + name: "ImageWithHyphen", + imageName: "my-image:latest", + shouldSucceed: true, + description: "Image with hyphen should be valid", + }, + { + name: "ImageWithUnderscore", + imageName: "my_image:v1.0", + shouldSucceed: true, + description: "Image with underscore should be valid", + }, + { + name: "ImageWithDots", + imageName: "my.image.name:1.2.3", + shouldSucceed: true, + description: "Image with dots should be valid", + }, + { + name: "ImageWithSlash", + imageName: "namespace/image:tag", + shouldSucceed: true, + description: "Image with namespace slash should be valid", + }, + { + name: "ComplexRegistry", + imageName: "registry.example.com:5000/namespace/image:v1.0.0", + shouldSucceed: true, + description: "Complex registry path should be valid", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), tt.imageName, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + + if tt.shouldSucceed { + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for: "+tt.description) + assert.Assert(t, projectID != "", "Project ID should not be empty for: "+tt.description) + } else { + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for: "+tt.description) + } + }) + } +} + +// TestContainerScan_ImageTagVariations tests different tag formats +func TestContainerScan_ImageTagVariations(t *testing.T) { + tests := []struct { + name string + imageName string + shouldSucceed bool + description string + }{ + { + name: "LatestTag", + imageName: "nginx:latest", + shouldSucceed: true, + description: "Latest tag should be valid", + }, + { + name: "NumericTag", + imageName: "nginx:1.21", + shouldSucceed: true, + description: "Numeric tag should be valid", + }, + { + name: "SemanticVersionTag", + imageName: "nginx:1.21.6", + shouldSucceed: true, + description: "Semantic version tag should be valid", + }, + { + name: "AlphanumericTag", + imageName: "nginx:v2.1.11", + shouldSucceed: true, + description: "Alphanumeric tag with prefix should be valid", + }, + { + name: "TagWithHyphen", + imageName: "nginx:stable-alpine", + shouldSucceed: true, + description: "Tag with hyphen should be valid", + }, + { + name: "SHA256Tag", + imageName: "nginx:sha256", + shouldSucceed: true, + description: "SHA256 tag should be valid", + }, + { + name: "TagWithUnderscore", + imageName: "nginx:stable_release", + shouldSucceed: true, + description: "Tag with underscore should be valid", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), tt.imageName, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + + if tt.shouldSucceed { + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for: "+tt.description) + assert.Assert(t, projectID != "", "Project ID should not be empty for: "+tt.description) + } else { + err, _ := executeCommand(t, testArgs...) + assert.Assert(t, err != nil, "Expected error for: "+tt.description) + } + }) + } +} + +// TestContainerScan_BoundaryConditions tests boundary conditions +func TestContainerScan_BoundaryConditions(t *testing.T) { + t.Run("SingleCharacterImageName", func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), "a:b", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for single character image") + assert.Assert(t, projectID != "", "Project ID should not be empty for single character image") + }) + + t.Run("VeryLongImageName", func(t *testing.T) { + longName := "verylongimagenamethatshouldstillbevalidaslongasithasapropertagformat:v1.0.0" + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), longName, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for long image name") + assert.Assert(t, projectID != "", "Project ID should not be empty for long image name") + }) + + t.Run("ManyImagesInList", func(t *testing.T) { + // Test with 5 images (reduced from 10 to avoid timeout) + images := "nginx:1,nginx:2,nginx:3,nginx:4,nginx:5" + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), images, + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for many images") + assert.Assert(t, projectID != "", "Project ID should not be empty for many images") + }) +} + +// TestContainerScan_CombinedWithOtherScanTypes tests container scans combined with other scan types +func TestContainerScan_CombinedWithOtherScanTypes(t *testing.T) { + t.Run("ContainerAndIaC", func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/iac-insecure.zip", + flag(params.ContainerImagesFlag), "nginx:alpine", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag + "," + params.IacType, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for combined scan") + assert.Assert(t, projectID != "", "Project ID should not be empty for combined scan") + }) + + t.Run("ContainerWithSCA", func(t *testing.T) { + createASTIntegrationTestCommand(t) + testArgs := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.ContainerImagesFlag), "nginx:alpine", + flag(params.BranchFlag), "dummy_branch", + flag(params.ScanTypes), params.ContainersTypeFlag + "," + params.ScaType, + flag(params.ScanInfoFormatFlag), printer.FormatJSON, + } + scanID, projectID := executeCreateScan(t, testArgs) + assert.Assert(t, scanID != "", "Scan ID should not be empty for container+sca scan") + assert.Assert(t, projectID != "", "Project ID should not be empty for container+sca scan") + }) +}