Skip to content

Commit

Permalink
Add missing citool
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaszcl committed Aug 9, 2024
1 parent 8a82cb2 commit 4ce18a0
Show file tree
Hide file tree
Showing 11 changed files with 982 additions and 0 deletions.
166 changes: 166 additions & 0 deletions integration-tests/citool/cmd/check_tests_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package cmd

import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

type JobConfig struct {
Jobs map[string]struct {
Strategy struct {
Matrix struct {
Test []struct {
Path string `yaml:"path"`
TestOpts string `yaml:"testOpts"`
} `yaml:"test"`
} `yaml:"matrix"`
} `yaml:"strategy"`
} `yaml:"jobs"`
}

var checkTestsCmd = &cobra.Command{
Use: "check-tests [directory] [yaml file]",
Short: "Check if all tests in a directory are included in the test configurations YAML file",
Args: cobra.ExactArgs(2),
Run: func(_ *cobra.Command, args []string) {
directory := args[0]
yamlFile := args[1]
excludedDirs := []string{"../../citool"}
tests, err := extractTests(directory, excludedDirs)
if err != nil {
fmt.Println("Error extracting tests:", err)
os.Exit(1)
}

checkTestsInPipeline(yamlFile, tests)
},
}

// extractTests scans the given directory and subdirectories (except the excluded ones)
// for Go test files, extracts test function names, and returns a slice of Test.
func extractTests(dir string, excludeDirs []string) ([]Test, error) {
var tests []Test

// Resolve to absolute path
absDir, err := filepath.Abs(dir)
if err != nil {
return nil, err
}

// filepath.WalkDir provides more control and is more efficient for skipping directories
err = filepath.WalkDir(absDir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}

// Check if the current path is one of the excluded directories
for _, exclude := range excludeDirs {
absExclude, _ := filepath.Abs(exclude)
if strings.HasPrefix(path, absExclude) {
if d.IsDir() {
return filepath.SkipDir // Skip this directory
}
return nil // Skip this file
}
}

if !d.IsDir() && strings.HasSuffix(d.Name(), "_test.go") {
content, err := os.ReadFile(path)
if err != nil {
return err
}
re := regexp.MustCompile(`func (Test\w+)`)
matches := re.FindAllSubmatch(content, -1)
for _, match := range matches {
funcName := string(match[1])
if funcName == "TestMain" { // Skip "TestMain"
continue
}
tests = append(tests, Test{
Name: funcName,
Path: mustExtractSubpath(path, "integration-tests"),
})
}
}
return nil
})

return tests, err
}

// ExtractSubpath extracts a specific subpath from a given full path.
// If the subpath is not found, it returns an error.
func mustExtractSubpath(fullPath, subPath string) string {
index := strings.Index(fullPath, subPath)
if index == -1 {
panic("subpath not found in the provided full path")
}
return fullPath[index:]
}

func checkTestsInPipeline(yamlFile string, tests []Test) {
data, err := os.ReadFile(yamlFile)
if err != nil {
fmt.Printf("Error reading YAML file: %s\n", err)
return
}

var config Config
err = yaml.Unmarshal(data, &config)
if err != nil {
fmt.Printf("Error parsing YAML: %s\n", err)
return
}

missingTests := []string{} // Track missing tests

for _, test := range tests {
found := false
for _, item := range config.Tests {
if item.Path == test.Path {
if strings.Contains(item.TestCmd, "-test.run") {
if matchTestNameInCmd(item.TestCmd, test.Name) {
found = true
break
}
} else {
found = true
break
}
}
}
if !found {
missingTests = append(missingTests, fmt.Sprintf("ERROR: Test '%s' in file '%s' does not have CI configuration in '%s'", test.Name, test.Path, yamlFile))
}
}

if len(missingTests) > 0 {
for _, missing := range missingTests {
fmt.Println(missing)
}
os.Exit(1) // Exit with a failure status
}
}

// matchTestNameInCmd checks if the given test name matches the -test.run pattern in the command string.
func matchTestNameInCmd(cmd string, testName string) bool {
testRunRegex := regexp.MustCompile(`-test\.run ([^\s]+)`)
matches := testRunRegex.FindStringSubmatch(cmd)
if len(matches) > 1 {
// Extract the regex pattern used in the -test.run command
pattern := matches[1]

// Escape regex metacharacters in the testName before matching
escapedTestName := regexp.QuoteMeta(testName)

// Check if the escaped test name matches the extracted pattern
return regexp.MustCompile(pattern).MatchString(escapedTestName)
}
return false
}
47 changes: 47 additions & 0 deletions integration-tests/citool/cmd/check_tests_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package cmd

import (
"testing"
)

func TestMatchTestNameInCmd(t *testing.T) {
tests := []struct {
cmd string
testName string
expected bool
}{
{"go test -test.run ^TestExample$", "TestExample", true},
{"go test -test.run ^TestExample$", "TestAnother", false},
{"go test -test.run ^TestExample$ -v", "TestExample", true},
{"go test -test.run ^TestExamplePart$", "TestExample", false},
{"go test -test.run ^TestWithNumbers123$", "TestWithNumbers123", true},
{"go test -test.run ^Test_With_Underscores$", "Test_With_Underscores", true},
{"go test -test.run ^Test-With-Dash$", "Test-With-Dash", true},
{"go test -test.run ^TestWithSpace Space$", "TestWithSpace Space", true},
{"go test -test.run ^TestWithNewline\nNewline$", "TestWithNewline\nNewline", true},
{"go test -test.run ^TestOne$|^TestTwo$", "TestOne", true},
{"go test -test.run ^TestOne$|^TestTwo$", "TestTwo", true},
{"go test -test.run ^TestOne$|^TestTwo$", "TestThree", false},
{"go test -test.run TestOne|TestTwo", "TestTwo", true},
{"go test -test.run TestOne|TestTwo", "TestOne", true},
{"go test -test.run TestOne|TestTwo|TestThree", "TestFour", false},
{"go test -test.run ^TestOne$|TestTwo$", "TestTwo", true},
{"go test -test.run ^TestOne$|TestTwo|TestThree$", "TestThree", true},
{"go test -test.run TestOne|TestTwo|TestThree", "TestOne", true},
{"go test -test.run TestOne|TestTwo|TestThree", "TestThree", true},
{"go test -test.run ^TestA$|^TestB$|^TestC$", "TestA", true},
{"go test -test.run ^TestA$|^TestB$|^TestC$", "TestB", true},
{"go test -test.run ^TestA$|^TestB$|^TestC$", "TestD", false},
{"go test -test.run TestA|^TestB$|TestC", "TestB", true},
{"go test -test.run ^TestA|^TestB|TestC$", "TestA", true},
{"go test -test.run ^TestA|^TestB|TestC$", "TestC", true},
{"go test -test.run ^TestA|^TestB|TestC$", "TestD", false},
}

for _, tt := range tests {
result := matchTestNameInCmd(tt.cmd, tt.testName)
if result != tt.expected {
t.Errorf("matchTestNameInCmd(%s, %s) = %t; expected %t", tt.cmd, tt.testName, result, tt.expected)
}
}
}
179 changes: 179 additions & 0 deletions integration-tests/citool/cmd/create_test_config_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package cmd

import (
"fmt"
"os"

"github.com/pelletier/go-toml/v2"
"github.com/spf13/cobra"

ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config"
ctf_config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types"
)

var createTestConfigCmd = &cobra.Command{
Use: "create",
Short: "Create a test config from the provided flags",
Run: func(cmd *cobra.Command, _ []string) {
var tc ctf_config.TestConfig

var version, postgresVersion *string
if cmd.Flags().Changed(ChainlinkVersionFlag) {
version = &oc.ChainlinkVersion
}
if cmd.Flags().Changed(ChainlinkPostgresVersionFlag) {
version = &oc.ChainlinkPostgresVersion
}
if version != nil || postgresVersion != nil {
tc.ChainlinkImage = &ctf_config.ChainlinkImageConfig{
Version: version,
PostgresVersion: postgresVersion,
}
}

var upgradeVersion *string
if cmd.Flags().Changed(ChainlinkUpgradeVersionFlag) {
upgradeVersion = &oc.ChainlinkUpgradeVersion
}
if upgradeVersion != nil {
tc.ChainlinkUpgradeImage = &ctf_config.ChainlinkImageConfig{
Version: upgradeVersion,
}
}

var selectedNetworks *[]string
if cmd.Flags().Changed(SelectedNetworksFlag) {
selectedNetworks = &oc.SelectedNetworks
}
if selectedNetworks != nil {
tc.Network = &ctf_config.NetworkConfig{
SelectedNetworks: oc.SelectedNetworks,
}
}

var peryscopeEnabled *bool
var pyroscopeServerURL, pyroscopeEnvironment, pyroscopeKey *string
if cmd.Flags().Changed(PyroscopeEnabledFlag) {
peryscopeEnabled = &oc.PyroscopeEnabled
}
if cmd.Flags().Changed(PyroscopeServerURLFlag) {
pyroscopeServerURL = &oc.PyroscopeServerURL
}
if cmd.Flags().Changed(PyroscopeKeyFlag) {
pyroscopeKey = &oc.PyroscopeKey
}
if cmd.Flags().Changed(PyroscopeEnvironmentFlag) {
pyroscopeEnvironment = &oc.PyroscopeEnvironment
}
if peryscopeEnabled != nil {
tc.Pyroscope = &ctf_config.PyroscopeConfig{
Enabled: peryscopeEnabled,
ServerUrl: pyroscopeServerURL,
Environment: pyroscopeEnvironment,
Key: pyroscopeKey,
}
}

var testLogCollect *bool
if cmd.Flags().Changed(LoggingTestLogCollectFlag) {
testLogCollect = &oc.LoggingTestLogCollect
}
var loggingRunID *string
if cmd.Flags().Changed(LoggingRunIDFlag) {
loggingRunID = &oc.LoggingRunID
}
var loggingLogTargets []string
if cmd.Flags().Changed(LoggingLogTargetsFlag) {
loggingLogTargets = oc.LoggingLogTargets
}
var loggingLokiTenantID *string
if cmd.Flags().Changed(LoggingLokiTenantIDFlag) {
loggingLokiTenantID = &oc.LoggingLokiTenantID
}
var loggingLokiBasicAuth *string
if cmd.Flags().Changed(LoggingLokiBasicAuthFlag) {
loggingLokiBasicAuth = &oc.LoggingLokiBasicAuth
}
var loggingLokiEndpoint *string
if cmd.Flags().Changed(LoggingLokiEndpointFlag) {
loggingLokiEndpoint = &oc.LoggingLokiEndpoint
}
var loggingGrafanaBaseURL *string
if cmd.Flags().Changed(LoggingGrafanaBaseURLFlag) {
loggingGrafanaBaseURL = &oc.LoggingGrafanaBaseURL
}
var loggingGrafanaDashboardURL *string
if cmd.Flags().Changed(LoggingGrafanaDashboardURLFlag) {
loggingGrafanaDashboardURL = &oc.LoggingGrafanaDashboardURL
}
var loggingGrafanaBearerToken *string
if cmd.Flags().Changed(LoggingGrafanaBearerTokenFlag) {
loggingGrafanaBearerToken = &oc.LoggingGrafanaBearerToken
}

if testLogCollect != nil || loggingRunID != nil || loggingLogTargets != nil || loggingLokiEndpoint != nil || loggingLokiTenantID != nil || loggingLokiBasicAuth != nil || loggingGrafanaBaseURL != nil || loggingGrafanaDashboardURL != nil || loggingGrafanaBearerToken != nil {
tc.Logging = &ctf_config.LoggingConfig{}
tc.Logging.TestLogCollect = testLogCollect
tc.Logging.RunId = loggingRunID
if loggingLogTargets != nil {
tc.Logging.LogStream = &ctf_config.LogStreamConfig{
LogTargets: loggingLogTargets,
}
}
if loggingLokiTenantID != nil || loggingLokiBasicAuth != nil || loggingLokiEndpoint != nil {
tc.Logging.Loki = &ctf_config.LokiConfig{
TenantId: loggingLokiTenantID,
BasicAuth: loggingLokiBasicAuth,
Endpoint: loggingLokiEndpoint,
}
}
if loggingGrafanaBaseURL != nil || loggingGrafanaDashboardURL != nil || loggingGrafanaBearerToken != nil {
tc.Logging.Grafana = &ctf_config.GrafanaConfig{
BaseUrl: loggingGrafanaBaseURL,
DashboardUrl: loggingGrafanaDashboardURL,
BearerToken: loggingGrafanaBearerToken,
}
}
}

var privateEthereumNetworkExecutionLayer *string
if cmd.Flags().Changed(PrivateEthereumNetworkExecutionLayerFlag) {
privateEthereumNetworkExecutionLayer = &oc.PrivateEthereumNetworkExecutionLayer
}
var privateEthereumNetworkEthereumVersion *string
if cmd.Flags().Changed(PrivateEthereumNetworkEthereumVersionFlag) {
privateEthereumNetworkEthereumVersion = &oc.PrivateEthereumNetworkEthereumVersion
}
var privateEthereumNetworkCustomDockerImage *string
if cmd.Flags().Changed(PrivateEthereumNetworkCustomDockerImageFlag) {
privateEthereumNetworkCustomDockerImage = &oc.PrivateEthereumNetworkCustomDockerImages
}
if privateEthereumNetworkExecutionLayer != nil || privateEthereumNetworkEthereumVersion != nil || privateEthereumNetworkCustomDockerImage != nil {
var el ctf_config_types.ExecutionLayer
if privateEthereumNetworkExecutionLayer != nil {
el = ctf_config_types.ExecutionLayer(*privateEthereumNetworkExecutionLayer)
}
var ev ctf_config_types.EthereumVersion
if privateEthereumNetworkEthereumVersion != nil {
ev = ctf_config_types.EthereumVersion(*privateEthereumNetworkEthereumVersion)
}
var customImages map[ctf_config.ContainerType]string
if privateEthereumNetworkCustomDockerImage != nil {
customImages = map[ctf_config.ContainerType]string{"execution_layer": *privateEthereumNetworkCustomDockerImage}
}
tc.PrivateEthereumNetwork = &ctf_config.EthereumNetworkConfig{
ExecutionLayer: &el,
EthereumVersion: &ev,
CustomDockerImages: customImages,
}
}

configToml, err := toml.Marshal(tc)
if err != nil {
fmt.Fprintf(os.Stderr, "Error marshalling TestConfig to TOML: %v\n", err)
os.Exit(1)
}

fmt.Fprintln(cmd.OutOrStdout(), string(configToml))
},
}
Loading

0 comments on commit 4ce18a0

Please sign in to comment.