// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License. //go:build darwin // +build darwin // Package application contains a application gatherer. package application import ( "fmt" "testing" "github.com/aws/amazon-ssm-agent/agent/mocks/context" "github.com/aws/amazon-ssm-agent/agent/plugins/inventory/model" "github.com/stretchr/testify/assert" ) var ( sampleData = ` install-location private/tmp install-time 1581382444 pkg-version 16.17.18080304 pkgid randomPackage-1 receipt-plist-version 1 volume / install-location Applications install-time 1581382096 pkg-version 16.31.19111002 pkgid randomPackage-2 receipt-plist-version 1 volume / ` unexpectedSampleData = `test` sampleDataPackages = ` install-location private/tmp install-time 1581382444 pkg-version 16.17.18080304 pkgid randomPackage-1 receipt-plist-version 1 volume / install-location Applications install-time 1581382096 pkg-version 16.31.19111002 pkgid randomPackage-2 receipt-plist-version 1 volume / ` applicationSampleData = ` _name Calendar has64BitIntelCode yes lastModified 2019-04-03T07:20:22Z obtained_from apple path /Applications/Calendar.app runtime_environment arch_x86 test-key signed_by Software Signing Apple Code Signing Certification Authority Apple Root CA version 11.0` applicationSampleDataWrapper = ` _SPCommandLineArguments /usr/sbin/system_profiler -nospawn -xml SPApplicationsDataType -detailLevel full _SPCompletionInterval 2.8108129501342773 _SPResponseTime 2.9170479774475098 _dataType SPApplicationsDataType _detailLevel 1 _items _name Calendar has64BitIntelCode yes lastModified 2019-04-03T07:20:22Z obtained_from apple path /Applications/Calendar.app runtime_environment arch_x86 signed_by Software Signing Apple Code Signing Certification Authority Apple Root CA version 11.0 _name Amazon Chime has64BitIntelCode yes lastModified 2020-02-06T22:52:21Z obtained_from identified_developer path /Applications/Amazon Chime.app runtime_environment arch_x86 signed_by Developer ID Application: AMZN Mobile LLC (94KV3E626L) Developer ID Certification Authority Apple Root CA version 4.28.7255 ` ) var sampleDataParsed = []model.ApplicationData{ { Name: "Calendar", Version: "11.0", Release: "", Epoch: "", Publisher: "apple", ApplicationType: "", Architecture: "arch_x86", URL: "", Summary: "", PackageId: "", }, { Name: "Amazon Chime", Version: "4.28.7255", Release: "", Epoch: "", Publisher: "identified_developer", ApplicationType: "", Architecture: "arch_x86", URL: "", Summary: "", PackageId: "", }, } var sampleDataPackagesParsed = []model.ApplicationData{ { Name: "randomPackage-1", Version: "16.17.18080304", InstalledTime: "2020-02-11T00:54:04Z", Release: "", Epoch: "", Publisher: "", ApplicationType: "", Architecture: "", URL: "", Summary: "", PackageId: "", }, { Name: "randomPackage-2", Version: "16.31.19111002", InstalledTime: "2020-02-11T00:48:16Z", Release: "", Epoch: "", Publisher: "", ApplicationType: "", Architecture: "", URL: "", Summary: "", PackageId: "", }, } var unexpectedSampleDataParsed = []model.ApplicationData{} func MockTestExecutorWithError(command string, args ...string) ([]byte, error) { var result []byte return result, fmt.Errorf("random error") } func MockTestExecutorWithoutError(command string, args ...string) ([]byte, error) { return []byte(sampleData), nil } func TestConvertToApplicationData(t *testing.T) { data, err := convertToApplicationData(applicationSampleDataWrapper) assert.Nil(t, err, "Check conversion logic - since sample data in unit test is tied to implementation") assertEqual(t, sampleDataParsed, data) data, err = convertToApplicationData(unexpectedSampleData) assertEqual(t, unexpectedSampleDataParsed, data) } func TestGetApplicationData(t *testing.T) { var data []model.ApplicationData var err error //setup mockContext := context.NewMockDefault() mockCommand := "RandomCommand" mockArgs := []string{ "RandomArgument-1", "RandomArgument-2", } //testing with error cmdExecutor = MockTestExecutorWithError data, err = getApplicationData(mockContext, mockCommand, mockArgs) assert.NotNil(t, err, "Error must be thrown when command execution fails") assert.Equal(t, 0, len(data), "When command execution fails - application dataset must be empty") //testing without error cmdExecutor = func(command string, args ...string) ([]byte, error) { return []byte(applicationSampleDataWrapper), nil } data, err = getApplicationData(mockContext, mockCommand, mockArgs) assert.Nil(t, err, "Error must not be thrown with MockTestExecutorWithoutError") assertEqual(t, sampleDataParsed, data) } func TestCollectApplicationData(t *testing.T) { mockContext := context.NewMockDefault() // sysctl return result without error cmdExecutor = func(command string, args ...string) ([]byte, error) { return []byte(applicationSampleDataWrapper), nil } data := collectPlatformDependentApplicationData(mockContext) assertEqual(t, sampleDataParsed, data) // sysctl return errors cmdExecutor = MockTestExecutorWithError data = collectPlatformDependentApplicationData(mockContext) assert.Equal(t, 0, len(data), "When command execution fails - application dataset must be empty") } func TestConvertToApplicationDataFromInstalledPkg(t *testing.T) { data, err := convertToApplicationDataFromInstalledPkg(sampleDataPackages) assert.Nil(t, err, "Check conversion logic - since sample data in unit test is tied to implementation") assertEqual(t, sampleDataPackagesParsed, data) data, err = convertToApplicationDataFromInstalledPkg(unexpectedSampleData) assertEqual(t, unexpectedSampleDataParsed, data) } func TestGetInstalledPackages(t *testing.T) { var data []model.ApplicationData var err error //setup mockContext := context.NewMockDefault() mockCommand := "RandomCommand" //testing with error cmdExecutor = MockTestExecutorWithError data, err = getInstalledPackages(mockContext, mockCommand) assert.NotNil(t, err, "Error must be thrown when command execution fails") assert.Equal(t, 0, len(data), "When command execution fails - application dataset must be empty") //testing without error cmdExecutor = MockTestExecutorWithoutError data, err = getInstalledPackages(mockContext, mockCommand) assert.Nil(t, err, "Error must not be thrown with MockTestExecutorWithoutError") assertEqual(t, sampleDataPackagesParsed, data) } func TestGetFieldValue(t *testing.T) { name := getFieldValue(applicationSampleData, "_name", "string") assert.Equal(t, "Calendar", name) version := getFieldValue(applicationSampleData, "version", "string") assert.Equal(t, "11.0", version) publisher := getFieldValue(applicationSampleData, "obtained_from", "string") assert.Equal(t, "apple", publisher) architecture := getFieldValue(applicationSampleData, "runtime_environment", "string") assert.Equal(t, "arch_x86", architecture) // test: if key doesn't exist return empty string randomField := getFieldValue(applicationSampleData, "random_field", "string") assert.Equal(t, "", randomField) // if value doesn't exist for the key, return empty string negTestVal := getFieldValue(applicationSampleData, "test-key", "string") assert.Equal(t, "", negTestVal) }