// 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)
}