Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pkg/launcher/jvmLauncher.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,15 @@ func (launcher *JvmLauncher) GetRunsById(runId string) (*galasaapi.Run, error) {
return run, err
}

// Gets a run based on the submission ID of that run.
// For local runs, the submission ID is the same as the test run id.
func (launcher *JvmLauncher) GetRunsBySubmissionId(submissionId string, groupId string) (*galasaapi.Run, error) {
log.Printf("JvmLauncher: GetRunsBySubmissionId entered. runId=%s", submissionId)

log.Printf("JvmLauncher: Local runs cannot find tests based on submission ID")
return nil, nil
}

func createRunFromLocalTest(localTest *LocalTest) (*galasaapi.Run, error) {

var run = galasaapi.NewRun()
Expand Down
3 changes: 3 additions & 0 deletions pkg/launcher/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type Launcher interface {
// GetRunsById gets the Run information for the run with a specific run identifier
GetRunsById(runId string) (*galasaapi.Run, error)

// Gets a run based on the submission ID of that run.
GetRunsBySubmissionId(submissionId string, groupId string) (*galasaapi.Run, error)

// GetStreams gets a list of streams available on this launcher
GetStreams() ([]string, error)

Expand Down
7 changes: 6 additions & 1 deletion pkg/launcher/launcherMock.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (launcher *MockLauncher) SubmitTestRun(
stream string,
obrFromPortfolio string,
isTraceEnabled bool,
GherkinURL string,
GherkinURL string,
GherkinFeature string,
overrides map[string]interface{},
) (*galasaapi.TestRuns, error) {
Expand Down Expand Up @@ -123,6 +123,11 @@ func (launcher *MockLauncher) GetRunsById(runId string) (*galasaapi.Run, error)
return &galasaapi.Run{}, nil
}

// Gets a run based on the submission ID of that run.
func (launcher *MockLauncher) GetRunsBySubmissionId(submissionId string, groupId string) (*galasaapi.Run, error) {
return &galasaapi.Run{}, nil
}

// GetStreams gets a list of streams available on this launcher
func (launcher *MockLauncher) GetStreams() ([]string, error) {
return make([]string, 0), nil
Expand Down
65 changes: 60 additions & 5 deletions pkg/launcher/remoteLauncher.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io"
"log"
"net/http"
"strconv"
"strings"

"github.com/galasa-dev/cli/pkg/api"
Expand All @@ -36,7 +37,7 @@ func NewRemoteLauncher(commsClient api.APICommsClient) *RemoteLauncher {

// A comms client that communicates with the API server in a Galasa service.
launcher.commsClient = commsClient

return launcher
}

Expand Down Expand Up @@ -125,6 +126,60 @@ func (launcher *RemoteLauncher) GetRunsById(runId string) (*galasaapi.Run, error
return rasRun, err
}

// Gets the latest run based on the submission ID of that run.
// For local runs, the submission ID is the same as the test run id.
func (launcher *RemoteLauncher) GetRunsBySubmissionId(submissionId string, groupId string) (*galasaapi.Run, error) {
log.Printf("RemoteLauncher: GetRunsBySubmissionId entered. runId=%v groupId=%v", submissionId, groupId)
var err error
var rasRun *galasaapi.Run
var restApiVersion string

restApiVersion, err = embedded.GetGalasactlRestApiVersion()

if err == nil {
var runData *galasaapi.RunResults

err = launcher.commsClient.RunAuthenticatedCommandWithRateLimitRetries(func(apiClient *galasaapi.APIClient) error {
var err error
var httpResponse *http.Response
var context context.Context = nil

apicall := apiClient.ResultArchiveStoreAPIApi.GetRasSearchRuns(context).ClientApiVersion(restApiVersion).
IncludeCursor("false").
SubmissionId(submissionId).Group(groupId).Sort("from:desc")

runData, httpResponse, err = apicall.Execute()

var statusCode int
if httpResponse != nil {
defer httpResponse.Body.Close()
statusCode = httpResponse.StatusCode
}

if err != nil {
err = galasaErrors.NewGalasaErrorWithHttpStatusCode(statusCode, galasaErrors.GALASA_ERROR_QUERY_RUNS_FAILED, err.Error())
} else {
if statusCode != http.StatusOK {
httpError := "\nhttp response status code: " + strconv.Itoa(statusCode)
errString := httpError
err = galasaErrors.NewGalasaErrorWithHttpStatusCode(statusCode, galasaErrors.GALASA_ERROR_QUERY_RUNS_FAILED, errString)
} else {

log.Printf("HTTP status was OK")

if runData.GetAmountOfRuns() > 0 {
runs := runData.GetRuns()
rasRun = &runs[0]
}

}
}
return err
})
}
return rasRun, err
}

func (launcher *RemoteLauncher) GetStreams() ([]string, error) {

var streams []string
Expand Down Expand Up @@ -179,14 +234,14 @@ func (launcher *RemoteLauncher) GetTestCatalog(stream string) (TestCatalog, erro
if err == nil {
var cpsResponse *http.Response
err = launcher.commsClient.RunAuthenticatedCommandWithRateLimitRetries(func(apiClient *galasaapi.APIClient) error {
cpsProperty, cpsResponse, err = apiClient.ConfigurationPropertyStoreAPIApi.QueryCpsNamespaceProperties(context.TODO(), "framework").Prefix("test.stream."+stream).Suffix("location").ClientApiVersion(restApiVersion).Execute()
cpsProperty, cpsResponse, err = apiClient.ConfigurationPropertyStoreAPIApi.QueryCpsNamespaceProperties(context.TODO(), "framework").Prefix("test.stream." + stream).Suffix("location").ClientApiVersion(restApiVersion).Execute()

var statusCode int
if cpsResponse != nil {
defer cpsResponse.Body.Close()
statusCode = cpsResponse.StatusCode
}

if err != nil {
err = galasaErrors.NewGalasaErrorWithHttpStatusCode(statusCode, galasaErrors.GALASA_ERROR_PROPERTY_GET_FAILED, stream, err)
} else if len(cpsProperty) < 1 {
Expand All @@ -196,7 +251,7 @@ func (launcher *RemoteLauncher) GetTestCatalog(stream string) (TestCatalog, erro
})

if err == nil {
streamLocation :=cpsProperty[0].Data.Value
streamLocation := cpsProperty[0].Data.Value
catalogString := new(strings.Builder)
var resp *http.Response
resp, err = http.Get(*streamLocation)
Expand Down
75 changes: 37 additions & 38 deletions pkg/launcher/remoteLauncher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ func TestGetTestCatalogHttpErrorGetsReported(t *testing.T) {
}))
defer server.Close()


mockFactory := utils.NewMockFactory()
apiServerUrl := server.URL
apiClient := api.InitialiseAPI(apiServerUrl)
Expand All @@ -134,11 +133,11 @@ func TestGetTestCatalogHttpErrorGetsReported(t *testing.T) {
mockFileSystem := mockFactory.GetFileSystem()
mockEnvironment := mockFactory.GetEnvironment()
mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "")
mockFileSystem.WriteTextFile(mockGalasaHome.GetUrlFolderPath() + "/bootstrap.properties", "")
mockFileSystem.WriteTextFile(mockGalasaHome.GetUrlFolderPath()+"/bootstrap.properties", "")

bootstrap := ""
maxAttempts := 3
retryBackoffSeconds := 1
maxAttempts := 3
retryBackoffSeconds := 1

commsClient, _ := api.NewAPICommsClient(bootstrap, maxAttempts, float64(retryBackoffSeconds), mockFactory, mockGalasaHome)

Expand All @@ -153,56 +152,56 @@ func TestGetTestCatalogHttpErrorGetsReported(t *testing.T) {
func TestGetRunsByGroupWithInvalidBearerTokenGetsNewTokenOk(t *testing.T) {
groupId := "group1"

initialLoginOperation := utils.NewHttpInteraction("/auth/tokens", http.MethodPost)
initialLoginOperation.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusCreated)
initialLoginOperation := utils.NewHttpInteraction("/auth/tokens", http.MethodPost)
initialLoginOperation.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusCreated)

mockResponse := fmt.Sprintf(`{"jwt": "%s", "refresh_token": "abc"}`, mockExpiredJwt)
writer.Write([]byte(mockResponse))
}
writer.Write([]byte(mockResponse))
}

unauthorizedGetRunsInteraction := utils.NewHttpInteraction("/runs/" + groupId, http.MethodGet)
unauthorizedGetRunsInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusUnauthorized)
writer.Write([]byte(`{ "error_message": "Invalid bearer token provided!" }`))
}
unauthorizedGetRunsInteraction := utils.NewHttpInteraction("/runs/"+groupId, http.MethodGet)
unauthorizedGetRunsInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusUnauthorized)
writer.Write([]byte(`{ "error_message": "Invalid bearer token provided!" }`))
}

newLoginInteraction := utils.NewHttpInteraction("/auth/tokens", http.MethodPost)
newLoginInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusCreated)
newLoginInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusCreated)

newJwt := createValidMockJwt()
fmt.Println(newJwt)
mockResponse := fmt.Sprintf(`{"jwt": "%s", "refresh_token": "abc"}`, newJwt)
writer.Write([]byte(mockResponse))
}
writer.Write([]byte(mockResponse))
}

getRunsInteraction := utils.NewHttpInteraction("/runs/" + groupId, http.MethodGet)
getRunsInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
getRunsInteraction := utils.NewHttpInteraction("/runs/"+groupId, http.MethodGet)
getRunsInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)

mockRun := galasaapi.NewTestRun()

mockRuns := galasaapi.NewTestRuns()
mockRuns.Runs = []galasaapi.TestRun{ *mockRun }
mockRuns.Runs = []galasaapi.TestRun{*mockRun}
mockRunsBytes, _ := json.Marshal(mockRuns)

writer.Write(mockRunsBytes)
}
writer.Write(mockRunsBytes)
}

interactions := []utils.HttpInteraction{
interactions := []utils.HttpInteraction{
initialLoginOperation,
unauthorizedGetRunsInteraction,
unauthorizedGetRunsInteraction,
newLoginInteraction,
getRunsInteraction,
}
}

server := utils.NewMockHttpServer(t, interactions)
defer server.Server.Close()
server := utils.NewMockHttpServer(t, interactions)
defer server.Server.Close()

mockFactory := utils.NewMockFactory()

Expand All @@ -212,16 +211,16 @@ func TestGetRunsByGroupWithInvalidBearerTokenGetsNewTokenOk(t *testing.T) {
mockGalasaHome, _ := utils.NewGalasaHome(mockFileSystem, mockEnvironment, "")
mockTimeService := mockFactory.GetTimeService()
jwtCache := auth.NewJwtCache(mockFileSystem, mockGalasaHome, mockTimeService)
mockFileSystem.WriteTextFile(mockGalasaHome.GetUrlFolderPath() + "/galasactl.properties", "GALASA_TOKEN=my:token")
mockFileSystem.WriteTextFile(mockGalasaHome.GetUrlFolderPath() + "/bootstrap.properties", "")

mockFileSystem.WriteTextFile(mockGalasaHome.GetUrlFolderPath()+"/galasactl.properties", "GALASA_TOKEN=my:token")
mockFileSystem.WriteTextFile(mockGalasaHome.GetUrlFolderPath()+"/bootstrap.properties", "")

authenticator := auth.NewAuthenticator(apiServerUrl, mockFileSystem, mockGalasaHome, mockTimeService, mockEnvironment, jwtCache)
mockFactory.Authenticator = authenticator

bootstrap := ""
maxAttempts := 3
retryBackoffSeconds := 1
maxAttempts := 3
retryBackoffSeconds := 1

commsClient, _ := api.NewAPICommsClient(bootstrap, maxAttempts, float64(retryBackoffSeconds), mockFactory, mockGalasaHome)

Expand Down
4 changes: 3 additions & 1 deletion pkg/runs/jsonReporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestJsonReportWorks(t *testing.T) {
Tests: []TestMethod{{Method: "method1", Result: "passed"}, {Method: "method2", Result: "passed"}},
GherkinUrl: "file:///my.feature",
GherkinFeature: "my",
SubmissionId: "123",
}

finishedRunsMap := make(map[string]*TestRun, 1)
Expand Down Expand Up @@ -79,7 +80,8 @@ func TestJsonReportWorks(t *testing.T) {
],
"GherkinUrl":"file:///my.feature",
"GherkinFeature":"my",
"group":""
"group":"",
"submissionId":"123"
}
]
}`
Expand Down
1 change: 1 addition & 0 deletions pkg/runs/runTypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type TestRun struct {
GherkinUrl string `yaml:"gherkin"`
GherkinFeature string `yaml:"feature"`
Group string `yaml:"group" json:"group"`
SubmissionId string `yaml:"submissionId" json:"submissionId"`
RunId string `yaml:"runId,omitempty" json:"runId,omitempty"`
}

Expand Down
63 changes: 45 additions & 18 deletions pkg/runs/submitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,24 +439,51 @@ func (submitter *Submitter) processLostRuns(

for runName, possiblyLostRun := range runsToCheck {
isRunLost := true

if possiblyLostRun.RunId != "" {
// Check the RAS to see if the run has been saved
var rasRun *galasaapi.Run
rasRun, err = submitter.launcher.GetRunsById(possiblyLostRun.RunId)
if err != nil {
log.Printf("processLostRuns - Failed to retrieve RAS run for %v - %v\n", possiblyLostRun.Name, err)
} else {
if rasRun != nil {
// The run was found in the RAS, not in the DSS
isRunLost = false

testStructure := rasRun.GetTestStructure()
runStatus := testStructure.GetStatus()
if runStatus == "finished" {

// The run has finished, so we no longer need to check its status
submitter.markRunFinished(possiblyLostRun, testStructure.GetResult(), submittedRuns, finishedRuns, fetchRas)

if possiblyLostRun.RunId == "" {
if possiblyLostRun.SubmissionId != "" {
// We don't know this runs' RunId yet
// so lets try to find it in the RAS
var rasRun *galasaapi.Run
rasRun, err = submitter.launcher.GetRunsBySubmissionId(possiblyLostRun.SubmissionId, possiblyLostRun.Group)
if err != nil {
log.Printf("processLostRuns - Failed to retrieve RAS run by submissionId %v - %v\n", possiblyLostRun.Name, err)
} else {
if rasRun != nil {
// The run was found in the RAS, not in the DSS
isRunLost = false

testStructure := rasRun.GetTestStructure()
runStatus := testStructure.GetStatus()
if runStatus == "finished" {

// The run has finished, so we no longer need to check its status
submitter.markRunFinished(possiblyLostRun, testStructure.GetResult(), submittedRuns, finishedRuns, fetchRas)
}
}
}
}
}

if isRunLost {
if possiblyLostRun.RunId != "" {
// Check the RAS to see if the run has been saved, as we know it's run id.
var rasRun *galasaapi.Run
rasRun, err = submitter.launcher.GetRunsById(possiblyLostRun.RunId)
if err != nil {
log.Printf("processLostRuns - Failed to retrieve RAS run for %v - %v\n", possiblyLostRun.Name, err)
} else {
if rasRun != nil {
// The run was found in the RAS, not in the DSS
isRunLost = false

testStructure := rasRun.GetTestStructure()
runStatus := testStructure.GetStatus()
if runStatus == "finished" {

// The run has finished, so we no longer need to check its status
submitter.markRunFinished(possiblyLostRun, testStructure.GetResult(), submittedRuns, finishedRuns, fetchRas)
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/runs/yamlReporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func TestYamlReportWorks(t *testing.T) {
Tests: []TestMethod{{Method: "method1", Result: "passed"}, {Method: "method2", Result: "passed"}},
GherkinUrl: "file:///my.feature",
GherkinFeature: "my",
SubmissionId: "123",
}

finishedRunsMap := make(map[string]*TestRun, 1)
Expand Down Expand Up @@ -74,7 +75,8 @@ func TestYamlReportWorks(t *testing.T) {
result: passed
gherkin: file:///my.feature
feature: my
group:""`
group:""
submissionId:"123"`

actualContents, err := mockFileSystem.ReadTextFile("myReportYamlFilename")
if err != nil {
Expand Down
Loading
Loading