@@ -20,8 +20,10 @@ import (
2020)
2121
2222const (
23- ExampleRepoID = 466156917
24- ExamplePRNumber = 3
23+ ExampleRepoID = 466156917
24+ ExamplePRNumber = 3
25+ InvalidUUIDProvided = "Invalid UUID provided"
26+ GoUUIDValidationError = " in path should match '^[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?[a-fA-F0-9]{4}-?[a-fA-F0-9]{4}-?[a-fA-F0-9]{12}$'"
2527)
2628
2729var (
3032 PY_API_URL string
3133 GO_API_URL string
3234 DEBUG bool
33- REMOTE bool
34- PROD bool
3535 MAX_PARALLEL int
3636 PROJECT_UUID string
3737 USER_UUID string
@@ -102,31 +102,23 @@ var (
102102func init() {
103103 TOKEN = os.Getenv("TOKEN")
104104 XACL = os.Getenv("XACL")
105- REMOTE = os.Getenv("REMOTE") != ""
106- PROD = os.Getenv("PROD") != ""
107105 PY_API_URL = os.Getenv("PY_API_URL")
108- if PY_API_URL == "" {
109- if REMOTE {
110- if PROD {
111- PY_API_URL = "https://api.easycla.lfx.linuxfoundation.org"
112- } else {
113- PY_API_URL = "https://api.lfcla.dev.platform.linuxfoundation.org"
114- }
115- } else {
116- PY_API_URL = "http://127.0.0.1:5000"
117- }
106+ switch PY_API_URL {
107+ case "local", "":
108+ PY_API_URL = "http://127.0.0.1:5000"
109+ case "dev":
110+ PY_API_URL = "https://api.lfcla.dev.platform.linuxfoundation.org"
111+ case "prod":
112+ PY_API_URL = "https://api.easycla.lfx.linuxfoundation.org"
118113 }
119114 GO_API_URL = os.Getenv("GO_API_URL")
120- if GO_API_URL == "" {
121- if REMOTE {
122- if PROD {
123- GO_API_URL = "https://api-gw.platform.linuxfoundation.org/cla-service"
124- } else {
125- GO_API_URL = "https://api-gw.dev.platform.linuxfoundation.org/cla-service"
126- }
127- } else {
128- GO_API_URL = "http://127.0.0.1:5001"
129- }
115+ switch GO_API_URL {
116+ case "local", "":
117+ GO_API_URL = "http://127.0.0.1:5001"
118+ case "dev":
119+ GO_API_URL = "https://api-gw.dev.platform.linuxfoundation.org/cla-service"
120+ case "prod":
121+ GO_API_URL = "https://api-gw.platform.linuxfoundation.org/cla-service"
130122 }
131123 DEBUG = os.Getenv("DEBUG") != ""
132124 MAX_PARALLEL = runtime.NumCPU()
@@ -341,6 +333,21 @@ func compareNestedFields(t *testing.T, pyData, goData, keyMapping map[string]int
341333 }
342334}
343335
336+ func expectedPyInvalidUUID(field string) map[string]interface{} {
337+ return map[string]interface{}{
338+ "errors": map[string]interface{}{
339+ field: InvalidUUIDProvided,
340+ },
341+ }
342+ }
343+
344+ func expectedGoInvalidUUID(field string) map[string]interface{} {
345+ return map[string]interface{}{
346+ "code": float64(605),
347+ "message": field + GoUUIDValidationError,
348+ }
349+ }
350+
344351func runProjectCompatAPIForProject(t *testing.T, projectId string) {
345352 apiURL := PY_API_URL + fmt.Sprintf(ProjectAPIPath[0], projectId)
346353 Debugf("Py API call: %s\n", apiURL)
@@ -396,6 +403,41 @@ func runProjectCompatAPIForProject(t *testing.T, projectId string) {
396403 }
397404}
398405
406+ func runProjectCompatAPIForProjectExpectFail(t *testing.T, projectId string) {
407+ apiURL := PY_API_URL + fmt.Sprintf(ProjectAPIPath[0], projectId)
408+ Debugf("Py API call: %s\n", apiURL)
409+ oldResp, err := http.Get(apiURL)
410+ if err != nil {
411+ t.Fatalf("Failed to call API: %v", err)
412+ }
413+ assert.Equal(t, http.StatusBadRequest, oldResp.StatusCode, "Expected 400 from Py API")
414+ defer oldResp.Body.Close()
415+ oldBody, _ := io.ReadAll(oldResp.Body)
416+ var oldJSON interface{}
417+ err = json.Unmarshal(oldBody, &oldJSON)
418+ assert.NoError(t, err)
419+ Debugf("Py raw response: %+v\n", string(oldBody))
420+ Debugf("Py response: %+v\n", oldJSON)
421+
422+ apiURL = GO_API_URL + fmt.Sprintf(ProjectAPIPath[2], projectId)
423+ Debugf("Go API call: %s\n", apiURL)
424+ newResp, err := http.Get(apiURL)
425+ if err != nil {
426+ t.Fatalf("Failed to call API: %v", err)
427+ }
428+ assert.Equal(t, http.StatusUnprocessableEntity, newResp.StatusCode, "Expected 422 from Go API")
429+ defer newResp.Body.Close()
430+ newBody, _ := io.ReadAll(newResp.Body)
431+ var newJSON interface{}
432+ err = json.Unmarshal(newBody, &newJSON)
433+ assert.NoError(t, err)
434+ Debugf("Go raw Response: %+v\n", string(newBody))
435+ Debugf("Go response: %+v\n", newJSON)
436+
437+ assert.Equal(t, expectedPyInvalidUUID("project_id"), oldJSON)
438+ assert.Equal(t, expectedGoInvalidUUID("projectID"), newJSON)
439+ }
440+
399441func TestProjectCompatAPI(t *testing.T) {
400442 projectId := PROJECT_UUID
401443 if projectId == "" {
@@ -416,6 +458,40 @@ func TestProjectCompatAPI(t *testing.T) {
416458 runProjectCompatAPIForProject(t, projectId)
417459}
418460
461+ func TestProjectCompatAPIWithNonV4UUID(t *testing.T) {
462+ projectId := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" // Non-v4 UUID
463+ putTestItem("projects", "project_id", projectId, "S", map[string]interface{}{
464+ "project_name": "CNCF",
465+ "project_icla_enabled": true,
466+ "project_ccla_enabled": true,
467+ "project_ccla_requires_icla_signature": true,
468+ "date_created": "2022-11-21T10:31:31Z",
469+ "date_modified": "2023-02-23T13:14:48Z",
470+ "foundation_sfid": "a09410000182dD2AAI",
471+ "version": "2",
472+ }, DEBUG)
473+ defer deleteTestItem("projects", "project_id", projectId, "S", DEBUG)
474+
475+ runProjectCompatAPIForProject(t, projectId)
476+ }
477+
478+ func TestProjectCompatAPIWithInvalidUUID(t *testing.T) {
479+ projectId := "6ba7b810-9dad-11d1-80b4-00c04fd430cg" // Invalid UUID - "g" is not a hex digit
480+ putTestItem("projects", "project_id", projectId, "S", map[string]interface{}{
481+ "project_name": "CNCF",
482+ "project_icla_enabled": true,
483+ "project_ccla_enabled": true,
484+ "project_ccla_requires_icla_signature": true,
485+ "date_created": "2022-11-21T10:31:31Z",
486+ "date_modified": "2023-02-23T13:14:48Z",
487+ "foundation_sfid": "a09410000182dD2AAI",
488+ "version": "2",
489+ }, DEBUG)
490+ defer deleteTestItem("projects", "project_id", projectId, "S", DEBUG)
491+
492+ runProjectCompatAPIForProjectExpectFail(t, projectId)
493+ }
494+
419495func TestAllProjectsCompatAPI(t *testing.T) {
420496 allProjects := getAllPrimaryKeys("projects", "project_id", "S")
421497
@@ -610,6 +686,96 @@ func runUserActiveSignatureAPIForUser(t *testing.T, userId string) {
610686 }
611687}
612688
689+ func runUserActiveSignatureAPIForUserExpectFail(t *testing.T, userId string) {
690+ apiURL := PY_API_URL + fmt.Sprintf(UserActiveSignatureAPIPath[0], userId)
691+ Debugf("Py API call: %s\n", apiURL)
692+ oldResp, err := http.Get(apiURL)
693+ if err != nil {
694+ t.Fatalf("Failed to call API: %v", err)
695+ }
696+ assert.Equal(t, http.StatusBadRequest, oldResp.StatusCode, "Expected 400 from Py API")
697+ defer oldResp.Body.Close()
698+ oldBody, _ := io.ReadAll(oldResp.Body)
699+ var oldJSON interface{}
700+ err = json.Unmarshal(oldBody, &oldJSON)
701+ assert.NoError(t, err)
702+ Debugf("Py raw response: %+v\n", string(oldBody))
703+ Debugf("Py response: %+v\n", oldJSON)
704+
705+ apiURL = GO_API_URL + fmt.Sprintf(UserActiveSignatureAPIPath[1], userId)
706+ Debugf("Go API call: %s\n", apiURL)
707+ newResp, err := http.Get(apiURL)
708+ if err != nil {
709+ t.Fatalf("Failed to call API: %v", err)
710+ }
711+ assert.Equal(t, http.StatusUnprocessableEntity, newResp.StatusCode, "Expected 422 from Go API")
712+ defer newResp.Body.Close()
713+ newBody, _ := io.ReadAll(newResp.Body)
714+ var newJSON interface{}
715+ err = json.Unmarshal(newBody, &newJSON)
716+ assert.NoError(t, err)
717+ Debugf("Go raw Response: %+v\n", string(newBody))
718+ Debugf("Go response: %+v\n", newJSON)
719+
720+ assert.Equal(t, expectedPyInvalidUUID("user_id"), oldJSON)
721+ assert.Equal(t, expectedGoInvalidUUID("userID"), newJSON)
722+ }
723+
724+ func TestUserActiveSignatureAPIWithInvalidUUID(t *testing.T) {
725+ userId := "6ba7b810-9dad-11d1-80b4-00c04fd430cg" // Invalid UUID "g" is not a hex digit
726+ projectId := uuid.New().String()
727+ key := "active_signature:" + userId
728+ expire := time.Now().Add(time.Hour).Unix()
729+ iValue := map[string]interface{}{
730+ "user_id": userId,
731+ "project_id": projectId,
732+ "repository_id": fmt.Sprintf("%d", REPO_ID),
733+ "pull_request_id": fmt.Sprintf("%d", PR_ID),
734+ }
735+ value, err := json.Marshal(iValue)
736+ if err != nil {
737+ t.Fatalf("failed to marshal value: %+v", err)
738+ }
739+ putTestItem("projects", "project_id", projectId, "S", map[string]interface{}{}, DEBUG)
740+ putTestItem("store", "key", key, "S", map[string]interface{}{
741+ "value": string(value),
742+ "expire": expire,
743+ }, DEBUG)
744+ defer deleteTestItem("projects", "project_id", projectId, "S", DEBUG)
745+ defer deleteTestItem("store", "key", key, "S", DEBUG)
746+ runUserActiveSignatureAPIForUserExpectFail(t, userId)
747+ }
748+
749+ func TestUserActiveSignatureAPIWithNonV4UUID(t *testing.T) {
750+ userId := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" // Non-v4 UUID
751+ projectId := uuid.New().String()
752+ key := "active_signature:" + userId
753+ expire := time.Now().Add(time.Hour).Unix()
754+ iValue := map[string]interface{}{
755+ "user_id": userId,
756+ "project_id": projectId,
757+ "repository_id": fmt.Sprintf("%d", REPO_ID),
758+ "pull_request_id": fmt.Sprintf("%d", PR_ID),
759+ }
760+ if rand.Intn(2) == 0 {
761+ mrId := rand.Intn(100)
762+ iValue["merge_request_id"] = fmt.Sprintf("%d", mrId)
763+ iValue["return_url"] = fmt.Sprintf("https://gitlab.com/gitlab-org/gitlab/-/merge_requests/%d", mrId)
764+ }
765+ value, err := json.Marshal(iValue)
766+ if err != nil {
767+ t.Fatalf("failed to marshal value: %+v", err)
768+ }
769+ putTestItem("projects", "project_id", projectId, "S", map[string]interface{}{}, DEBUG)
770+ putTestItem("store", "key", key, "S", map[string]interface{}{
771+ "value": string(value),
772+ "expire": expire,
773+ }, DEBUG)
774+ defer deleteTestItem("projects", "project_id", projectId, "S", DEBUG)
775+ defer deleteTestItem("store", "key", key, "S", DEBUG)
776+ runUserActiveSignatureAPIForUser(t, userId)
777+ }
778+
613779func TestUserActiveSignatureAPI(t *testing.T) {
614780 userId := USER_UUID
615781 if userId == "" {
0 commit comments