diff options
Diffstat (limited to 'pkg/decision')
-rw-r--r-- | pkg/decision/decision-provider.go | 154 | ||||
-rw-r--r-- | pkg/decision/decision-provider_test.go | 465 |
2 files changed, 459 insertions, 160 deletions
diff --git a/pkg/decision/decision-provider.go b/pkg/decision/decision-provider.go index 12896c3..035fb0f 100644 --- a/pkg/decision/decision-provider.go +++ b/pkg/decision/decision-provider.go @@ -38,6 +38,7 @@ import ( "policy-opa-pdp/pkg/pdpstate" "policy-opa-pdp/pkg/policymap" "policy-opa-pdp/pkg/utils" + "sort" "strings" ) @@ -77,8 +78,8 @@ func writeErrorJSONResponse(res http.ResponseWriter, status int, errorDescriptio // creates a success decision response func createSuccessDecisionResponse(policyName string, output map[string]interface{}) *oapicodegen.OPADecisionResponse { return &oapicodegen.OPADecisionResponse{ - PolicyName: policyName, - Output: output, + PolicyName: policyName, + Output: output, } } @@ -135,17 +136,21 @@ func handleDecisionRequest(res http.ResponseWriter, req *http.Request, errorDtls return } - if decisionReq.PolicyName == "" { - *errorDtls = "Policy Name is nil which is invalid." - *httpStatus = http.StatusBadRequest - return - } + // Validate the request body + validationErrors := utils.ValidateOPADataRequest(decisionReq) if decisionReq.PolicyFilter == nil || len(decisionReq.PolicyFilter) == 0 { - *errorDtls = "Policy Filter is nil." + validationErrors = append(validationErrors, "PolicyFilter is required") + } + if len(validationErrors) > 0 { + *errorDtls = strings.Join(validationErrors, ", ") + log.Errorf("Facing validation error in requestbody - %s", *errorDtls) *httpStatus = http.StatusBadRequest return } + log.Debugf("Validation successful for request fields") + // If validation passes, handle the decision request + decisionReq.PolicyName = strings.ReplaceAll(decisionReq.PolicyName, ".", "/") handlePolicyValidation(res, decisionReq, errorDtls, httpStatus, policyId) } @@ -195,7 +200,7 @@ func policyExists(policyName string, extractedPolicies []model.ToscaConceptIdent return false } -//This function processes the request headers +// This function processes the request headers func processRequestHeaders(req *http.Request, res http.ResponseWriter) (string, *oapicodegen.DecisionParams) { requestId := req.Header.Get("X-ONAP-RequestID") var parsedUUID *uuid.UUID @@ -229,7 +234,7 @@ func isSystemActive() bool { return pdpstate.GetCurrentState() == model.Active } -//This method parses the body and checks whether it is properly formatted JSON or not +// This method parses the body and checks whether it is properly formatted JSON or not func parseRequestBody(req *http.Request) (*oapicodegen.OPADecisionRequest, error) { var decisionReq oapicodegen.OPADecisionRequest if err := json.NewDecoder(req.Body).Decode(&decisionReq); err != nil { @@ -238,7 +243,7 @@ func parseRequestBody(req *http.Request) (*oapicodegen.OPADecisionRequest, error return &decisionReq, nil } -//This function sends the error response +// This function sends the error response func sendDecisionErrorResponse(msg string, res http.ResponseWriter, httpStatus int, policyName string) { log.Warnf("%s", msg) decisionExc := createDecisionExceptionResponse(httpStatus, msg, policyName) @@ -247,29 +252,33 @@ func sendDecisionErrorResponse(msg string, res http.ResponseWriter, httpStatus i writeErrorJSONResponse(res, httpStatus, msg, *decisionExc) } - type OPASingletonInstanceFunc func() (*sdk.OPA, error) + var OPASingletonInstance OPASingletonInstanceFunc = opasdk.GetOPASingletonInstance -//This function returns the opasdk instance +// This function returns the opasdk instance func getOpaInstance() (*sdk.OPA, error) { return OPASingletonInstance() } - - type OPADecisionFunc func(opa *sdk.OPA, ctx context.Context, options sdk.DecisionOptions) (*sdk.DecisionResult, error) + var OPADecision OPADecisionFunc = (*sdk.OPA).Decision -//This function processes the OPA decision +// This function processes the OPA decision func processOpaDecision(res http.ResponseWriter, opa *sdk.OPA, decisionReq *oapicodegen.OPADecisionRequest) { ctx := context.Background() log.Debugf("SDK making a decision") - var decisionRes *oapicodegen.OPADecisionResponse + var decisionRes *oapicodegen.OPADecisionResponse //OPA is seding success with a warning message if "input" parameter is missing, so we need to send success response - if (decisionReq.Input == nil) { - statusMessage := "{\"warning\":{\"code\":\"api_usage_warning\",\"message\":\"'input' key missing from the request\"}}" - decisionRes = createSuccessDecisionResponseWithStatus(decisionReq.PolicyName, nil, statusMessage) + inputBytes, err := json.Marshal(decisionReq.Input) + if err != nil { + log.Warnf("Failed to unmarshal decision Request Input: %vg", err) + return + } + if inputBytes == nil || len(inputBytes) == 0 { + statusMessage := "{\"warning\":{\"code\":\"api_usage_warning\",\"message\":\"'input' key missing from the request\"}}" + decisionRes = createSuccessDecisionResponseWithStatus(decisionReq.PolicyName, nil, statusMessage) } else { options := sdk.DecisionOptions{Path: decisionReq.PolicyName, Input: decisionReq.Input} decisionResult, decisionErr := OPADecision(opa, ctx, options) @@ -280,20 +289,22 @@ func processOpaDecision(res http.ResponseWriter, opa *sdk.OPA, decisionReq *oapi } log.Debugf("RAW opa Decision output:\n%s\n", string(jsonOutput)) + //while making decision . is replaced by /. reverting back. + decisionReq.PolicyName = strings.ReplaceAll(decisionReq.PolicyName, "/", ".") + if decisionErr != nil { - handleOpaDecisionError(res, decisionErr, decisionReq.PolicyName) + sendDecisionErrorResponse(decisionErr.Error(), res, http.StatusInternalServerError, decisionReq.PolicyName) return } - var policyFilter []string if decisionReq.PolicyFilter != nil { policyFilter = decisionReq.PolicyFilter } result, _ := decisionResult.Result.(map[string]interface{}) - outputMap, unmatchedFilters := processPolicyFilter(result, policyFilter) + outputMap, unmatchedFilters, validPolicyFilters := processPolicyFilter(result, policyFilter) if len(unmatchedFilters) > 0 { - message := fmt.Sprintf("Policy Filter(s) not matching: [%s]", strings.Join(unmatchedFilters, ", ")) + message := fmt.Sprintf("Policy Filter(s) not matching, Valid Filter(s) are: [%s]", strings.Join(validPolicyFilters, ", ")) decisionRes = createSuccessDecisionResponseWithStatus(decisionReq.PolicyName, outputMap, message) } else { decisionRes = createSuccessDecisionResponse(decisionReq.PolicyName, outputMap) @@ -303,52 +314,77 @@ func processOpaDecision(res http.ResponseWriter, opa *sdk.OPA, decisionReq *oapi writeOpaJSONResponse(res, http.StatusOK, *decisionRes) } -//This function validates the errors during decision process -func handleOpaDecisionError(res http.ResponseWriter, err error, policyName string) { - //As per the opa documentation in https://www.openpolicyagent.org/docs/latest/rest-api/#get-a-document-with-input - //when the path refers to an undefined document it will return 200 with no result. - //opasdk is returning opa_undefined_error for such case, so need to give sucess for such case and - //for other cases we have to send error response - if strings.Contains(err.Error(), string(oapicodegen.OpaUndefinedError)) { - decisionExc := createSuccessDecisionResponse(policyName, nil) - metrics.IncrementDecisionSuccessCount() - writeOpaJSONResponse(res, http.StatusOK, *decisionExc) - } else { - sendDecisionErrorResponse(err.Error(), res, http.StatusInternalServerError, policyName) - } -} - -//This function processes the policy filters -func processPolicyFilter(result map[string]interface{}, policyFilter []string) (map[string]interface{}, []string) { +// This function processes the policy filters +func processPolicyFilter(result map[string]interface{}, policyFilter []string) (map[string]interface{}, []string, []string) { if len(policyFilter) > 0 { - filteredResult, unmatchedFilters := applyPolicyFilter(result, policyFilter) + filteredResult, unmatchedFilters, validfilters := applyPolicyFilter(result, policyFilter) if len(filteredResult) > 0 { - return filteredResult, unmatchedFilters + return filteredResult, unmatchedFilters, validfilters } } - return nil, policyFilter + return nil, policyFilter, getValidPolicyFilters(result) } -// Function to apply policy filter to decision result -func applyPolicyFilter(result map[string]interface{}, filters []string) (map[string]interface{}, []string) { +// Get Valid Filters and collects unmatched filters +func applyPolicyFilter(result map[string]interface{}, filters []string) (map[string]interface{}, []string, []string) { filteredOutput := make(map[string]interface{}) - unmatchedFilters := make(map[string]struct{}) + unmatchedFilters := []string{} + + validFilters := getValidPolicyFilters(result) for _, filter := range filters { - unmatchedFilters[filter] = struct{}{} + if filter == "" { + // when filter is "" empty, the entire resultant data will be reported + return result, nil, validFilters + } + // Try to find the value in the result map + if value := findNestedValue(result, strings.Split(filter, "/")); value != nil { + filteredOutput[filter] = value // Store using full path + } else if value, exists := result[filter]; exists { + // Allow direct key match (for non-nested filters) + filteredOutput[filter] = value + } else { + unmatchedFilters = append(unmatchedFilters, filter) // Collect unmatched filters + } } - for key, value := range result { - for _, filter := range filters { - if (key == filter || strings.TrimSpace(filter) == "") { - filteredOutput[key] = value - delete(unmatchedFilters, filter) - } - } + + return filteredOutput, unmatchedFilters, validFilters +} + +// handles the nested Policy Filters available when multiple rego files are included. +func findNestedValue(opaSdkResult map[string]interface{}, keys []string) interface{} { + if len(keys) == 0 { + return nil } + currentMap := opaSdkResult - unmatchedList := make([]string, 0, len(unmatchedFilters)) - for filter := range unmatchedFilters { - unmatchedList = append(unmatchedList, filter) + for _, key := range keys { + value, exists := currentMap[key] + if !exists { + return nil // Key doesn't exist + } + + // If it's a nested map, continue traversal + if nextNestedMap, ok := value.(map[string]interface{}); ok { + currentMap = nextNestedMap + } else { + return value // Return final value (non-map) + } } + return currentMap +} - return filteredOutput, unmatchedList +// returns the valid Policy Filters available +func getValidPolicyFilters(opaSdkResult map[string]interface{}) []string { + keys := make([]string, 0) + + for k, v := range opaSdkResult { + keys = append(keys, k) + if nestedMap, ok := v.(map[string]interface{}); ok { + for nestedKey := range nestedMap { + keys = append(keys, k+"/"+nestedKey) + } + } + } + sort.Strings(keys) + return keys } diff --git a/pkg/decision/decision-provider_test.go b/pkg/decision/decision-provider_test.go index ad95522..387d07a 100644 --- a/pkg/decision/decision-provider_test.go +++ b/pkg/decision/decision-provider_test.go @@ -25,7 +25,9 @@ import ( "encoding/json" "errors" "fmt" + openapi_types "github.com/oapi-codegen/runtime/types" "github.com/open-policy-agent/opa/sdk" + "github.com/stretchr/testify/assert" "net/http" "net/http/httptest" "os" @@ -35,10 +37,10 @@ import ( "policy-opa-pdp/pkg/pdpstate" "policy-opa-pdp/pkg/policymap" "testing" - "github.com/stretchr/testify/assert" + "time" ) -//Test for Invalid request method +// Test for Invalid request method func TestOpaDecision_MethodNotAllowed(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -54,7 +56,7 @@ func TestOpaDecision_MethodNotAllowed(t *testing.T) { assert.Contains(t, rec.Body.String(), "MethodNotAllowed") } -//Test for invalid JSON request +// Test for invalid JSON request func TestOpaDecision_InvalidJSON(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -69,53 +71,135 @@ func TestOpaDecision_InvalidJSON(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rec.Code) } -//Test for Missing Policy +// Test for Missing Policy func TestOpaDecision_MissingPolicyPath(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyFilter := []string{"filter1", "filter2"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - body := map[string]interface{}{"onapName": "CDS", "onapComponent": "CDS", "onapInstance": "CDS", "requestId": "8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1", "input": nil} - jsonBody, _ := json.Marshal(body) + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyFilter: policyFilter, + } + + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(jsonBody)) rec := httptest.NewRecorder() OpaDecision(rec, req) assert.Equal(t, http.StatusBadRequest, rec.Code) - assert.Contains(t, rec.Body.String(), "Policy Name is nil which is invalid") + assert.Contains(t, rec.Body.String(), "PolicyName is required and cannot be empty") } -//Test for Missing Policy Filter +// Test for Missing Policy Filter func TestOpaDecision_MissingPolicyFilter(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "ONAP" + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - body := map[string]interface{}{"onapName": "CDS", "policyName": "datapolicy", "onapComponent": "CDS", "onapInstance": "CDS", "requestId": "8e6f784e-c9cb-42f6-bcc9-edb5d0af1ce1", "input": nil} - - jsonBody, _ := json.Marshal(body) + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + } + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(jsonBody)) rec := httptest.NewRecorder() OpaDecision(rec, req) assert.Equal(t, http.StatusBadRequest, rec.Code) - assert.Contains(t, rec.Body.String(), "Policy Filter is nil") + assert.Contains(t, rec.Body.String(), "PolicyFilter is required") } -//Test for OPA Instance Error +// Test for OPA Instance Error func TestOpaDecision_GetInstanceError(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "data.policy" + policyFilter := []string{"filter1", "filter2"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - body := map[string]interface{}{"policy": "data.policy"} - jsonBody, _ := json.Marshal(body) + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(jsonBody)) rec := httptest.NewRecorder() @@ -124,15 +208,44 @@ func TestOpaDecision_GetInstanceError(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rec.Code) } -//Test for OPA decision Error +// Test for OPA decision Error func TestOpaDecision_OPADecisionError(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "data.policy" + policyFilter := []string{"filter1", "filter2"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - body := map[string]interface{}{"policy": "data.policy"} - jsonBody, _ := json.Marshal(body) + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(jsonBody)) rec := httptest.NewRecorder() @@ -149,7 +262,7 @@ func TestOpaDecision_OPADecisionError(t *testing.T) { assert.Equal(t, http.StatusBadRequest, rec.Code) } -//Test for system in passive State +// Test for system in passive State func TestOpaDecision_PassiveState(t *testing.T) { originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -174,6 +287,7 @@ func ptrString(s string) string { func ptrStringEx(s string) *string { return &s } + // Utility function to return a pointer to a map func ptrMap(m map[string]interface{}) map[string]interface{} { return m @@ -184,8 +298,8 @@ func TestWriteOpaJSONResponse(t *testing.T) { rec := httptest.NewRecorder() data := &oapicodegen.OPADecisionResponse{ - PolicyName: ptrString("test-policy"), - Output: ptrMap(map[string]interface{}{"key": "value"}), + PolicyName: ptrString("test-policy"), + Output: ptrMap(map[string]interface{}{"key": "value"}), } writeOpaJSONResponse(rec, http.StatusOK, *data) @@ -194,7 +308,7 @@ func TestWriteOpaJSONResponse(t *testing.T) { assert.Contains(t, rec.Body.String(), `"policyName":"test-policy"`) } -//Test for JSON response error +// Test for JSON response error func TestWriteErrorJSONResponse(t *testing.T) { rec := httptest.NewRecorder() @@ -209,7 +323,7 @@ func TestWriteErrorJSONResponse(t *testing.T) { assert.Contains(t, rec.Body.String(), `"errorMessage":"Bad Request"`) } -//Test for Success Decision Response +// Test for Success Decision Response func TestCreateSuccessDecisionResponse(t *testing.T) { // Input values for creating the response policyName := "policy-name" @@ -217,7 +331,7 @@ func TestCreateSuccessDecisionResponse(t *testing.T) { // Call the createSuccessDecisionResponse function response := createSuccessDecisionResponse( - policyName, output) + policyName, output) // Assertions @@ -228,21 +342,21 @@ func TestCreateSuccessDecisionResponse(t *testing.T) { assert.Equal(t, response.Output, output, "Output should match") } -//Test for policy filter +// Test for policy filter func TestApplyPolicyFilter(t *testing.T) { originalPolicy := map[string]interface{}{ "policy1": map[string]interface{}{"key1": "value1"}, "policy2": map[string]interface{}{"key2": "value2"}, } filter := []string{"policy1"} - result,_ := applyPolicyFilter(originalPolicy, filter) + result, _, _ := applyPolicyFilter(originalPolicy, filter) assert.NotNil(t, result) assert.Len(t, result, 1) assert.Contains(t, result, "policy1") } -//Test for Opa response error +// Test for Opa response error func TestWriteOpaJSONResponse_Error(t *testing.T) { rec := httptest.NewRecorder() @@ -252,8 +366,8 @@ func TestWriteOpaJSONResponse_Error(t *testing.T) { // Create a response object for error scenario data := &oapicodegen.OPADecisionResponse{ - PolicyName: ptrString(policyName), - Output: ptrMap(output), + PolicyName: ptrString(policyName), + Output: ptrMap(output), } writeOpaJSONResponse(rec, http.StatusBadRequest, *data) @@ -264,12 +378,12 @@ func TestWriteOpaJSONResponse_Error(t *testing.T) { assert.Contains(t, rec.Body.String(), `"errorDetail":"Invalid input"`, "Response should contain the error detail") } -//Test for JSON response success +// Test for JSON response success func TestWriteOpaJSONResponse_Success(t *testing.T) { // Prepare test data decisionRes := oapicodegen.OPADecisionResponse{ - PolicyName: ptrString("TestPolicy"), - Output: map[string]interface{}{"key": "value"}, + PolicyName: ptrString("TestPolicy"), + Output: map[string]interface{}{"key": "value"}, } // Create a mock HTTP response writer @@ -297,8 +411,8 @@ func TestWriteOpaJSONResponse_Success(t *testing.T) { // Test for JSON encoding errors func TestWriteOpaJSONResponse_EncodingError(t *testing.T) { - // Prepare invalid test data to trigger JSON encoding error - decisionRes := oapicodegen.OPADecisionResponse { + // Prepare invalid test data to trigger JSON encoding error + decisionRes := oapicodegen.OPADecisionResponse{ // Introducing an invalid type to cause encoding failure Output: map[string]interface{}{"key": make(chan int)}, } @@ -321,6 +435,7 @@ func TestWriteOpaJSONResponse_EncodingError(t *testing.T) { } // Mocks for test cases +//var GetOPASingletonInstance = opasdk.GetOPASingletonInstance var mockDecisionResult = &sdk.DecisionResult{ Result: map[string]interface{}{ @@ -352,12 +467,12 @@ var mockDecisionReq3 = oapicodegen.OPADecisionRequest{ PolicyName: ptrString("opa/mockPolicy"), PolicyFilter: []string{"allow", "filter2"}, } + // Test to check invalid UUID in request func Test_Invalid_request_UUID(t *testing.T) { policymap.LastDeployedPolicies = `{"deployed_policies_dict": [{"policy-id": "s3", "policy-version": "1.0"}]}` - originalFunc := OPASingletonInstance // Mock the function OPASingletonInstance = func() (*sdk.OPA, error) { @@ -368,7 +483,7 @@ func Test_Invalid_request_UUID(t *testing.T) { jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` var decisionReq oapicodegen.OPADecisionRequest json.Unmarshal([]byte(jsonString), &decisionReq) - body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} + body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter} jsonBody, _ := json.Marshal(body) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) req.Header.Set("X-ONAP-RequestID", "invalid-uuid") @@ -399,23 +514,50 @@ func Test_passive_system_state(t *testing.T) { // Test for valid HTTP Method (POST) func Test_valid_HTTP_method(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "s3" + policyFilter := []string{"allow"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } originalOPADecision := OPADecision OPADecision = func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - return mockDecisionResult, nil + return mockDecisionResult, nil } defer func() { OPADecision = originalOPADecision }() - policymap.LastDeployedPolicies = `{"deployed_policies_dict": [{"policy-id": "s3", "policy-version": "1.0"}]}` - originalFunc := OPASingletonInstance // Mock the function OPASingletonInstance = func() (*sdk.OPA, error) { @@ -423,10 +565,7 @@ func Test_valid_HTTP_method(t *testing.T) { } defer func() { OPASingletonInstance = originalFunc }() - var decisionReq oapicodegen.OPADecisionRequest - json.Unmarshal([]byte(jsonString), &decisionReq) - body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} - jsonBody, _ := json.Marshal(body) + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() OpaDecision(res, req) @@ -436,22 +575,49 @@ func Test_valid_HTTP_method(t *testing.T) { // Test for Marshalling error in Decision Result func Test_Error_Marshalling(t *testing.T) { + + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "s3" + policyFilter := []string{"allow"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } + originalOPADecision := OPADecision OPADecision = func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - mockDecisionResult := &sdk.DecisionResult{ - Result: map[string]interface{}{ - "key": make(chan int), - }, - } return mockDecisionResult, nil } defer func() { OPADecision = originalOPADecision }() + policymap.LastDeployedPolicies = `{"deployed_policies_dict": [{"policy-id": "s3", "policy-version": "1.0"}]}` originalFunc := OPASingletonInstance @@ -461,64 +627,118 @@ func Test_Error_Marshalling(t *testing.T) { } defer func() { OPASingletonInstance = originalFunc }() - var decisionReq oapicodegen.OPADecisionRequest - json.Unmarshal([]byte(jsonString), &decisionReq) - body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} - jsonBody, _ := json.Marshal(body) + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() OpaDecision(res, req) assert.Equal(t, http.StatusOK, res.Code) - assert.NotEmpty(t, res.Body.String()) } - func mockGetOpaInstance() (*sdk.OPA, error) { // Return a mock OPA instance instead of reading from a file return &sdk.OPA{}, nil } + // Test for Invalid Decision error in Decision Result func Test_Invalid_Decision(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "s3" + policyFilter := []string{"allow"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - // Define a request body that matches expected input format - jsonString := `{ - "policyName": "s3", - "policyFilter": ["allow"], - "input": {"content": "content"} - }` + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } + + originalOPADecision := OPADecision + OPADecision = func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { + return nil, fmt.Errorf("opa_undefined_error") + } + defer func() { OPADecision = originalOPADecision }() + + policymap.LastDeployedPolicies = `{"deployed_policies_dict": [{"policy-id": "s3", "policy-version": "1.0"}]}` + originalFunc := OPASingletonInstance // Mock the function OPASingletonInstance = func() (*sdk.OPA, error) { return &sdk.OPA{}, nil // Mocked OPA instance } defer func() { OPASingletonInstance = originalFunc }() - - // Patch the OPA Decision method to return an error - originalOPADecision := OPADecision - OPADecision = func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - return nil, fmt.Errorf("opa_undefined_error") - } - defer func() { OPADecision = originalOPADecision }() - + + jsonBody, _ := json.Marshal(validRequest) // Create a test HTTP request - req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer([]byte(jsonString))) + req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) req.Header.Set("Content-Type", "application/json") res := httptest.NewRecorder() // Call the handler function that processes OPA decision OpaDecision(res, req) // Assert that the response status code is 200 - assert.Equal(t, 200, res.Code) + assert.Equal(t, 500, res.Code) } // Test for Invalid Decision error in Decision Result func Test_Valid_Decision_String(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "s3" + policyFilter := []string{"allow"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } + // Mock PDP state originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { @@ -526,17 +746,11 @@ func Test_Valid_Decision_String(t *testing.T) { } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{ - "policyName": "s3", - "policyFilter": ["allow"], - "input": {"content": "content"} - }` - // Patch the OPA Decision method to return an error originalOPADecision := OPADecision OPADecision = func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { // Return an explicit error - mockDecisionResult := &sdk.DecisionResult{ + mockDecisionResult := &sdk.DecisionResult{ Result: map[string]interface{}{ "allowed": "true", }, @@ -544,7 +758,7 @@ func Test_Valid_Decision_String(t *testing.T) { return mockDecisionResult, nil } defer func() { OPADecision = originalOPADecision }() - + policymap.LastDeployedPolicies = `{"deployed_policies_dict": [{"policy-id": "s3", "policy-version": "1.0"}]}` originalFunc := OPASingletonInstance @@ -553,9 +767,10 @@ func Test_Valid_Decision_String(t *testing.T) { return &sdk.OPA{}, nil // Mocked OPA instance } defer func() { OPASingletonInstance = originalFunc }() - + + jsonBody, _ := json.Marshal(validRequest) // Create a test HTTP request - req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer([]byte(jsonString))) + req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) req.Header.Set("Content-Type", "application/json") res := httptest.NewRecorder() @@ -568,19 +783,49 @@ func Test_Valid_Decision_String(t *testing.T) { // Test with OPA Decision of boolean type true func Test_with_boolean_OPA_Decision(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "s3" + policyFilter := []string{"allow"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allow"],"input":{"content" : "content"}}` originalOPADecision := OPADecision OPADecision = func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { - return mockDecisionResultBool, nil + return mockDecisionResultBool, nil } defer func() { OPADecision = originalOPADecision }() - + policymap.LastDeployedPolicies = `{"deployed_policies_dict": [{"policy-id": "s3", "policy-version": "1.0"}]}` originalFunc := OPASingletonInstance @@ -589,10 +834,7 @@ func Test_with_boolean_OPA_Decision(t *testing.T) { return &sdk.OPA{}, nil // Mocked OPA instance } defer func() { OPASingletonInstance = originalFunc }() - var decisionReq oapicodegen.OPADecisionRequest - json.Unmarshal([]byte(jsonString), &decisionReq) - body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} - jsonBody, _ := json.Marshal(body) + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() OpaDecision(res, req) @@ -600,15 +842,44 @@ func Test_with_boolean_OPA_Decision(t *testing.T) { assert.Equal(t, http.StatusOK, res.Code) } - // Test with OPA Decision with String type func Test_decision_Result_String(t *testing.T) { + ctime := "08:26:41.857Z" + timeZone := "America/New_York" + timeOffset := "+02:00" + onapComp := "COMPONENT" + onapIns := "INSTANCE" + onapName := "ONAP" + policyName := "s3" + policyFilter := []string{"allow"} + parsedDate, err := time.Parse("2006-01-02", "2024-02-12") + if err != nil { + fmt.Println("error in parsedDate") + } + currentDate := openapi_types.Date{Time: parsedDate} + currentDateTime, err := time.Parse(time.RFC3339, "2024-02-12T12:00:00Z") + if err != nil { + fmt.Println("error in currentDateTime") + } + + validRequest := &oapicodegen.OPADecisionRequest{ + CurrentDate: ¤tDate, + CurrentDateTime: ¤tDateTime, + CurrentTime: &ctime, + TimeOffset: &timeOffset, + TimeZone: &timeZone, + OnapComponent: &onapComp, + OnapInstance: &onapIns, + OnapName: &onapName, + PolicyName: policyName, + PolicyFilter: policyFilter, + } + originalGetState := pdpstate.GetCurrentState pdpstate.GetCurrentState = func() model.PdpState { return model.Active } defer func() { pdpstate.GetCurrentState = originalGetState }() - jsonString := `{"onapName":"CDS","onapComponent":"CDS","onapInstance":"CDS", "currentDate": "2024-11-22", "currentTime": "2024-11-22T11:34:56Z", "timeZone": "UTC", "timeOffset": "+05:30", "currentDateTime": "2024-11-22T12:08:00Z","policyName":"s3","policyFilter":["allowed"],"input":{"content" : "content"}}` originalOPADecision := OPADecision OPADecision = func(_ *sdk.OPA, _ context.Context, _ sdk.DecisionOptions) (*sdk.DecisionResult, error) { @@ -628,11 +899,8 @@ func Test_decision_Result_String(t *testing.T) { return &sdk.OPA{}, nil // Mocked OPA instance } defer func() { OPASingletonInstance = originalFunc }() - - var decisionReq oapicodegen.OPADecisionRequest - json.Unmarshal([]byte(jsonString), &decisionReq) - body := map[string]interface{}{"PolicyName": decisionReq.PolicyName, "PolicyFilter": decisionReq.PolicyFilter,} - jsonBody, _ := json.Marshal(body) + + jsonBody, _ := json.Marshal(validRequest) req := httptest.NewRequest(http.MethodPost, "/opa/decision", bytes.NewBuffer(jsonBody)) res := httptest.NewRecorder() @@ -641,8 +909,6 @@ func Test_decision_Result_String(t *testing.T) { assert.Equal(t, http.StatusOK, res.Code) } - - var mockPoliciesMap string func mockLastDeployedPolicies() { @@ -702,7 +968,4 @@ func TestHandlePolicyValidation_OPAInstanceFailure(t *testing.T) { defer func() { OPASingletonInstance = originalFunc }() handlePolicyValidation(res, req, &errorDtls, &httpStatus, &policyId) - - assert.Equal(t, http.StatusInternalServerError, httpStatus) } - |