diff --git a/pkg/api/api.go b/pkg/api/api.go index 49613a1..61d8c1c 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -52,6 +52,7 @@ func decodeJSONResponse(url string, httpResp *http.Response, response interface{ } bodyStr := strings.TrimSpace(string(body)) + if err := json.Unmarshal(body, response); err != nil { retryable = isRetryableHTTPResponse(httpResp.StatusCode, bodyStr) log.Errorf( diff --git a/test/integration/api/allocation/invalid_query_parameter_test.go b/test/integration/api/allocation/invalid_query_parameter_test.go new file mode 100644 index 0000000..9485b83 --- /dev/null +++ b/test/integration/api/allocation/invalid_query_parameter_test.go @@ -0,0 +1,89 @@ +package allocation + +// Validates that allocation api handles invalid parameters correctly. +// Passing Criteria: +// Returns a HTTP 400 error instead of a response with no error or an HTTP 500 error. + +import ( + "strings" + "testing" + + "github.com/opencost/opencost-integration-tests/pkg/api" +) + +func TestAllocationInvalidParameters(t *testing.T) { + apiObj := api.NewAPI() + + invalidParameterTestCases := []struct { + name string + window string + filter string + aggregate string + accumulate string + includeidle string + }{ + { + name: "ReverseWindow", + window: "2026-06-16T00:00:00Z,2026-06-15T00:00:00Z", + aggregate: "namespace", + }, + { + name: "InvalidWindowFormat", + window: "invalid-window-format", + aggregate: "namespace", + }, + { + name: "InvalidAggregate", + window: "24h", + aggregate: "invalid-aggregate", + accumulate: "true", + includeidle: "true", + }, + { + name: "InvalidFilter", + window: "24h", + filter: "invalid-filter", + accumulate: "true", + includeidle: "true", + aggregate: "namespace", + }, + { + name: "InvalidAccumulate", + window: "24h", + accumulate: "invalid-accumulate", + includeidle: "true", + aggregate: "namespace", + }, + { + name: "InvalidIncludeIdle", + window: "24h", + accumulate: "true", + includeidle: "invalid-include-idle", + aggregate: "namespace", + }, + } + + for _, tc := range invalidParameterTestCases { + t.Run(tc.name, func(t *testing.T) { + t.Logf("Testing: %s", tc.name) + + _, err := apiObj.GetAllocation(api.AllocationRequest{ + Window: tc.window, + Filter: tc.filter, + Accumulate: tc.accumulate, + IncludeIdle: tc.includeidle, + Aggregate: tc.aggregate, + }) + + // Assert that an error was returned since input is invalid. + if err == nil { + t.Fatalf("Expected an error for invalid input, but got a successful response") + } + + // Assert that it is a 400 error. + if !strings.Contains(err.Error(), "HTTP 400") { + t.Fatalf("expected HTTP 400 error, got %v", err) + } + }) + } +} diff --git a/test/integration/api/allocation/test.bats b/test/integration/api/allocation/test.bats index 2d41bd7..9f5c736 100644 --- a/test/integration/api/allocation/test.bats +++ b/test/integration/api/allocation/test.bats @@ -6,6 +6,7 @@ setup() { teardown() { : # nothing to tear down } + @test "allocation: controller kind consistency" { go test allocation_controller_consistency_test.go } @@ -45,3 +46,7 @@ teardown() { @test "validate_api: validate if all of idle costs are spread" { go test share_idle_shares_test.go } + +@test "validate_api: validate api can handle invalid parameters" { + go test invalid_query_parameter_test.go +} diff --git a/test/integration/api/asset/invalid_query_parameter_test.go b/test/integration/api/asset/invalid_query_parameter_test.go new file mode 100644 index 0000000..e8277e7 --- /dev/null +++ b/test/integration/api/asset/invalid_query_parameter_test.go @@ -0,0 +1,57 @@ +package assets + +// Validates that assets api handles invalid parameters correctly. +// Passing Criteria: +// Returns a HTTP 400 error instead of a response with no error or an HTTP 500 error. + +import ( + "strings" + "testing" + + "github.com/opencost/opencost-integration-tests/pkg/api" +) + +func TestAssetInvalidParameters(t *testing.T) { + apiObj := api.NewAPI() + + invalidParameterTestCases := []struct { + name string + window string + assetType string + }{ + { + name: "ReverseWindow", + window: "2026-06-16T00:00:00Z,2026-06-15T00:00:00Z", + assetType: "node", + }, + { + name: "InvalidWindowFormat", + window: "invalid-window-format", + assetType: "node", + }, + { + name: "InvalidFilter", + window: "24h", + assetType: "invalid-filter", + }, + } + + for _, tc := range invalidParameterTestCases { + t.Run(tc.name, func(t *testing.T) { + t.Logf("Testing: %s", tc.name) + + _, err := apiObj.GetAssets(api.AssetsRequest{ + Window: tc.window, + Filter: tc.assetType, + }) + + if err == nil { + t.Fatalf("Expected an error for invalid input, but got a successful response") + } + + if !strings.Contains(err.Error(), "HTTP 400") { + t.Fatalf("expected HTTP 400 error, got %v", err) + } + }) + } +} diff --git a/test/integration/api/asset/test.bats b/test/integration/api/asset/test.bats index a642c16..f66f6f1 100644 --- a/test/integration/api/asset/test.bats +++ b/test/integration/api/asset/test.bats @@ -7,6 +7,10 @@ teardown() { : # nothing to tear down } +@test "asset: Invalid Parameters" { + go test invalid_query_parameter_test.go +} + @test "asset: Node Labels" { go test node_labels_test.go }