From 2a310915698f83d629a3d41307b773191dc258a5 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 15 Sep 2025 05:52:04 -0700 Subject: [PATCH] Make tests for URL load check rule functions self contained Several rules check whether URLs provided in project metadata load. Previously, the handling of various HTTP response status codes of these rule functions was tested by using the convenient httpstat.us site. Unfortunately the maintainers of that site shut it down months ago due to abuse. This caused the spurious failure of the unit tests. Although the httpstat.us maintainers state that they plan to eventually resume service, there hasn't been any indication of progress on that front. So it will be best to make the tests self contained, without any dependence on an external site. This is accomplished by creating a local HTTP server that responds with the intended status code. Unfortunately this does require the addition of code to the test in order to generate test data on the fly in order to reference the arbitrary port of the test server. --- internal/rule/rulefunction/library_test.go | 72 ++- .../rule/rulefunction/packageindex_test.go | 524 +++++++++++++++++- .../TestLibraryPropertiesUrlFieldDeadLink.h} | 0 .../libraries/URL404/library.properties | 9 - .../package_foo_index.json | 26 - .../package_foo_index.json | 46 -- .../package_foo_index.json | 46 -- .../package_foo_index.json | 36 -- .../package_foo_index.json | 26 - internal/util/test/test.go | 17 +- 10 files changed, 590 insertions(+), 212 deletions(-) rename internal/rule/rulefunction/testdata/libraries/{URL404/src/URL404.h => TestLibraryPropertiesUrlFieldDeadLink/src/TestLibraryPropertiesUrlFieldDeadLink.h} (100%) delete mode 100644 internal/rule/rulefunction/testdata/libraries/URL404/library.properties delete mode 100644 internal/rule/rulefunction/testdata/packageindexes/packages-help-online-dead/package_foo_index.json delete mode 100644 internal/rule/rulefunction/testdata/packageindexes/packages-platforms-help-online-dead/package_foo_index.json delete mode 100644 internal/rule/rulefunction/testdata/packageindexes/packages-platforms-url-dead/package_foo_index.json delete mode 100644 internal/rule/rulefunction/testdata/packageindexes/packages-tools-systems-url-dead/package_foo_index.json delete mode 100644 internal/rule/rulefunction/testdata/packageindexes/packages-websiteurl-dead/package_foo_index.json diff --git a/internal/rule/rulefunction/library_test.go b/internal/rule/rulefunction/library_test.go index 91a670b57..e36a0300a 100644 --- a/internal/rule/rulefunction/library_test.go +++ b/internal/rule/rulefunction/library_test.go @@ -18,8 +18,10 @@ package rulefunction import ( "fmt" + "net/http" "os" "regexp" + "strings" "testing" "time" @@ -27,7 +29,9 @@ import ( "github.com/arduino/arduino-lint/internal/project/projectdata" "github.com/arduino/arduino-lint/internal/project/projecttype" "github.com/arduino/arduino-lint/internal/rule/ruleresult" + "github.com/arduino/arduino-lint/internal/util/test" "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" "github.com/stretchr/testify/assert" @@ -780,11 +784,75 @@ func TestLibraryPropertiesUrlFieldDeadLink(t *testing.T) { {"Unable to load", "InvalidLibraryProperties", ruleresult.NotRun, ""}, {"Not defined", "MissingFields", ruleresult.NotRun, ""}, {"Bad URL", "BadURL", ruleresult.Fail, "^Head \"http://invalid/\": dial tcp: lookup invalid"}, - {"HTTP error 404", "URL404", ruleresult.Fail, "^404 Not Found$"}, - {"Good URL", "Recursive", ruleresult.Pass, ""}, } checkLibraryRuleFunction(LibraryPropertiesURLFieldDeadLink, testTables, t) + + /* + In order to avoid a dependency on an external site, a test HTTP server is used for the tests covering handling of + various HTTP response status codes. For this reason, the following tests can't be performed via the + checkLibraryRuleFunction function. + */ + statusTestTables := []struct { + testName string + serverStatus int + expectedRuleResult ruleresult.Type + expectedOutputQuery string + }{ + {"HTTP error 404", http.StatusNotFound, ruleresult.Fail, "^404 Not Found$"}, + {"Good URL", http.StatusOK, ruleresult.Pass, ""}, + } + + var propertiesMap = map[string]string{ + "name": "WebServer", + "version": "1.0.0", + "author": "Cristian Maglie , Pippo Pluto ", + "maintainer": "Cristian Maglie ", + "sentence": "A library that makes coding a Webserver a breeze.", + "paragraph": "Supports HTTP1.1 and you can do GET and POST.", + "category": "Communication", + "architectures": "avr", + } + + libraryProperties := properties.NewFromHashmap(propertiesMap) + + for _, testTable := range statusTestTables { + // Create an HTTP server that will return the desired status. + server := test.StatusServer(testTable.serverStatus) + defer server.Close() + + libraryProperties.Set("url", server.URL) + // AsSlice is the closest thing to a []byte output function provided by + // `github.com/arduino/go-properties-orderedmap`. + propertiesBytes := []byte(strings.Join(libraryProperties.AsSlice(), "\n")) + + // Generate the test data library. + sourcePath := librariesTestDataPath.Join("TestLibraryPropertiesUrlFieldDeadLink") + tempPath, err := paths.MkTempDir("", "TestLibraryPropertiesUrlFieldDeadLink") + defer tempPath.RemoveAll() // Clean up after the test. + require.NoError(t, err) + libraryPath := tempPath.Join("TestLibraryPropertiesUrlFieldDeadLink") + err = sourcePath.CopyDirTo(libraryPath) + require.NoError(t, err) + err = libraryPath.Join("library.properties").WriteFile(propertiesBytes) + require.NoError(t, err) + + testProject := project.Type{ + Path: libraryPath, + ProjectType: projecttype.Library, + SuperprojectType: projecttype.Library, + } + projectdata.Initialize(testProject) + + result, output := LibraryPropertiesURLFieldDeadLink() + assert.Equal(t, testTable.expectedRuleResult, result, testTable.testName) + expectedOutputRegexp := regexp.MustCompile(testTable.expectedOutputQuery) + assert.True( + t, + expectedOutputRegexp.MatchString(output), + fmt.Sprintf("%s (output: %s, assertion regex: %s)", testTable.testName, output, testTable.expectedOutputQuery), + ) + } } func TestLibraryPropertiesArchitecturesFieldMissing(t *testing.T) { diff --git a/internal/rule/rulefunction/packageindex_test.go b/internal/rule/rulefunction/packageindex_test.go index 4cd1c7ea5..a955872fb 100644 --- a/internal/rule/rulefunction/packageindex_test.go +++ b/internal/rule/rulefunction/packageindex_test.go @@ -17,7 +17,9 @@ package rulefunction import ( + "encoding/json" "fmt" + "net/http" "regexp" "testing" @@ -25,12 +27,69 @@ import ( "github.com/arduino/arduino-lint/internal/project/projectdata" "github.com/arduino/arduino-lint/internal/project/projecttype" "github.com/arduino/arduino-lint/internal/rule/ruleresult" + "github.com/arduino/arduino-lint/internal/util/test" "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var packageIndexesTestDataPath *paths.Path +// Help is the type of the `packages[*].help` and `packages[*].platforms[*].help` package index keys. +type Help struct { + Online string `json:"online"` +} + +// Help is the type of the elements of the `packages[*].platforms[*].toolsDependencies` package index key. +type ToolDependency struct{} + +// Platform is the type of the elements of the `packages[*].platforms` package index key. +type Platform struct { + Architecture string `json:"architecture"` + ArchiveFileName string `json:"archiveFileName"` + Boards []string `json:"boards"` + Category string `json:"category"` + Checksum string `json:"checksum"` + Help Help `json:"help"` + Name string `json:"name"` + Size string `json:"size"` + ToolsDependencies []ToolDependency `json:"toolsDependencies"` + URL string `json:"url"` + Version string `json:"version"` +} + +// System is the type of the elements of the `packages[*].tools[*].systems` package index key. +type System struct { + ArchiveFileName string `json:"archiveFileName"` + Checksum string `json:"checksum"` + Host string `json:"host"` + Size string `json:"size"` + URL string `json:"url"` +} + +// Tool is the type of the elements of the `packages[*].tools` package index key. +type Tool struct { + Name string `json:"name"` + Systems []System `json:"systems"` + Version string `json:"version"` +} + +// Package is the type of the elements of the `packages` package index key. +type Package struct { + Email string `json:"email"` + Help Help `json:"help"` + Maintainer string `json:"maintainer"` + Name string `json:"name"` + Platforms []Platform `json:"platforms"` + Tools []Tool `json:"tools"` + WebsiteURL string `json:"websiteURL"` +} + +// Package is the type of the package index data. +type Index struct { + Packages []Package `json:"packages"` +} + func init() { workingDirectory, _ := paths.Getwd() packageIndexesTestDataPath = workingDirectory.Join("testdata", "packageindexes") @@ -43,22 +102,54 @@ type packageIndexRuleFunctionTestTable struct { expectedOutputQuery string } +// checkPackageIndexRuleFunction tests the given rule function according to the given test tables. func checkPackageIndexRuleFunction(ruleFunction Type, testTables []packageIndexRuleFunctionTestTable, t *testing.T) { for _, testTable := range testTables { - expectedOutputRegexp := regexp.MustCompile(testTable.expectedOutputQuery) + indexFolder := packageIndexesTestDataPath.Join(testTable.packageIndexFolderName) + checkPackageIndexRuleFunctionForPath(indexFolder, ruleFunction, testTable, t) + } +} - testProject := project.Type{ - Path: packageIndexesTestDataPath.Join(testTable.packageIndexFolderName), - ProjectType: projecttype.PackageIndex, - SuperprojectType: projecttype.PackageIndex, - } +// checkPackageIndexRuleFunctionForPath tests the given rule function on the given path against the given assertions. +// This may be called directly from the test in cases where the test data is generated and thus not in the static test +// data folder. +func checkPackageIndexRuleFunctionForPath( + indexFolder *paths.Path, + ruleFunction Type, + testTable packageIndexRuleFunctionTestTable, + t *testing.T, +) { + expectedOutputRegexp := regexp.MustCompile(testTable.expectedOutputQuery) + + testProject := project.Type{ + Path: indexFolder, + ProjectType: projecttype.PackageIndex, + SuperprojectType: projecttype.PackageIndex, + } - projectdata.Initialize(testProject) + projectdata.Initialize(testProject) - result, output := ruleFunction() - assert.Equal(t, testTable.expectedRuleResult, result, testTable.testName) - assert.True(t, expectedOutputRegexp.MatchString(output), fmt.Sprintf("%s (output: %s, assertion regex: %s)", testTable.testName, output, testTable.expectedOutputQuery)) + result, output := ruleFunction() + assert.Equal(t, testTable.expectedRuleResult, result, testTable.testName) + assert.True( + t, + expectedOutputRegexp.MatchString(output), + fmt.Sprintf("%s (output: %s, assertion regex: %s)", testTable.testName, output, testTable.expectedOutputQuery), + ) +} + +// makeIndex generates a package index file from the given data. +func makeIndex(folder *paths.Path, data Index) error { + indexData, err := json.Marshal(data) + if err != nil { + return err + } + + if err := folder.Join("package_foo_index.json").WriteFile(indexData); err != nil { + return err } + + return nil } func TestPackageIndexMissing(t *testing.T) { @@ -265,12 +356,82 @@ func TestPackageIndexPackagesWebsiteURLInvalidFormat(t *testing.T) { func TestPackageIndexPackagesWebsiteURLDeadLink(t *testing.T) { testTables := []packageIndexRuleFunctionTestTable{ {"Invalid JSON", "invalid-JSON", ruleresult.NotRun, ""}, - {"Dead URLs", "packages-websiteurl-dead", ruleresult.Fail, "^foopackager1, foopackager2$"}, {"Invalid URL", "packages-websiteurl-invalid", ruleresult.Fail, "^foopackager$"}, - {"Valid URL", "valid-package-index", ruleresult.Pass, ""}, } checkPackageIndexRuleFunction(PackageIndexPackagesWebsiteURLDeadLink, testTables, t) + + /* + In order to avoid a dependency on an external site, a test HTTP server is used for the tests covering handling of + various HTTP response status codes. For this reason, the following tests can't be performed via the + checkPackageIndexRuleFunction function. + */ + statusTestTables := []struct { + serverStatuses []int + testTable packageIndexRuleFunctionTestTable + }{ + { + []int{http.StatusNotFound, http.StatusForbidden}, + packageIndexRuleFunctionTestTable{ + "Dead URLs", + "", + ruleresult.Fail, + "^foopackager1, foopackager2$", + }, + }, + { + []int{http.StatusOK, http.StatusOK}, + packageIndexRuleFunctionTestTable{ + "Valid URL", + "", + ruleresult.Pass, + "", + }, + }, + } + + index := Index{ + Packages: []Package{ + { + Email: "jane@example.com", + Help: Help{ + Online: "http://example.com", + }, + Maintainer: "Jane Developer", + Name: "foopackager1", + }, + { + Email: "jane@example.com", + Help: Help{ + Online: "http://example.com", + }, + Maintainer: "Jane Developer", + Name: "foopackager2", + }, + }, + } + + for _, statusTestTable := range statusTestTables { + // Create HTTP servers that will return the desired statuses. + for packageIndex, status := range statusTestTable.serverStatuses { + server := test.StatusServer(status) + defer server.Close() + index.Packages[packageIndex].WebsiteURL = server.URL + } + + // Generate the test package index file. + indexFolder, err := paths.MkTempDir("", "TestPackageIndexPackagesWebsiteURLDeadLink") + defer indexFolder.RemoveAll() // Clean up after the test. + err = makeIndex(indexFolder, index) + require.NoError(t, err) + + checkPackageIndexRuleFunctionForPath( + indexFolder, + PackageIndexPackagesWebsiteURLDeadLink, + statusTestTable.testTable, + t, + ) + } } func TestPackageIndexPackagesEmailMissing(t *testing.T) { @@ -346,11 +507,77 @@ func TestPackageIndexPackagesHelpOnlineInvalidFormat(t *testing.T) { func TestPackageIndexPackagesHelpOnlineDeadLink(t *testing.T) { testTables := []packageIndexRuleFunctionTestTable{ {"Invalid JSON", "invalid-JSON", ruleresult.NotRun, ""}, - {"Dead URLs", "packages-help-online-dead", ruleresult.Fail, "^foopackager1, foopackager2$"}, - {"Valid URL", "valid-package-index", ruleresult.Pass, ""}, } checkPackageIndexRuleFunction(PackageIndexPackagesHelpOnlineDeadLink, testTables, t) + + /* + In order to avoid a dependency on an external site, a test HTTP server is used for the tests covering handling of + various HTTP response status codes. For this reason, the following tests can't be performed via the + checkPackageIndexRuleFunction function. + */ + statusTestTables := []struct { + serverStatuses []int + testTable packageIndexRuleFunctionTestTable + }{ + { + []int{http.StatusNotFound, http.StatusForbidden}, + packageIndexRuleFunctionTestTable{ + "Dead URLs", + "", + ruleresult.Fail, + "^foopackager1, foopackager2$", + }, + }, + { + []int{http.StatusOK, http.StatusOK}, + packageIndexRuleFunctionTestTable{ + "Valid URL", + "", + ruleresult.Pass, + "", + }, + }, + } + + index := Index{ + Packages: []Package{ + { + Email: "jane@example.com", + Maintainer: "Jane Developer", + Name: "foopackager1", + WebsiteURL: "http://example.com", + }, + { + Email: "jane@example.com", + Maintainer: "Jane Developer", + Name: "foopackager2", + WebsiteURL: "http://example.com", + }, + }, + } + + for _, statusTestTable := range statusTestTables { + // Create HTTP servers that will return the desired statuses. + for packageIndex, status := range statusTestTable.serverStatuses { + server := test.StatusServer(status) + defer server.Close() + index.Packages[packageIndex].Help.Online = server.URL + } + + // Generate the test package index file. + indexFolder, err := paths.MkTempDir("", "TestPackageIndexPackagesHelpOnlineDeadLink") + defer indexFolder.RemoveAll() // Clean up after the test. + err = makeIndex(indexFolder, index) + require.NoError(t, err) + + checkPackageIndexRuleFunctionForPath( + indexFolder, + PackageIndexPackagesHelpOnlineDeadLink, + statusTestTable.testTable, + t, + ) + } } func TestPackageIndexPackagesPlatformsMissing(t *testing.T) { @@ -586,11 +813,96 @@ func TestPackageIndexPackagesPlatformsHelpOnlineInvalidFormat(t *testing.T) { func TestPackageIndexPackagesPlatformsHelpOnlineDeadLink(t *testing.T) { testTables := []packageIndexRuleFunctionTestTable{ {"Invalid JSON", "invalid-JSON", ruleresult.NotRun, ""}, - {"Dead URLs", "packages-platforms-help-online-dead", ruleresult.Fail, "^" + brokenOutputListIndent + "foopackager:avr@1\\.0\\.0\n" + brokenOutputListIndent + "foopackager:samd@1\\.0\\.0$"}, - {"Valid URL", "valid-package-index", ruleresult.Pass, ""}, } checkPackageIndexRuleFunction(PackageIndexPackagesPlatformsHelpOnlineDeadLink, testTables, t) + + /* + In order to avoid a dependency on an external site, a test HTTP server is used for the tests covering handling of + various HTTP response status codes. For this reason, the following tests can't be performed via the + checkPackageIndexRuleFunction function. + */ + statusTestTables := []struct { + serverStatuses []int + testTable packageIndexRuleFunctionTestTable + }{ + { + []int{http.StatusNotFound, http.StatusForbidden}, + packageIndexRuleFunctionTestTable{ + "Dead URLs", + "", + ruleresult.Fail, + "^" + brokenOutputListIndent + "foopackager:avr@1\\.0\\.0\n" + brokenOutputListIndent + "foopackager:samd@1\\.0\\.0$", + }, + }, + { + []int{http.StatusOK, http.StatusOK}, + packageIndexRuleFunctionTestTable{ + "Valid URL", + "", + ruleresult.Pass, + "", + }, + }, + } + + index := Index{ + Packages: []Package{ + { + Email: "jane@example.com", + Help: Help{ + Online: "http://example.com", + }, + Maintainer: "Jane Developer", + Name: "foopackager", + Platforms: []Platform{ + { + Architecture: "avr", + ArchiveFileName: "myboard-1.0.0.zip", + Category: "Contributed", + Checksum: "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", + Name: "My AVR Board", + Size: "15005", + URL: "https://janedeveloper.github.io/myboard/myboard-1.0.0.zip", + Version: "1.0.0", + }, + { + Architecture: "samd", + ArchiveFileName: "myboard-1.0.0.zip", + Category: "Contributed", + Checksum: "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", + Name: "My AVR Board", + Size: "15005", + URL: "https://janedeveloper.github.io/myboard/myboard-1.0.0.zip", + Version: "1.0.0", + }, + }, + WebsiteURL: "http://example.com", + }, + }, + } + + for _, statusTestTable := range statusTestTables { + // Create HTTP servers that will return the desired statuses. + for platformIndex, status := range statusTestTable.serverStatuses { + server := test.StatusServer(status) + defer server.Close() + index.Packages[0].Platforms[platformIndex].Help.Online = server.URL + } + + // Generate the test package index file. + indexFolder, err := paths.MkTempDir("", "TestPackageIndexPackagesPlatformsHelpOnlineDeadLink") + defer indexFolder.RemoveAll() // Clean up after the test. + err = makeIndex(indexFolder, index) + require.NoError(t, err) + + checkPackageIndexRuleFunctionForPath( + indexFolder, + PackageIndexPackagesPlatformsHelpOnlineDeadLink, + statusTestTable.testTable, + t, + ) + } } func TestPackageIndexPackagesPlatformsUrlMissing(t *testing.T) { @@ -626,11 +938,100 @@ func TestPackageIndexPackagesPlatformsUrlInvalidFormat(t *testing.T) { func TestPackageIndexPackagesPlatformsURLDeadLink(t *testing.T) { testTables := []packageIndexRuleFunctionTestTable{ {"Invalid JSON", "invalid-JSON", ruleresult.NotRun, ""}, - {"Dead URLs", "packages-platforms-url-dead", ruleresult.Fail, "^" + brokenOutputListIndent + "foopackager:avr@1\\.0\\.0\n" + brokenOutputListIndent + "foopackager:samd@1\\.0\\.0$"}, - {"Valid URL", "valid-package-index", ruleresult.Pass, ""}, } checkPackageIndexRuleFunction(PackageIndexPackagesPlatformsURLDeadLink, testTables, t) + + /* + In order to avoid a dependency on an external site, a test HTTP server is used for the tests covering handling of + various HTTP response status codes. For this reason, the following tests can't be performed via the + checkPackageIndexRuleFunction function. + */ + statusTestTables := []struct { + serverStatuses []int + testTable packageIndexRuleFunctionTestTable + }{ + { + []int{http.StatusNotFound, http.StatusForbidden}, + packageIndexRuleFunctionTestTable{ + "Dead URLs", + "", + ruleresult.Fail, + "^" + brokenOutputListIndent + "foopackager:avr@1\\.0\\.0\n" + brokenOutputListIndent + "foopackager:samd@1\\.0\\.0$", + }, + }, + { + []int{http.StatusOK, http.StatusOK}, + packageIndexRuleFunctionTestTable{ + "Valid URL", + "", + ruleresult.Pass, + "", + }, + }, + } + + index := Index{ + Packages: []Package{ + { + Email: "jane@example.com", + Help: Help{ + Online: "http://example.com", + }, + Maintainer: "Jane Developer", + Name: "foopackager", + Platforms: []Platform{ + { + Architecture: "avr", + ArchiveFileName: "myboard-1.0.0.zip", + Category: "Contributed", + Checksum: "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", + Help: Help{ + Online: "http://example.com", + }, + Name: "My AVR Board", + Size: "15005", + Version: "1.0.0", + }, + { + Architecture: "samd", + ArchiveFileName: "myboard-1.0.0.zip", + Category: "Contributed", + Checksum: "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", + Help: Help{ + Online: "http://example.com", + }, + Name: "My AVR Board", + Size: "15005", + Version: "1.0.0", + }, + }, + WebsiteURL: "http://example.com", + }, + }, + } + + for _, statusTestTable := range statusTestTables { + // Create HTTP servers that will return the desired statuses. + for platformIndex, status := range statusTestTable.serverStatuses { + server := test.StatusServer(status) + defer server.Close() + index.Packages[0].Platforms[platformIndex].URL = server.URL + } + + // Generate the test package index file. + indexFolder, err := paths.MkTempDir("", "TestPackageIndexPackagesPlatformsURLDeadLink") + defer indexFolder.RemoveAll() // Clean up after the test. + err = makeIndex(indexFolder, index) + require.NoError(t, err) + + checkPackageIndexRuleFunctionForPath( + indexFolder, + PackageIndexPackagesPlatformsURLDeadLink, + statusTestTable.testTable, + t, + ) + } } func TestPackageIndexPackagesPlatformsArchiveFileNameMissing(t *testing.T) { @@ -1286,11 +1687,94 @@ func TestPackageIndexPackagesToolsSystemsUrlInvalidFormat(t *testing.T) { func TestPackageIndexPackagesToolsSystemsURLDeadLink(t *testing.T) { testTables := []packageIndexRuleFunctionTestTable{ {"Invalid JSON", "invalid-JSON", ruleresult.NotRun, ""}, - {"Dead URLs", "packages-tools-systems-url-dead", ruleresult.Fail, "^" + brokenOutputListIndent + "foopackager:CMSIS@4\\.0\\.0-atmel >> arm-linux-gnueabihf\n" + brokenOutputListIndent + "foopackager:CMSIS@4\\.0\\.0-atmel >> i686-mingw32$"}, - {"Valid URL", "valid-package-index", ruleresult.Pass, ""}, } checkPackageIndexRuleFunction(PackageIndexPackagesToolsSystemsURLDeadLink, testTables, t) + + /* + In order to avoid a dependency on an external site, a test HTTP server is used for the tests covering handling of + various HTTP response status codes. For this reason, the following tests can't be performed via the + checkPackageIndexRuleFunction function. + */ + statusTestTables := []struct { + serverStatuses []int + testTable packageIndexRuleFunctionTestTable + }{ + { + []int{http.StatusNotFound, http.StatusForbidden}, + packageIndexRuleFunctionTestTable{ + "Dead URLs", + "", + ruleresult.Fail, + "^" + brokenOutputListIndent + "foopackager:CMSIS@4\\.0\\.0-atmel >> arm-linux-gnueabihf\n" + brokenOutputListIndent + "foopackager:CMSIS@4\\.0\\.0-atmel >> i686-mingw32$", + }, + }, + { + []int{http.StatusOK, http.StatusOK}, + packageIndexRuleFunctionTestTable{ + "Valid URL", + "", + ruleresult.Pass, + "", + }, + }, + } + + index := Index{ + Packages: []Package{ + { + Email: "jane@example.com", + Help: Help{ + Online: "http://example.com", + }, + Maintainer: "Jane Developer", + Name: "foopackager", + WebsiteURL: "http://example.com", + Tools: []Tool{ + { + Name: "CMSIS", + Systems: []System{ + { + ArchiveFileName: "CMSIS-4.0.0.tar.bz2", + Checksum: "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", + Host: "arm-linux-gnueabihf", + Size: "17642623", + }, + { + ArchiveFileName: "CMSIS-4.0.0.tar.bz2", + Checksum: "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", + Host: "i686-mingw32", + Size: "17642623", + }, + }, + Version: "4.0.0-atmel", + }, + }, + }, + }, + } + + for _, statusTestTable := range statusTestTables { + // Create HTTP servers that will return the desired statuses. + for systemIndex, status := range statusTestTable.serverStatuses { + server := test.StatusServer(status) + defer server.Close() + index.Packages[0].Tools[0].Systems[systemIndex].URL = server.URL + } + + // Generate the test package index file. + indexFolder, err := paths.MkTempDir("", "TestPackageIndexPackagesToolsSystemsURLDeadLink") + defer indexFolder.RemoveAll() // Clean up after the test. + err = makeIndex(indexFolder, index) + require.NoError(t, err) + + checkPackageIndexRuleFunctionForPath( + indexFolder, + PackageIndexPackagesToolsSystemsURLDeadLink, + statusTestTable.testTable, + t, + ) + } } func TestPackageIndexPackagesToolsSystemsArchiveFileNameMissing(t *testing.T) { diff --git a/internal/rule/rulefunction/testdata/libraries/URL404/src/URL404.h b/internal/rule/rulefunction/testdata/libraries/TestLibraryPropertiesUrlFieldDeadLink/src/TestLibraryPropertiesUrlFieldDeadLink.h similarity index 100% rename from internal/rule/rulefunction/testdata/libraries/URL404/src/URL404.h rename to internal/rule/rulefunction/testdata/libraries/TestLibraryPropertiesUrlFieldDeadLink/src/TestLibraryPropertiesUrlFieldDeadLink.h diff --git a/internal/rule/rulefunction/testdata/libraries/URL404/library.properties b/internal/rule/rulefunction/testdata/libraries/URL404/library.properties deleted file mode 100644 index 2789ab18e..000000000 --- a/internal/rule/rulefunction/testdata/libraries/URL404/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=URL404 -version=1.0.0 -author=Cristian Maglie , Pippo Pluto -maintainer=Cristian Maglie -sentence=A library that makes coding a web server a breeze. -paragraph=Supports HTTP1.1 and you can do GET and POST. -category=Communication -url=http://httpstat.us/404 -architectures=avr diff --git a/internal/rule/rulefunction/testdata/packageindexes/packages-help-online-dead/package_foo_index.json b/internal/rule/rulefunction/testdata/packageindexes/packages-help-online-dead/package_foo_index.json deleted file mode 100644 index 9f282cda5..000000000 --- a/internal/rule/rulefunction/testdata/packageindexes/packages-help-online-dead/package_foo_index.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "packages": [ - { - "name": "foopackager1", - "maintainer": "Jane Developer", - "websiteURL": "http://example.com", - "email": "jane@example.com", - "help": { - "online": "http://httpstat.us/403" - }, - "platforms": [], - "tools": [] - }, - { - "name": "foopackager2", - "maintainer": "Jane Developer", - "websiteURL": "http://example.com", - "email": "jane@example.com", - "help": { - "online": "http://httpstat.us/404" - }, - "platforms": [], - "tools": [] - } - ] -} diff --git a/internal/rule/rulefunction/testdata/packageindexes/packages-platforms-help-online-dead/package_foo_index.json b/internal/rule/rulefunction/testdata/packageindexes/packages-platforms-help-online-dead/package_foo_index.json deleted file mode 100644 index 4755ba1e2..000000000 --- a/internal/rule/rulefunction/testdata/packageindexes/packages-platforms-help-online-dead/package_foo_index.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "packages": [ - { - "name": "foopackager", - "maintainer": "Jane Developer", - "websiteURL": "http://example.com", - "email": "jane@example.com", - "help": { - "online": "http://example.com" - }, - "platforms": [ - { - "name": "My AVR Board", - "architecture": "avr", - "version": "1.0.0", - "category": "Contributed", - "help": { - "online": "http://httpstat.us/403" - }, - "url": "https://janedeveloper.github.io/myboard/myboard-1.0.0.zip", - "archiveFileName": "myboard-1.0.0.zip", - "checksum": "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", - "size": "15005", - "boards": [], - "toolsDependencies": [] - }, - { - "name": "My SAMD Board", - "architecture": "samd", - "version": "1.0.0", - "category": "Contributed", - "help": { - "online": "http://httpstat.us/404" - }, - "url": "https://janedeveloper.github.io/myboard/myboard-1.0.0.zip", - "archiveFileName": "myboard-1.0.0.zip", - "checksum": "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", - "size": "15005", - "boards": [], - "toolsDependencies": [] - } - ], - "tools": [] - } - ] -} diff --git a/internal/rule/rulefunction/testdata/packageindexes/packages-platforms-url-dead/package_foo_index.json b/internal/rule/rulefunction/testdata/packageindexes/packages-platforms-url-dead/package_foo_index.json deleted file mode 100644 index 7e881273b..000000000 --- a/internal/rule/rulefunction/testdata/packageindexes/packages-platforms-url-dead/package_foo_index.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "packages": [ - { - "name": "foopackager", - "maintainer": "Jane Developer", - "websiteURL": "http://example.com", - "email": "jane@example.com", - "help": { - "online": "http://example.com" - }, - "platforms": [ - { - "name": "My AVR Board", - "architecture": "avr", - "version": "1.0.0", - "category": "Contributed", - "help": { - "online": "http://example.com" - }, - "url": "http://httpstat.us/403", - "archiveFileName": "myboard-1.0.0.zip", - "checksum": "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", - "size": "15005", - "boards": [], - "toolsDependencies": [] - }, - { - "name": "My SAMD Board", - "architecture": "samd", - "version": "1.0.0", - "category": "Contributed", - "help": { - "online": "http://example.com" - }, - "url": "http://httpstat.us/404", - "archiveFileName": "myboard-1.0.0.zip", - "checksum": "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", - "size": "15005", - "boards": [], - "toolsDependencies": [] - } - ], - "tools": [] - } - ] -} diff --git a/internal/rule/rulefunction/testdata/packageindexes/packages-tools-systems-url-dead/package_foo_index.json b/internal/rule/rulefunction/testdata/packageindexes/packages-tools-systems-url-dead/package_foo_index.json deleted file mode 100644 index f44e64e31..000000000 --- a/internal/rule/rulefunction/testdata/packageindexes/packages-tools-systems-url-dead/package_foo_index.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "packages": [ - { - "name": "foopackager", - "maintainer": "Jane Developer", - "websiteURL": "http://httpstat.us/404", - "email": "jane@example.com", - "help": { - "online": "http://example.com" - }, - "platforms": [], - "tools": [ - { - "name": "CMSIS", - "version": "4.0.0-atmel", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://httpstat.us/403", - "archiveFileName": "CMSIS-4.0.0.tar.bz2", - "checksum": "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", - "size": "17642623" - }, - { - "host": "i686-mingw32", - "url": "http://httpstat.us/404", - "archiveFileName": "CMSIS-4.0.0.tar.bz2", - "checksum": "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", - "size": "17642623" - } - ] - } - ] - } - ] -} diff --git a/internal/rule/rulefunction/testdata/packageindexes/packages-websiteurl-dead/package_foo_index.json b/internal/rule/rulefunction/testdata/packageindexes/packages-websiteurl-dead/package_foo_index.json deleted file mode 100644 index f731b359f..000000000 --- a/internal/rule/rulefunction/testdata/packageindexes/packages-websiteurl-dead/package_foo_index.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "packages": [ - { - "name": "foopackager1", - "maintainer": "Jane Developer", - "websiteURL": "http://httpstat.us/403", - "email": "jane@example.com", - "help": { - "online": "http://example.com/forum/myboard" - }, - "platforms": [], - "tools": [] - }, - { - "name": "foopackager2", - "maintainer": "Jane Developer", - "websiteURL": "http://httpstat.us/404", - "email": "jane@example.com", - "help": { - "online": "http://example.com/forum/myboard" - }, - "platforms": [], - "tools": [] - } - ] -} diff --git a/internal/util/test/test.go b/internal/util/test/test.go index 1beeb72bd..71d16f560 100644 --- a/internal/util/test/test.go +++ b/internal/util/test/test.go @@ -17,7 +17,12 @@ // Package test provides resources for testing arduino-lint. package test -import "github.com/spf13/pflag" +import ( + "net/http" + "net/http/httptest" + + "github.com/spf13/pflag" +) // ConfigurationFlags returns a set of the flags used for command line configuration of arduino-lint. func ConfigurationFlags() *pflag.FlagSet { @@ -35,3 +40,13 @@ func ConfigurationFlags() *pflag.FlagSet { return flags } + +// StatusServer returns an HTTP test server that will respond with the given status code. +func StatusServer(status int) *httptest.Server { + handler := func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(status) + } + server := httptest.NewServer(http.HandlerFunc(handler)) + + return server +}