Skip to content

Commit 2094139

Browse files
committed
feat(testing): Adds support for composer api package detection
1 parent d0031ca commit 2094139

File tree

2 files changed

+102
-30
lines changed

2 files changed

+102
-30
lines changed

daemon/internal/newrelic/integration/php_packages.go

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ type PhpPackagesCollection struct {
3131
// PHP packages config describes how to collect the JSON for the packages installed
3232
// for the current test case
3333
type PhpPackagesConfiguration struct {
34-
path string
35-
command string
36-
supportedListFile string
37-
overrideVersionsFile string
38-
expectedPackages []string
39-
packageNameOnly []string
34+
path string //
35+
command string // command to run to detect packages
36+
supportedListFile string // JSON file containing list of packages we expect agent to detect
37+
overrideVersionsFile string // JSON file containing overrides for expected package versions
38+
expectedPackages []string // manual override of packages we expect to detect
39+
packageNameOnly []string // list of packages which only have a name because agent cannot determine the version
40+
expectAllDetected bool // flag to indicate we expect all packages detected by the command "command"
4041
}
4142

4243
// composer package JSON
@@ -159,16 +160,22 @@ func NewPhpPackagesCollection(path string, config []byte) (*PhpPackagesCollectio
159160
}
160161
}
161162

162-
if supportedOK && expectedOK {
163-
return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - cannot specify 'supported_packages' and 'expected packages' - got %+v", params)
163+
// or "expect_all" which means we expect the agent to detect all the packages that the "command" option would detect
164+
_, expectAllOK := params["expect_all"]
165+
166+
if (supportedOK && expectedOK) || (supportedOK && expectAllOK) || (expectedOK && expectAllOK) {
167+
return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify one of 'supported_packages', "+
168+
"'expected packages' or 'expect_all' - got %+v", params)
164169
}
165170

166-
if !supportedOK && !expectedOK {
167-
return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'supported_packages' or 'expected packages' - got %+v", params)
171+
if !supportedOK && !expectedOK && !expectAllOK {
172+
return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'supported_packages' or 'expected packages' "+
173+
"or 'expect_all' - got %+v", params)
168174
}
169175

170-
if supportedOK && !commandOK {
171-
return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'command' option with `supported_packages` - got %+v", params)
176+
if (supportedOK || expectAllOK) && !commandOK {
177+
return nil, fmt.Errorf("Improper EXPECT_PHP_PACKAGES config - must specify 'command' option with `supported_packages` / "+
178+
"'expect_all' - got %+v", params)
172179
}
173180

174181
// optional option to specify which packages will only have a name because agent cannot determine the version
@@ -209,7 +216,8 @@ func NewPhpPackagesCollection(path string, config []byte) (*PhpPackagesCollectio
209216
supportedListFile: supportedListFile,
210217
overrideVersionsFile: overrideVersionsFile,
211218
expectedPackages: expectedPackagesArr,
212-
packageNameOnly: packageNameOnlyArr},
219+
packageNameOnly: packageNameOnlyArr,
220+
expectAllDetected: expectAllOK},
213221
}
214222

215223
return p, nil
@@ -296,18 +304,27 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro
296304
var supported []string
297305

298306
// get list of packages we expected the agent to detect
299-
// this can be one of 2 scenarios:
307+
// this can be one of 3 scenarios:
300308
// 1) test case used the "supported_packages" option which gives a JSON file which
301309
// lists all the packages the agent can detect
302310
// 2) test case used the "expected_packages" options which provides a comma separated
303311
// list of packages we expect the agent to detect
312+
// 3) test case used the "expect_all" option which means we expect the agent to
313+
// detect all the packages that the "command" option would detect
314+
//
315+
// Options #1 and #2 are mutually exclusive, and are intended for testing the legacy VM detection
316+
// mechanism where the agent looks for "magic" files of a package and examinew internals of the
317+
// package to determine its version.
304318
//
305-
// Option #1 is preferable as it provides the most comprehensive view of what the agent can do.
319+
// Option #1 is preferable when it available as it provides the most comprehensive view of what the agent can do.
306320
//
307321
// Option #2 is needed because some test cases do not exercise all the packages which are
308322
// installed and so the agent will not detect everything for that test case run which it could
309323
// theorectically detect if the test case used all the available packages installed.
310324
//
325+
// Option #3 is used when testing the agent's ability to detect packages using the Composer API. In
326+
// this case we expect the agent to detect the exact same packages as composer would detect.
327+
//
311328
// Once the list of packages the agent is expected to detect is created it is used to filter
312329
// down the package list returned by running the "command" (usually composer) option for the
313330
// test case provided.
@@ -318,8 +335,9 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro
318335
}
319336
} else if 0 < len(pkgs.config.expectedPackages) {
320337
supported = pkgs.config.expectedPackages
321-
} else {
322-
return nil, fmt.Errorf("Error determining expected packages - supported_packages and expected_packages are both empty")
338+
} else if !pkgs.config.expectAllDetected {
339+
return nil, fmt.Errorf("Error determining expected packages - supported_packages and expected_packages are both empty " +
340+
"and expect_all is false")
323341
}
324342

325343
splitCmd := strings.Split(pkgs.config.command, " ")
@@ -338,8 +356,8 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro
338356
detected := ComposerJSON{}
339357
json.Unmarshal([]byte(out), &detected)
340358
for _, v := range detected.Installed {
341-
//fmt.Printf("composer detected %s %s\n", v.Name, v.Version)
342-
if StringSliceContains(supported, v.Name) {
359+
fmt.Printf("composer detected %s %s\n", v.Name, v.Version)
360+
if pkgs.config.expectAllDetected || StringSliceContains(supported, v.Name) {
343361
var version string
344362

345363
// remove any 'v' from front of version string
@@ -349,9 +367,9 @@ func (pkgs *PhpPackagesCollection) GatherInstalledPackages() ([]PhpPackage, erro
349367
version = v.Version
350368
}
351369
pkgs.packages = append(pkgs.packages, PhpPackage{v.Name, version})
352-
//fmt.Printf(" -> %s in supported!\n", v.Name)
370+
fmt.Printf(" -> %s in supported!\n", v.Name)
353371
} else {
354-
//fmt.Printf(" -> %s NOT in supported!\n", v.Name)
372+
fmt.Printf(" -> %s NOT in supported!\n", v.Name)
355373
}
356374
}
357375
} else if 1 < len(splitCmd) && "wp-cli.phar" == splitCmd[1] {

daemon/internal/newrelic/integration/test.go

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) {
637637
var expectedPkgsCollection *PhpPackagesCollection
638638
var expectNullPkgs bool
639639
var version_overrides map[string]interface{}
640+
var subsetMatch bool = false
640641

641642
if nil != t.phpPackagesConfig {
642643
var err error
@@ -656,6 +657,19 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) {
656657
t.Fatal(err)
657658
return
658659
}
660+
661+
// Determine if we expect an exact match between expected and actual packages
662+
// when using the composer API in the agent to detect packages it is possible
663+
// it will return packages which the "composer show" command does not. These
664+
// are usually virtual or other types of packages which "composer show"
665+
// decides to not show.
666+
//
667+
// Currently the test handles this by listing all the packages that the agent
668+
// detected but "composer show" did not as "Notes" on the test results.
669+
//
670+
// Tests for the legacy package detection mechanism still expect an exact match.
671+
//
672+
subsetMatch = expectedPkgsCollection.config.expectAllDetected
659673
}
660674
} else {
661675
// no configuration given for package (no EXPECT_PHP_PACKAGES in test case) so don't run test
@@ -690,22 +704,44 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) {
690704
if nil == expectedPackages {
691705
t.Fail(fmt.Errorf("No expected PHP packages, harvest contains %+v\n", actualPackages))
692706
}
693-
// compare expected and actual lists of packages
707+
// Compare expected and actual lists of packages
694708
// since package names should be identical, iterate over
695709
// expected list and compare element by element with same
696710
// position in actual list. Name and version should match.
697711
// this works because the functions which generate these
698712
// lists sort them by package name for us
699-
if len(expectedPackages) != len(actualPackages) {
713+
//
714+
// As explained above - if testing results from the Composer API
715+
// agent detection mechanism, we may not get an exact match so
716+
// we relax the test that the lengths of the two lists are the same
717+
if !subsetMatch && (len(expectedPackages) != len(actualPackages)) {
700718
t.Fail(fmt.Errorf("Expected and actual php packages differ in length %d vs %d: expected %+v actual %+v",
701719
len(expectedPackages), len(actualPackages), expectedPackages, actualPackages))
702720
return
703721
}
704722
for i, _ := range expectedPackages {
705-
if expectedPackages[i].Name == actualPackages[i].Name {
723+
var matchingIdx int = -1
724+
for j, pkg := range actualPackages {
725+
fmt.Printf("Comparing %s to %s\n", pkg.Name, expectedPackages[i].Name)
726+
if pkg.Name == expectedPackages[i].Name {
727+
fmt.Printf("Match - index = %d\n", j)
728+
matchingIdx = j
729+
break
730+
}
731+
}
732+
733+
fmt.Printf("MatchingIdx: %d\n", matchingIdx)
734+
fmt.Printf("expectedPatckages[%d]: %+v\n", i, expectedPackages[i])
735+
if -1 != matchingIdx {
736+
fmt.Printf("actualPackages[%d]: %+v\n", matchingIdx, actualPackages[matchingIdx])
737+
} else {
738+
fmt.Printf("no match in actualPackages!\n")
739+
}
740+
741+
if -1 != matchingIdx {
706742
testPackageNameOnly := false
707743
if nil != expectedPkgsCollection.config.packageNameOnly {
708-
testPackageNameOnly = StringSliceContains(expectedPkgsCollection.config.packageNameOnly, actualPackages[i].Name)
744+
testPackageNameOnly = StringSliceContains(expectedPkgsCollection.config.packageNameOnly, actualPackages[matchingIdx].Name)
709745
if testPackageNameOnly {
710746
t.AddNote(fmt.Sprintf("Tested package name only for packages: %+v", expectedPkgsCollection.config.packageNameOnly))
711747
}
@@ -720,23 +756,41 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) {
720756
}
721757

722758
if testPackageNameOnly {
723-
if " " != actualPackages[i].Version {
759+
if " " != actualPackages[matchingIdx].Version {
724760
t.Fail(fmt.Errorf("Expected no package version and a package version was detected - expected \" \" actual %+v. ",
725-
actualPackages[i].Version))
761+
actualPackages[matchingIdx].Version))
726762
return
727763
} else {
728764
continue
729765
}
730766
}
731767

732-
if expected_version == actualPackages[i].Version {
768+
if expected_version == actualPackages[matchingIdx].Version {
733769
continue
770+
} else {
771+
t.Fail(fmt.Errorf("Expected version %s does not match actual version %s for package %s",
772+
expected_version, actualPackages[matchingIdx].Version, expectedPackages[i].Name))
773+
return
734774
}
735775
}
736-
t.Fail(fmt.Errorf("Expected and actual Php packages do not match: expected %+v actual %+v. Complete expected %v actual %v.",
737-
expectedPackages[i], actualPackages[i], expectedPackages, actualPackages))
776+
t.Fail(fmt.Errorf("Expected Php packages do not match any actual packages: expected %+v\nComplete:\nexpected %v\nactual %v.\n",
777+
expectedPackages[i], expectedPackages, actualPackages))
738778
return
739779
}
780+
781+
// create notes for all packages in the actual list not in the expected list
782+
for ii, _ := range actualPackages {
783+
var found bool = false
784+
for _, pkg := range expectedPackages {
785+
if pkg.Name == actualPackages[ii].Name {
786+
found = true
787+
break
788+
}
789+
}
790+
if !found {
791+
t.AddNote(fmt.Sprintf("Detected package not in expected: %+v", actualPackages[ii]))
792+
}
793+
}
740794
} else {
741795
if nil != expectedPackages {
742796
t.Fail(fmt.Errorf("Expected PHP packages %+v, harvest contains none\n", expectedPackages))

0 commit comments

Comments
 (0)