From 281991f93ac3891303e84fc8097a1beeade3694c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Fri, 20 Mar 2026 21:08:02 -0300 Subject: [PATCH 1/9] docs: add HTTP 201 success response to Swagger annotations The Validate endpoint now returns 201 for new requests and 200 for duplicates, but Swagger only documented 200. Add the 201 response entry and clarify both descriptions. --- api/docs.go | 8 +++++++- api/openapi.yaml | 8 +++++++- api/swagger.json | 8 +++++++- api/swagger.yaml | 6 +++++- internal/adapters/http/in/validation_handler.go | 3 ++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/api/docs.go b/api/docs.go index f8cf78c..a969222 100644 --- a/api/docs.go +++ b/api/docs.go @@ -1869,7 +1869,13 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "Validation result", + "description": "Duplicate request (idempotent)", + "schema": { + "$ref": "#/definitions/tracer_pkg_model.ValidationResponse" + } + }, + "201": { + "description": "New validation created", "schema": { "$ref": "#/definitions/tracer_pkg_model.ValidationResponse" } diff --git a/api/openapi.yaml b/api/openapi.yaml index ff07d9a..05ab2bd 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1458,7 +1458,13 @@ paths: application/json: schema: $ref: '#/components/schemas/tracer_pkg_model.ValidationResponse' - description: Validation result + description: Duplicate request (idempotent) + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/tracer_pkg_model.ValidationResponse' + description: New validation created "400": content: application/json: diff --git a/api/swagger.json b/api/swagger.json index 2e7b4ed..7640b34 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -1867,7 +1867,13 @@ ], "responses": { "200": { - "description": "Validation result", + "description": "Duplicate request (idempotent)", + "schema": { + "$ref": "#/definitions/tracer_pkg_model.ValidationResponse" + } + }, + "201": { + "description": "New validation created", "schema": { "$ref": "#/definitions/tracer_pkg_model.ValidationResponse" } diff --git a/api/swagger.yaml b/api/swagger.yaml index 0a587c6..eefa756 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -2040,7 +2040,11 @@ paths: - application/json responses: "200": - description: Validation result + description: Duplicate request (idempotent) + schema: + $ref: '#/definitions/tracer_pkg_model.ValidationResponse' + "201": + description: New validation created schema: $ref: '#/definitions/tracer_pkg_model.ValidationResponse' "400": diff --git a/internal/adapters/http/in/validation_handler.go b/internal/adapters/http/in/validation_handler.go index 39dac4e..ea79ed0 100644 --- a/internal/adapters/http/in/validation_handler.go +++ b/internal/adapters/http/in/validation_handler.go @@ -68,7 +68,8 @@ func NewValidationHandler(service ValidationService, clk clock.Clock) (*Validati // @Produce json // @Security ApiKeyAuth // @Param request body model.ValidationRequest true "Validation request" -// @Success 200 {object} model.ValidationResponse "Validation result" +// @Success 200 {object} model.ValidationResponse "Duplicate request (idempotent)" +// @Success 201 {object} model.ValidationResponse "New validation created" // @Failure 400 {object} api.ErrorResponse "Invalid input" // @Failure 401 {object} api.ErrorResponse "Unauthorized" // @Failure 413 {object} api.ErrorResponse "Payload too large (exceeds 100KB)" From 38b685c8d35eaa79d239f79001b7e3776e4ad945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Fri, 20 Mar 2026 21:11:12 -0300 Subject: [PATCH 2/9] test: improve deep copy verification for nested pointers Add mutation of AccountID pointer inside Scopes to catch shallow copy of nested pointer fields. The append-only check would miss shared pointer references between original and copy. --- pkg/model/transaction_validation_response_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/model/transaction_validation_response_test.go b/pkg/model/transaction_validation_response_test.go index 9dd9c26..da6e8dd 100644 --- a/pkg/model/transaction_validation_response_test.go +++ b/pkg/model/transaction_validation_response_test.go @@ -250,6 +250,10 @@ func TestTransactionValidation_ToValidationResponse_DefensiveCopy_Scopes(t *test // Mutate response's Scopes slice (append) resp.LimitUsageDetails[0].Scopes = append(resp.LimitUsageDetails[0].Scopes, Scope{}) + // Mutate a nested pointer inside the copied Scope to verify deep copy + mutatedID := uuid.MustParse("99999999-9999-9999-9999-999999999999") + resp.LimitUsageDetails[0].Scopes[0].AccountID = &mutatedID + // Original must remain unchanged require.Len(t, tv.LimitUsageDetails[0].Scopes, 1, "original Scopes length mutated via response append") From 64b28f6decbdf64aafcd8484ae12dbf5fbd84c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Fri, 20 Mar 2026 21:13:53 -0300 Subject: [PATCH 3/9] test: remove stale TDD-RED compile failure comments These 4 comments were scaffolding from the TDD-RED phase and should have been removed when the implementation was added in TDD-GREEN. --- pkg/hash/hash_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/hash/hash_test.go b/pkg/hash/hash_test.go index c441291..51e36ed 100644 --- a/pkg/hash/hash_test.go +++ b/pkg/hash/hash_test.go @@ -23,7 +23,6 @@ import ( // the same int32 hash value. This is critical for advisory locks - concurrent // requests with the same request_id MUST acquire the same lock. // -// This test will FAIL to compile because HashUUIDToInt32 doesn't exist yet. func TestHashUUIDToInt32_Deterministic(t *testing.T) { t.Parallel() @@ -78,7 +77,6 @@ func TestHashUUIDToInt32_Deterministic(t *testing.T) { // different hash values (with high probability). This ensures that advisory // locks for different request_ids don't collide. // -// This test will FAIL to compile because HashUUIDToInt32 doesn't exist yet. func TestHashUUIDToInt32_DifferentInputs(t *testing.T) { t.Parallel() @@ -118,7 +116,6 @@ func TestHashUUIDToInt32_DifferentInputs(t *testing.T) { // TestHashUUIDToInt32_BoundaryValues tests edge cases and boundary values // to ensure the hash function handles them correctly. // -// This test will FAIL to compile because HashUUIDToInt32 doesn't exist yet. func TestHashUUIDToInt32_BoundaryValues(t *testing.T) { t.Parallel() @@ -163,7 +160,6 @@ func TestHashUUIDToInt32_BoundaryValues(t *testing.T) { // This is important for advisory lock performance - we don't want all locks // to cluster in a small range. // -// This test will FAIL to compile because HashUUIDToInt32 doesn't exist yet. func TestHashUUIDToInt32_Distribution(t *testing.T) { t.Parallel() From efdda002f3ab8b0b2941eb068240f5859e06b191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Fri, 20 Mar 2026 21:15:27 -0300 Subject: [PATCH 4/9] test: use deterministic time in handler idempotency test Replace time.Now().UTC() with testutil.DefaultTestTime for stable test data across runs. Remove unused time import. --- .../adapters/http/in/validation_handler_idempotency_test.go | 3 +-- pkg/hash/hash_test.go | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/adapters/http/in/validation_handler_idempotency_test.go b/internal/adapters/http/in/validation_handler_idempotency_test.go index 613f0a6..51a1efb 100644 --- a/internal/adapters/http/in/validation_handler_idempotency_test.go +++ b/internal/adapters/http/in/validation_handler_idempotency_test.go @@ -10,7 +10,6 @@ import ( "net/http" "net/http/httptest" "testing" - "time" "github.com/gofiber/fiber/v2" "github.com/google/uuid" @@ -188,7 +187,7 @@ func TestValidationHandler_Validate_IdempotencyHeader(t *testing.T) { }, LimitUsageDetails: []model.LimitUsageDetail{}, ProcessingTimeMs: 5, - EvaluatedAt: time.Now().UTC(), + EvaluatedAt: now, }, IsDuplicate: true, }, nil) diff --git a/pkg/hash/hash_test.go b/pkg/hash/hash_test.go index 51e36ed..3b53ebb 100644 --- a/pkg/hash/hash_test.go +++ b/pkg/hash/hash_test.go @@ -22,7 +22,6 @@ import ( // TestHashUUIDToInt32_Deterministic verifies that the same UUID always produces // the same int32 hash value. This is critical for advisory locks - concurrent // requests with the same request_id MUST acquire the same lock. -// func TestHashUUIDToInt32_Deterministic(t *testing.T) { t.Parallel() @@ -76,7 +75,6 @@ func TestHashUUIDToInt32_Deterministic(t *testing.T) { // TestHashUUIDToInt32_DifferentInputs verifies that different UUIDs produce // different hash values (with high probability). This ensures that advisory // locks for different request_ids don't collide. -// func TestHashUUIDToInt32_DifferentInputs(t *testing.T) { t.Parallel() @@ -115,7 +113,6 @@ func TestHashUUIDToInt32_DifferentInputs(t *testing.T) { // TestHashUUIDToInt32_BoundaryValues tests edge cases and boundary values // to ensure the hash function handles them correctly. -// func TestHashUUIDToInt32_BoundaryValues(t *testing.T) { t.Parallel() @@ -159,7 +156,6 @@ func TestHashUUIDToInt32_BoundaryValues(t *testing.T) { // a reasonable distribution of values across the int32 range. // This is important for advisory lock performance - we don't want all locks // to cluster in a small range. -// func TestHashUUIDToInt32_Distribution(t *testing.T) { t.Parallel() From 595a4dda1e5649048e36bfdf47a7c4d238ddaa54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Fri, 20 Mar 2026 21:45:51 -0300 Subject: [PATCH 5/9] test: update integration tests to expect HTTP 201 for new validations All POST /v1/validations now return 201 Created (new) or 200 OK (duplicate). Update 16 integration test files: - Change StatusOK to StatusCreated for POST validation assertions - Preserve StatusOK for GET/PUT/PATCH endpoints (limits, rules, etc.) - Update tests 1_1_52 and 1_1_53 for idempotent behavior: duplicate requestId returns cached response (200) instead of re-processing - Use uuid.New() in auth and limit tests to avoid request_id collisions between tests sharing the same database --- .../01_validation_1140_1154_test.go | 76 ++++---- .../01_validation_post_error_codes_test.go | 2 +- tests/integration/01_validation_test.go | 176 +++++++++--------- tests/integration/04_cel_evaluation_test.go | 26 +-- .../04_complete_evaluation_test.go | 26 +-- tests/integration/04_context_objects_test.go | 20 +- .../04_decision_precedence_test.go | 10 +- .../04_metadata_validation_test.go | 10 +- tests/integration/04_scope_matching_test.go | 30 +-- .../05_limits_verification_test.go | 79 ++++---- tests/integration/06_authentication_test.go | 19 +- tests/integration/07_audit_events_test.go | 20 +- .../integration/08_limits_time_window_test.go | 60 +++--- .../09_mock_time_validation_test.go | 4 +- .../10_usage_counter_expires_at_test.go | 12 +- .../11_backward_compatibility_test.go | 16 +- 16 files changed, 294 insertions(+), 292 deletions(-) diff --git a/tests/integration/01_validation_1140_1154_test.go b/tests/integration/01_validation_1140_1154_test.go index 1e5acee..551277e 100644 --- a/tests/integration/01_validation_1140_1154_test.go +++ b/tests/integration/01_validation_1140_1154_test.go @@ -51,7 +51,7 @@ func TestValidation_1_1_40_MonthlyLimitType(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation should succeed: %s", string(body1)) var firstResult testutil.ValidationResponse err := json.Unmarshal(body1, &firstResult) @@ -73,7 +73,7 @@ func TestValidation_1_1_40_MonthlyLimitType(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, secondReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err = json.Unmarshal(body2, &result) @@ -124,7 +124,7 @@ func TestValidation_1_1_41_PerTransactionLimitType(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -163,7 +163,7 @@ func TestValidation_1_1_41_PerTransactionLimitType(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -202,7 +202,7 @@ func TestValidation_1_1_41_PerTransactionLimitType(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -261,7 +261,7 @@ func TestValidation_1_1_42_ScopeMatchingRulesSegment(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -290,7 +290,7 @@ func TestValidation_1_1_42_ScopeMatchingRulesSegment(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -336,7 +336,7 @@ func TestValidation_1_1_43_ScopeMatchingRulesPortfolio(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -365,7 +365,7 @@ func TestValidation_1_1_43_ScopeMatchingRulesPortfolio(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -407,7 +407,7 @@ func TestValidation_1_1_44_ScopeMatchingRulesTransactionType(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -433,7 +433,7 @@ func TestValidation_1_1_44_ScopeMatchingRulesTransactionType(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -476,7 +476,7 @@ func TestValidation_1_1_45_DenyRulePrecedenceOverLimitExceeded(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation should succeed: %s", string(body1)) var firstResult testutil.ValidationResponse err := json.Unmarshal(body1, &firstResult) @@ -498,7 +498,7 @@ func TestValidation_1_1_45_DenyRulePrecedenceOverLimitExceeded(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, secondReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err = json.Unmarshal(body2, &result) @@ -542,7 +542,7 @@ func TestValidation_1_1_46_LimitUsageUpdatedOnlyOnAllow(t *testing.T) { pixResp, pixBody := testutil.CreateValidation(t, pixReq) defer pixResp.Body.Close() - require.Equal(t, http.StatusOK, pixResp.StatusCode, "PIX validation should succeed: %s", string(pixBody)) + require.Equal(t, http.StatusCreated, pixResp.StatusCode, "PIX validation should succeed: %s", string(pixBody)) var pixResult testutil.ValidationResponse err := json.Unmarshal(pixBody, &pixResult) @@ -567,7 +567,7 @@ func TestValidation_1_1_46_LimitUsageUpdatedOnlyOnAllow(t *testing.T) { cardResp, cardBody := testutil.CreateValidation(t, cardReq) defer cardResp.Body.Close() - require.Equal(t, http.StatusOK, cardResp.StatusCode, "Expected 200 OK, got: %s", string(cardBody)) + require.Equal(t, http.StatusCreated, cardResp.StatusCode, "Expected 201 Created, got: %s", string(cardBody)) var cardResult testutil.ValidationResponse err = json.Unmarshal(cardBody, &cardResult) @@ -590,7 +590,7 @@ func TestValidation_1_1_46_LimitUsageUpdatedOnlyOnAllow(t *testing.T) { checkResp, checkBody := testutil.CreateValidation(t, checkReq) defer checkResp.Body.Close() - require.Equal(t, http.StatusOK, checkResp.StatusCode, "Check validation should succeed: %s", string(checkBody)) + require.Equal(t, http.StatusCreated, checkResp.StatusCode, "Check validation should succeed: %s", string(checkBody)) var checkResult testutil.ValidationResponse err = json.Unmarshal(checkBody, &checkResult) @@ -701,7 +701,7 @@ func TestValidation_1_1_47b_AcceptsMaxSafeCELAmount(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Amount at CEL precision boundary (2^53) should pass the precision guard: %s", string(body)) // Verify CEL actually evaluated the expression (decision DENY proves the amount @@ -734,7 +734,7 @@ func TestValidation_1_1_48_EmptyMetadataObject(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Empty metadata should be accepted: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Empty metadata should be accepted: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -776,7 +776,7 @@ func TestValidation_1_1_49_NullOptionalFields(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Null optional fields should be accepted: %s", string(respBody)) var result testutil.ValidationResponse @@ -844,7 +844,7 @@ func TestValidation_1_1_51_LowercaseCurrencyRejected(t *testing.T) { assert.Equal(t, "currency must be valid ISO 4217 code (e.g., BRL, USD)", errResp.Message, "Error message should indicate valid currency format") } -// Test 1.1.52: Validation returns unique validationId for each request +// Test 1.1.52: Duplicate requestId returns cached response (idempotent behavior) func TestValidation_1_1_52_UniqueValidationIdPerRequest(t *testing.T) { accountID := testutil.MustDeterministicUUID(1120).String() requestID := testutil.MustDeterministicUUID(1121).String() // Same requestId for both @@ -860,19 +860,19 @@ func TestValidation_1_1_52_UniqueValidationIdPerRequest(t *testing.T) { }, } - // First request + // First request — new validation resp1, body1 := testutil.CreateValidation(t, req) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First request should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First request should return 201 Created: %s", string(body1)) var result1 testutil.ValidationResponse err := json.Unmarshal(body1, &result1) require.NoError(t, err) - // Second request with same requestId + // Second request with same requestId — duplicate, returns cached response resp2, body2 := testutil.CreateValidation(t, req) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Second request should succeed: %s", string(body2)) + require.Equal(t, http.StatusOK, resp2.StatusCode, "Duplicate request should return 200 OK: %s", string(body2)) var result2 testutil.ValidationResponse err = json.Unmarshal(body2, &result2) @@ -882,9 +882,9 @@ func TestValidation_1_1_52_UniqueValidationIdPerRequest(t *testing.T) { assert.Equal(t, requestID, result1.RequestID, "First response should echo requestId") assert.Equal(t, requestID, result2.RequestID, "Second response should echo requestId") - // Verify validationIds are different - assert.NotEqual(t, result1.ValidationID, result2.ValidationID, - "Each response should have a unique validationId") + // With idempotency, duplicate returns the same validationId (cached response) + assert.Equal(t, result1.ValidationID, result2.ValidationID, + "Duplicate request should return the same validationId (cached)") // Verify validationId is different from requestId assert.NotEqual(t, result1.ValidationID, requestID, @@ -893,8 +893,8 @@ func TestValidation_1_1_52_UniqueValidationIdPerRequest(t *testing.T) { "validationId should be different from requestId") } -// Test 1.1.53: Validation non-idempotent behavior -func TestValidation_1_1_53_NonIdempotentBehavior(t *testing.T) { +// Test 1.1.53: Idempotent behavior — duplicate requestId returns cached result +func TestValidation_1_1_53_IdempotentBehavior(t *testing.T) { accountID := testutil.MustDeterministicUUID(1130).String() // Create and activate a DAILY limit @@ -920,7 +920,7 @@ func TestValidation_1_1_53_NonIdempotentBehavior(t *testing.T) { // First request - should ALLOW and consume limit resp1, body1 := testutil.CreateValidation(t, req) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First request should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First request should return 201 Created: %s", string(body1)) var result1 testutil.ValidationResponse err := json.Unmarshal(body1, &result1) @@ -928,21 +928,21 @@ func TestValidation_1_1_53_NonIdempotentBehavior(t *testing.T) { assert.Equal(t, "ALLOW", result1.Decision, "First request should be ALLOW") - // Second request with same payload and requestId - should DENY (limit exceeded: 600 + 600 > 1000) + // Second request with same requestId — duplicate, returns cached ALLOW (no double-count) resp2, body2 := testutil.CreateValidation(t, req) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Second request should succeed: %s", string(body2)) + require.Equal(t, http.StatusOK, resp2.StatusCode, "Duplicate request should return 200 OK: %s", string(body2)) var result2 testutil.ValidationResponse err = json.Unmarshal(body2, &result2) require.NoError(t, err) - assert.Equal(t, "DENY", result2.Decision, - "Second request should be DENY (non-idempotent: limit now exceeded)") + assert.Equal(t, "ALLOW", result2.Decision, + "Duplicate request should return cached ALLOW (idempotent, no double-count)") - // Verify different validationIds - assert.NotEqual(t, result1.ValidationID, result2.ValidationID, - "Each request should have a unique validationId") + // With idempotency, duplicate returns the same validationId + assert.Equal(t, result1.ValidationID, result2.ValidationID, + "Duplicate request should return the same validationId (cached)") } // Test 1.1.54: Validation with all rule actions matching @@ -984,7 +984,7 @@ func TestValidation_1_1_54_AllRuleActionsMatching(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) diff --git a/tests/integration/01_validation_post_error_codes_test.go b/tests/integration/01_validation_post_error_codes_test.go index a4fa8cd..194bc27 100644 --- a/tests/integration/01_validation_post_error_codes_test.go +++ b/tests/integration/01_validation_post_error_codes_test.go @@ -673,7 +673,7 @@ func TestValidation_FutureTimestamp_SmallClockSkew_IsAccepted(t *testing.T) { require.NoError(t, err) // Small clock skew (2 seconds) is tolerated - API accepts the request - assert.Equal(t, http.StatusOK, resp.StatusCode, + assert.Equal(t, http.StatusCreated, resp.StatusCode, "Small clock skew (2 seconds) should be tolerated. Response: %s", string(respBody)) // Verify we got a valid validation response diff --git a/tests/integration/01_validation_test.go b/tests/integration/01_validation_test.go index 95092d4..4dbea94 100644 --- a/tests/integration/01_validation_test.go +++ b/tests/integration/01_validation_test.go @@ -63,7 +63,7 @@ func TestValidation_CompletePayload(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -105,7 +105,7 @@ func TestValidation_ReturnsAllowWithoutDenyRules(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -144,7 +144,7 @@ func TestValidation_ReturnsDenyWhenRuleMatches(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -183,7 +183,7 @@ func TestValidation_ReturnsReviewWhenRuleMatches(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -218,7 +218,7 @@ func TestValidation_ReturnsDenyWhenLimitExceeded(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation should succeed: %s", string(body1)) // Now try to exceed the limit with amount 200 (900 + 200 > 1000) secondReq := &testutil.ValidationRequest{ @@ -235,7 +235,7 @@ func TestValidation_ReturnsDenyWhenLimitExceeded(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, secondReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err := json.Unmarshal(body2, &result) @@ -318,7 +318,7 @@ func TestValidation_DecisionPrecedence(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -359,7 +359,7 @@ func TestValidation_DecisionPrecedence(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -394,7 +394,7 @@ func TestValidation_DecisionPrecedence(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -430,7 +430,7 @@ func TestValidation_DecisionPrecedence(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -467,7 +467,7 @@ func TestValidation_DecisionPrecedence(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -505,7 +505,7 @@ func TestValidation_DefaultDecisionWithoutRules(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -658,7 +658,7 @@ func TestValidation_1_1_8_MultipleMatchingRules(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -928,8 +928,8 @@ func TestValidation_InvalidAmount(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode, - "Amount 0.01 (minimum valid) should return 200 OK: %s", string(body)) + assert.Equal(t, http.StatusCreated, resp.StatusCode, + "Amount 0.01 (minimum valid) should return 201 Created: %s", string(body)) }) } @@ -1073,7 +1073,7 @@ func TestValidation_OptionalMerchant(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode, "Request without merchant should return 200 OK: %s", string(body)) + assert.Equal(t, http.StatusCreated, resp.StatusCode, "Request without merchant should return 201 Created: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -1110,8 +1110,8 @@ func TestValidation_1_1_17_ScopesArray(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, - "Validation with multiple scopes should return 200 OK: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, + "Validation with multiple scopes should return 201 Created: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -1151,8 +1151,8 @@ func TestValidation_1_1_18_CustomMetadata(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, - "Validation with 50 metadata entries should return 200 OK: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, + "Validation with 50 metadata entries should return 201 Created: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -1712,8 +1712,8 @@ func TestValidation_1_1_29_AcceptsDecimalAmount(t *testing.T) { resp, body := testutil.CreateValidationRaw(t, []byte(jsonPayload)) defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode, - "Decimal amount should return 200 OK (decimal.Decimal accepts fractional values), body: %s", string(body)) + assert.Equal(t, http.StatusCreated, resp.StatusCode, + "Decimal amount should return 201 Created (decimal.Decimal accepts fractional values), body: %s", string(body)) } // Test 1.1.30: Validation rejects timestamp without timezone @@ -1739,12 +1739,12 @@ func TestValidation_1_1_30_RejectsTimestampWithoutTimezone(t *testing.T) { { name: "valid UTC timezone", timestamp: timestamp, - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid local timezone with offset", timestamp: testutil.FixedTime().In(time.FixedZone("BRT", -3*3600)).Format(time.RFC3339), - expected: http.StatusOK, + expected: http.StatusCreated, }, } @@ -1840,17 +1840,17 @@ func TestValidation_1_1_32_RejectsInvalidAccountType(t *testing.T) { { name: "valid checking", accountType: "checking", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid savings", accountType: "savings", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid credit", accountType: "credit", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "invalid INVALID", @@ -1865,7 +1865,7 @@ func TestValidation_1_1_32_RejectsInvalidAccountType(t *testing.T) { { name: "omitted (empty string with omitempty)", accountType: "", - expected: http.StatusOK, // Empty string with omitempty = field not sent = optional field OK + expected: http.StatusCreated, // Empty string with omitempty = field not sent = optional field OK }, } @@ -1916,17 +1916,17 @@ func TestValidation_1_1_33_RejectsInvalidAccountStatus(t *testing.T) { { name: "valid active", accountStatus: "active", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid suspended", accountStatus: "suspended", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid closed", accountStatus: "closed", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "invalid INVALID", @@ -1941,7 +1941,7 @@ func TestValidation_1_1_33_RejectsInvalidAccountStatus(t *testing.T) { { name: "omitted (empty string with omitempty)", accountStatus: "", - expected: http.StatusOK, // Empty string with omitempty = field not sent = optional field OK + expected: http.StatusCreated, // Empty string with omitempty = field not sent = optional field OK }, } @@ -1993,12 +1993,12 @@ func TestValidation_1_1_34_RejectsInvalidMerchantCategory(t *testing.T) { { name: "valid MCC 5411", category: "5411", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid MCC 5812", category: "5812", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "invalid non-numeric", @@ -2018,7 +2018,7 @@ func TestValidation_1_1_34_RejectsInvalidMerchantCategory(t *testing.T) { { name: "omitted (empty string with omitempty)", category: "", - expected: http.StatusOK, // Empty string with omitempty = field not sent = optional field OK + expected: http.StatusCreated, // Empty string with omitempty = field not sent = optional field OK }, } @@ -2073,12 +2073,12 @@ func TestValidation_1_1_35_RejectsInvalidMerchantCountry(t *testing.T) { { name: "valid BR", country: "BR", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid US", country: "US", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "invalid alpha-3", @@ -2108,7 +2108,7 @@ func TestValidation_1_1_35_RejectsInvalidMerchantCountry(t *testing.T) { { name: "omitted (empty string with omitempty)", country: "", - expected: http.StatusOK, // Empty string with omitempty = field not sent = optional field OK + expected: http.StatusCreated, // Empty string with omitempty = field not sent = optional field OK }, } @@ -2162,7 +2162,7 @@ func TestValidation_1_1_36_TimestampClockSkewBoundary(t *testing.T) { { name: "+55 seconds safely within tolerance", offset: 55 * time.Second, - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "+65 seconds safely beyond tolerance", @@ -2214,22 +2214,22 @@ func TestValidation_1_1_37_ValidTransactionTypes(t *testing.T) { { name: "valid CARD", transactionType: "CARD", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid WIRE", transactionType: "WIRE", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid PIX", transactionType: "PIX", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid CRYPTO", transactionType: "CRYPTO", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "invalid lowercase card", @@ -2283,22 +2283,22 @@ func TestValidation_1_1_38_SubTypeField(t *testing.T) { { name: "valid credit", subType: "credit", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid debit", subType: "debit", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid domestic", subType: "domestic", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "valid international", subType: "international", - expected: http.StatusOK, + expected: http.StatusCreated, }, { name: "invalid too long", @@ -2395,7 +2395,7 @@ func TestValidation_1_1_55_DeactivatedRuleNotEvaluated(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, req1) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "Expected 200 OK, got: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "Expected 201 Created, got: %s", string(body1)) var result1 testutil.ValidationResponse err := json.Unmarshal(body1, &result1) @@ -2427,7 +2427,7 @@ func TestValidation_1_1_55_DeactivatedRuleNotEvaluated(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, req2) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result2 testutil.ValidationResponse err = json.Unmarshal(body2, &result2) @@ -2466,7 +2466,7 @@ func TestValidation_1_2_1_RetrievesValidationByID(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -2556,7 +2556,7 @@ func TestValidation_1_2_4_RequiresAuthentication(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -2604,7 +2604,7 @@ func TestValidation_1_2_5_CompleteSnapshotPreserved(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -2677,7 +2677,7 @@ func TestValidation_1_2_6_CompleteResponseSnapshot(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -2750,7 +2750,7 @@ func TestValidation_1_2_7_CompleteDataWithSegmentPortfolio(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -2826,7 +2826,7 @@ func TestValidation_1_2_8_CompleteLimitUsagePreserved(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -2902,7 +2902,7 @@ func TestValidation_1_2_9_CreatedAtIsISO8601(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -2966,7 +2966,7 @@ func TestValidation_1_3_1_ListsValidationsWithoutFilters(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) // Now list validations without any filters listResp, listBody := testutil.ListValidations(t, "") @@ -3017,7 +3017,7 @@ func TestValidation_1_3_2_FiltersByDateRange(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) // Query with date range covering now startDate := now.Add(-1 * time.Hour).Format(time.RFC3339) @@ -3073,7 +3073,7 @@ func TestValidation_1_3_3_FiltersByDecision(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -3117,7 +3117,7 @@ func TestValidation_1_3_4_FiltersByAccountID(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) // Query with accountId filter listResp, listBody := testutil.ListValidations(t, fmt.Sprintf("accountId=%s", accountID)) @@ -3164,7 +3164,7 @@ func TestValidation_1_3_5_FiltersBySegmentID(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) // Query with segmentId filter listResp, listBody := testutil.ListValidations(t, fmt.Sprintf("segmentId=%s", segmentID)) @@ -3211,7 +3211,7 @@ func TestValidation_1_3_6_FiltersByPortfolioID(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) // Query with portfolioId filter listResp, listBody := testutil.ListValidations(t, fmt.Sprintf("portfolioId=%s", portfolioID)) @@ -3254,7 +3254,7 @@ func TestValidation_1_3_7_FiltersByTransactionType(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) // Query with transactionType=CARD filter listResp, listBody := testutil.ListValidations(t, "transactionType=CARD") @@ -3305,7 +3305,7 @@ func TestValidation_1_3_8_FiltersByRuleID(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -3361,7 +3361,7 @@ func TestValidation_1_3_9_FiltersByExceededLimitId(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -3423,7 +3423,7 @@ func TestValidation_1_3_10_PaginationWorks(t *testing.T) { resp, body := testutil.CreateValidation(t, req) resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) } // Get first page with limit=10 @@ -3488,7 +3488,7 @@ func TestValidation_1_3_11_SortingWorks(t *testing.T) { } resp, body := testutil.CreateValidation(t, req) - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) resp.Body.Close() } @@ -3542,7 +3542,7 @@ func TestValidation_1_3_12_CombinedFilters(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -3815,7 +3815,7 @@ func TestValidation_1_3_13_SortingByProcessingTimeAsc(t *testing.T) { } resp, body := testutil.CreateValidation(t, req) - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) resp.Body.Close() } @@ -3869,7 +3869,7 @@ func TestValidation_1_3_27_CombinedFiltersWithNewParams(t *testing.T) { } resp1, body1 := testutil.CreateValidation(t, req1) - require.Equal(t, http.StatusOK, resp1.StatusCode, "Expected 200 OK from POST, got: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "Expected 201 Created from POST, got: %s", string(body1)) resp1.Body.Close() var createResult1 testutil.ValidationResponse @@ -3895,7 +3895,7 @@ func TestValidation_1_3_27_CombinedFiltersWithNewParams(t *testing.T) { } resp2, body2 := testutil.CreateValidation(t, req2) - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK from POST, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created from POST, got: %s", string(body2)) resp2.Body.Close() // Query with combined filters: decision + transactionType + startDate + endDate + accountId + limit @@ -3968,7 +3968,7 @@ func TestValidation_1_2_10_ValidationIdEchoedCorrectly(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4017,7 +4017,7 @@ func TestValidation_1_2_11_ProcessingTimeMsNonNegative(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4077,7 +4077,7 @@ func TestValidation_1_2_12_ReturnsCorrectDecisionEnumValues(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4126,7 +4126,7 @@ func TestValidation_1_2_12_ReturnsCorrectDecisionEnumValues(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4174,7 +4174,7 @@ func TestValidation_1_2_12_ReturnsCorrectDecisionEnumValues(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4223,7 +4223,7 @@ func TestValidation_1_3_14_CombinedFiltersAdvanced(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4343,7 +4343,7 @@ func TestValidation_1_3_32_DefaultPaginationLimit(t *testing.T) { } resp, body := testutil.CreateValidation(t, req) - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) resp.Body.Close() } @@ -4384,7 +4384,7 @@ func TestValidation_1_3_33_SortingByCreatedAtAsc(t *testing.T) { } resp, body := testutil.CreateValidation(t, req) - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) resp.Body.Close() time.Sleep(10 * time.Millisecond) // Small delay to ensure different timestamps } @@ -4434,7 +4434,7 @@ func TestValidation_1_3_34_MultipleParametersSimultaneously(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4543,7 +4543,7 @@ func TestValidation_1_3_40_CursorPaginationConsistency(t *testing.T) { } resp, body := testutil.CreateValidation(t, req) - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) var createResult testutil.ValidationResponse err := json.Unmarshal(body, &createResult) @@ -4623,7 +4623,7 @@ func TestValidation_1_3_41_FilterByMatchedRuleId(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, reqMatch) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode) + require.Equal(t, http.StatusCreated, resp1.StatusCode) var matchResult testutil.ValidationResponse err := json.Unmarshal(body1, &matchResult) @@ -4644,7 +4644,7 @@ func TestValidation_1_3_41_FilterByMatchedRuleId(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, reqNoMatch) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode) + require.Equal(t, http.StatusCreated, resp2.StatusCode) var noMatchResult testutil.ValidationResponse err = json.Unmarshal(body2, &noMatchResult) @@ -4702,7 +4702,7 @@ func TestValidation_1_3_42_AllSortBySortOrderCombinations(t *testing.T) { } resp, body := testutil.CreateValidation(t, req) - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) resp.Body.Close() time.Sleep(10 * time.Millisecond) } @@ -4781,7 +4781,7 @@ func TestValidation_1_3_43_DateRangeBoundarySemantics(t *testing.T) { resp, _ := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) // Test startDate is inclusive t.Run("startDate is inclusive", func(t *testing.T) { @@ -4867,7 +4867,7 @@ func TestValidation_1_3_45_FilterByOnlyStartDate(t *testing.T) { resp, _ := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) // Query with only startDate startDate := now.Add(-1 * time.Hour).Format(time.RFC3339) @@ -4918,7 +4918,7 @@ func TestValidation_1_3_46_FilterByOnlyEndDate(t *testing.T) { resp, _ := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) // Query with only endDate (in the future to include recent validations) endDate := now.Add(1 * time.Hour).Format(time.RFC3339) @@ -4969,7 +4969,7 @@ func TestValidation_1_3_29_ValidationSummaryFields(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK from POST, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created from POST, got: %s", string(body)) // List validations with limit=1 to get at least one item listResp, listBody := testutil.ListValidations(t, fmt.Sprintf("accountId=%s&limit=1", accountID)) diff --git a/tests/integration/04_cel_evaluation_test.go b/tests/integration/04_cel_evaluation_test.go index 72c9e8b..3a48ae4 100644 --- a/tests/integration/04_cel_evaluation_test.go +++ b/tests/integration/04_cel_evaluation_test.go @@ -67,7 +67,7 @@ func TestValidation_CEL_TransactionType(t *testing.T) { result, status := testutil.ExecuteValidationRequest(t, payload) // VALIDATIONS - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Validate decision assert.Equal(t, "ALLOW", result["decision"]) @@ -104,7 +104,7 @@ func TestValidation_CEL_SubType_Match(t *testing.T) { payload["subType"] = "credit" result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS assert.Equal(t, "ALLOW", result["decision"]) @@ -129,7 +129,7 @@ func TestValidation_CEL_SubType_NoMatch(t *testing.T) { payload["subType"] = "debit" result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule evaluated but NOT matched testutil.AssertRuleEvaluatedButNotMatched(t, result, ruleID) @@ -164,7 +164,7 @@ func TestValidation_CEL_Amount_Comparison(t *testing.T) { payload["amount"] = tc.amount result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Rule always evaluated evaluatedRuleIDs, ok := result["evaluatedRuleIds"].([]any) @@ -199,7 +199,7 @@ func TestValidation_CEL_Currency(t *testing.T) { payload["currency"] = "BRL" result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS assert.Equal(t, "ALLOW", result["decision"]) @@ -231,7 +231,7 @@ func TestValidation_CEL_SegmentContext_NameField(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS assert.Equal(t, "ALLOW", result["decision"]) @@ -259,7 +259,7 @@ func TestValidation_CEL_SegmentContext_BracketNotation(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS assert.Equal(t, "ALLOW", result["decision"]) @@ -287,7 +287,7 @@ func TestValidation_CEL_MerchantContext_Optional(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule matches when merchant present assert.Equal(t, "REVIEW", result["decision"]) @@ -298,7 +298,7 @@ func TestValidation_CEL_MerchantContext_Optional(t *testing.T) { // No merchant field result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule evaluated but NOT matched (size(merchant) == 0) testutil.AssertRuleEvaluatedButNotMatched(t, result2, ruleID) @@ -325,7 +325,7 @@ func TestValidation_CEL_AccountContext(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS assert.Equal(t, "ALLOW", result["decision"]) @@ -352,7 +352,7 @@ func TestValidation_CEL_Metadata_BracketNotation(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS assert.Equal(t, "DENY", result["decision"]) @@ -390,7 +390,7 @@ func TestValidation_CEL_ComplexCombinedExpression(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS assert.Equal(t, "ALLOW", result["decision"]) @@ -412,7 +412,7 @@ func TestValidation_CEL_ComplexCombinedExpression(t *testing.T) { } result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule evaluated but NOT matched (only amount condition fails) testutil.AssertRuleEvaluatedButNotMatched(t, result2, ruleID) diff --git a/tests/integration/04_complete_evaluation_test.go b/tests/integration/04_complete_evaluation_test.go index 70f1137..a2f458d 100644 --- a/tests/integration/04_complete_evaluation_test.go +++ b/tests/integration/04_complete_evaluation_test.go @@ -85,7 +85,7 @@ func TestValidation_CompleteEvaluation_AllActiveRules(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: All test rules evaluated and matched (no short-circuit) evaluatedRuleIDs, ok := result["evaluatedRuleIds"].([]any) @@ -147,7 +147,7 @@ func TestValidation_CompleteEvaluation_CollectsMatchingWithDenyPrecedence(t *tes } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Verify matching rules (at least the test rules) matchedRuleIDs, ok := result["matchedRuleIds"].([]any) @@ -226,7 +226,7 @@ func TestValidation_CompleteEvaluation_CollectsEvaluatedRules(t *testing.T) { payload["amount"] = "100.00" result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Scoped rules evaluated, others filtered evaluatedRuleIDs, ok := result["evaluatedRuleIds"].([]any) @@ -266,7 +266,7 @@ func TestValidation_CompleteEvaluation_DraftRulesNotEvaluated(t *testing.T) { payload["currency"] = "BRL" // Would match DRAFT rule if it were active result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Only ACTIVE rules evaluated evaluatedRuleIDs, ok := result["evaluatedRuleIds"].([]any) @@ -307,7 +307,7 @@ func TestValidation_CompleteEvaluation_InactiveRulesNotEvaluated(t *testing.T) { payload["currency"] = "BRL" // Would match INACTIVE rule if it were active result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Only ACTIVE rules evaluated evaluatedRuleIDs, ok := result["evaluatedRuleIds"].([]any) @@ -347,7 +347,7 @@ func TestValidation_CompleteEvaluation_DeletedRulesNotEvaluated(t *testing.T) { payload["currency"] = "BRL" // Would match DELETED rule if it existed result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Only ACTIVE rules evaluated evaluatedRuleIDs, ok := result["evaluatedRuleIds"].([]any) @@ -373,7 +373,7 @@ func TestValidation_ResponseStructure_ValidationIdIsServerGenerated(t *testing.T payload1["requestId"] = requestID result1, status1 := testutil.ExecuteValidationRequest(t, payload1) - require.Equal(t, http.StatusOK, status1) + require.Equal(t, http.StatusCreated, status1) // VALIDATIONS for first call validationID1, ok := result1["validationId"].(string) @@ -392,7 +392,7 @@ func TestValidation_ResponseStructure_ValidationIdIsServerGenerated(t *testing.T payload2["requestId"] = testutil.MustDeterministicUUID(4006).String() result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) validationID2, ok := result2["validationId"].(string) require.True(t, ok, "validationId must be string") @@ -413,7 +413,7 @@ func TestValidation_ResponseStructure_RequestIdEchoed(t *testing.T) { payload["requestId"] = requestID result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: requestId echoed exactly responseRequestID, ok := result["requestId"].(string) @@ -430,7 +430,7 @@ func TestValidation_ResponseStructure_ProcessingTimeMsAlwaysPresent(t *testing.T payload := testutil.CreateBasicValidationPayload() result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS processingTimeMs, ok := result["processingTimeMs"].(float64) @@ -491,7 +491,7 @@ func TestValidation_ResponseStructure_ReasonField_AllDecisions(t *testing.T) { payload["amount"] = tc.amount result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Validate decision assert.Equal(t, tc.expectedDecision, result["decision"]) @@ -533,7 +533,7 @@ func TestValidation_ResponseStructure_LimitUsageDetails_EmptyArray(t *testing.T) } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS limitUsageDetails, ok := result["limitUsageDetails"].([]any) @@ -566,7 +566,7 @@ func TestValidation_ResponseStructure_LimitUsageDetails_Populated(t *testing.T) payload["amount"] = "500.00" result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS limitUsageDetails, ok := result["limitUsageDetails"].([]any) diff --git a/tests/integration/04_context_objects_test.go b/tests/integration/04_context_objects_test.go index 2794ff9..e8ce3a7 100644 --- a/tests/integration/04_context_objects_test.go +++ b/tests/integration/04_context_objects_test.go @@ -94,7 +94,7 @@ func TestValidation_AccountContext_AllFields(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(respBody)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(respBody)) var result map[string]any err = json.Unmarshal(respBody, &result) @@ -155,7 +155,7 @@ func TestValidation_AccountContext_MinimalFields(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Request should be accepted with minimal AccountContext (only accountId). Response: %s", string(respBody)) var result map[string]any @@ -260,7 +260,7 @@ func TestValidation_SegmentContext_Structure(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(respBody)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(respBody)) var result map[string]any err = json.Unmarshal(respBody, &result) @@ -326,7 +326,7 @@ func TestValidation_SegmentContext_CELFieldAccess(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(respBody)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(respBody)) var result map[string]any err = json.Unmarshal(respBody, &result) @@ -390,7 +390,7 @@ func TestValidation_PortfolioContext_Structure(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(respBody)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(respBody)) var result map[string]any err = json.Unmarshal(respBody, &result) @@ -456,7 +456,7 @@ func TestValidation_PortfolioContext_CELFieldAccess(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(respBody)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(respBody)) var result map[string]any err = json.Unmarshal(respBody, &result) @@ -522,7 +522,7 @@ func TestValidation_MerchantContext_CompleteStructure(t *testing.T) { respBody, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(respBody)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(respBody)) var result map[string]any err = json.Unmarshal(respBody, &result) @@ -604,7 +604,7 @@ func TestValidation_SegmentContext_Minimal(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status, "Segment with only segmentId should be accepted (name is optional)") + require.Equal(t, http.StatusCreated, status, "Segment with only segmentId should be accepted (name is optional)") // VALIDATIONS: Rule should match assert.Equal(t, "ALLOW", result["decision"]) @@ -635,7 +635,7 @@ func TestValidation_PortfolioContext_Minimal(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status, "Portfolio with only portfolioId should be accepted (name is optional)") + require.Equal(t, http.StatusCreated, status, "Portfolio with only portfolioId should be accepted (name is optional)") // VALIDATIONS: Rule should match assert.Equal(t, "ALLOW", result["decision"]) @@ -666,7 +666,7 @@ func TestValidation_MerchantContext_Minimal(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status, + require.Equal(t, http.StatusCreated, status, "Merchant with only merchantId should be accepted (other fields optional)") // VALIDATIONS: Rule should match diff --git a/tests/integration/04_decision_precedence_test.go b/tests/integration/04_decision_precedence_test.go index 077030d..0964769 100644 --- a/tests/integration/04_decision_precedence_test.go +++ b/tests/integration/04_decision_precedence_test.go @@ -78,7 +78,7 @@ func TestValidation_DenyPrecedence_OverAllow(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Validate decision is DENY (takes precedence over ALLOW) decision, ok := result["decision"].(string) @@ -169,7 +169,7 @@ func TestValidation_DenyPrecedence_OverReview(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Validate decision is DENY (takes precedence over REVIEW) decision, ok := result["decision"].(string) @@ -259,7 +259,7 @@ func TestValidation_ReviewDecision_WhenNoDeny(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Validate decision is REVIEW (no DENY, but REVIEW rule matched) decision, ok := result["decision"].(string) @@ -342,7 +342,7 @@ func TestValidation_DefaultDecision_AllowMode(t *testing.T) { payload["amount"] = 5000 // Less than 1000000, won't match rule result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Validate decision is ALLOW (default when no rules match) decision, ok := result["decision"].(string) @@ -408,7 +408,7 @@ func TestValidation_DefaultDecision_DenyMode(t *testing.T) { payload["amount"] = 5000 // Less than 1000000, won't match rule result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // Validate decision is DENY (configurable default in fail-closed mode) decision, ok := result["decision"].(string) diff --git a/tests/integration/04_metadata_validation_test.go b/tests/integration/04_metadata_validation_test.go index 43c2248..21300c7 100644 --- a/tests/integration/04_metadata_validation_test.go +++ b/tests/integration/04_metadata_validation_test.go @@ -88,7 +88,7 @@ func TestValidation_Metadata_MaxEntries_BoundaryValid(t *testing.T) { require.NoError(t, err) // Should be accepted at boundary (50 entries) - assert.Equal(t, http.StatusOK, resp.StatusCode, + assert.Equal(t, http.StatusCreated, resp.StatusCode, "Request with exactly 50 metadata entries should be accepted. Response: %s", string(respBody)) var result map[string]any @@ -327,7 +327,7 @@ func TestValidation_Metadata_KeyAtMaxLength_BoundaryValid(t *testing.T) { require.NoError(t, err) // Should be accepted at boundary (64 characters) - assert.Equal(t, http.StatusOK, resp.StatusCode, + assert.Equal(t, http.StatusCreated, resp.StatusCode, "Metadata key with exactly 64 characters should be accepted. Response: %s", string(respBody)) var result map[string]any @@ -385,7 +385,7 @@ func TestValidation_Metadata_DifferentValueTypes(t *testing.T) { require.NoError(t, err) // VALIDATIONS: All value types should be accepted - assert.Equal(t, http.StatusOK, resp.StatusCode, + assert.Equal(t, http.StatusCreated, resp.StatusCode, "Metadata with different JSON types should be accepted. Response: %s", string(respBody)) var result map[string]any @@ -415,7 +415,7 @@ func TestValidation_Metadata_CELExpressionAccess(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule should match (CEL successfully accessed metadata) assert.Equal(t, "ALLOW", result["decision"]) @@ -432,7 +432,7 @@ func TestValidation_Metadata_CELExpressionAccess(t *testing.T) { } result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule should NOT match (metadata values differ) matchedRuleIDs2, ok := result2["matchedRuleIds"].([]any) diff --git a/tests/integration/04_scope_matching_test.go b/tests/integration/04_scope_matching_test.go index 949b6ad..0cb33d5 100644 --- a/tests/integration/04_scope_matching_test.go +++ b/tests/integration/04_scope_matching_test.go @@ -61,7 +61,7 @@ func TestValidation_Scope_AccountId(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule evaluated and matched assert.Equal(t, "ALLOW", result["decision"]) @@ -76,7 +76,7 @@ func TestValidation_Scope_AccountId(t *testing.T) { } result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule NOT evaluated (filtered by scope) testutil.AssertRuleNotEvaluated(t, result2, ruleID) @@ -105,7 +105,7 @@ func TestValidation_Scope_SegmentId(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule evaluated and matched assert.Equal(t, "ALLOW", result["decision"]) @@ -119,7 +119,7 @@ func TestValidation_Scope_SegmentId(t *testing.T) { } result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule NOT evaluated (filtered by scope) testutil.AssertRuleNotEvaluated(t, result2, ruleID) @@ -149,7 +149,7 @@ func TestValidation_Scope_PortfolioId(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule evaluated and matched assert.Equal(t, "ALLOW", result["decision"]) @@ -163,7 +163,7 @@ func TestValidation_Scope_PortfolioId(t *testing.T) { } result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule NOT evaluated (filtered by scope) testutil.AssertRuleNotEvaluated(t, result2, ruleID) @@ -193,7 +193,7 @@ func TestValidation_Scope_MerchantId(t *testing.T) { } result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule evaluated and matched assert.Equal(t, "ALLOW", result["decision"]) @@ -207,7 +207,7 @@ func TestValidation_Scope_MerchantId(t *testing.T) { } result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule NOT evaluated (filtered by scope) testutil.AssertRuleNotEvaluated(t, result2, ruleID) @@ -233,7 +233,7 @@ func TestValidation_Scope_TransactionType(t *testing.T) { payload["transactionType"] = "CARD" result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule evaluated and matched assert.Equal(t, "ALLOW", result["decision"]) @@ -244,7 +244,7 @@ func TestValidation_Scope_TransactionType(t *testing.T) { payload2["transactionType"] = "PIX" result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule NOT evaluated (filtered by scope) testutil.AssertRuleNotEvaluated(t, result2, ruleID) @@ -271,7 +271,7 @@ func TestValidation_Scope_SubType(t *testing.T) { payload["subType"] = "credit" result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Rule evaluated and matched assert.Equal(t, "ALLOW", result["decision"]) @@ -282,7 +282,7 @@ func TestValidation_Scope_SubType(t *testing.T) { payload2["subType"] = "debit" result2, status2 := testutil.ExecuteValidationRequest(t, payload2) - require.Equal(t, http.StatusOK, status2) + require.Equal(t, http.StatusCreated, status2) // VALIDATIONS: Rule NOT evaluated (filtered by scope) testutil.AssertRuleNotEvaluated(t, result2, ruleID) @@ -349,7 +349,7 @@ func TestValidation_Scope_MultipleScopeFields(t *testing.T) { payload["transactionType"] = tc.transactionType result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) if tc.shouldBeEvaluated { testutil.AssertRuleMatched(t, result, ruleID) @@ -432,7 +432,7 @@ func TestValidation_Scope_MultipleScopes(t *testing.T) { payload["transactionType"] = tc.transactionType result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) if tc.shouldBeEvaluated { testutil.AssertRuleMatched(t, result, ruleID) @@ -489,7 +489,7 @@ func TestValidation_Scope_EmptyScopes(t *testing.T) { payload["amount"] = "5000.00" // Always > 0 to match expression result, status := testutil.ExecuteValidationRequest(t, payload) - require.Equal(t, http.StatusOK, status) + require.Equal(t, http.StatusCreated, status) // VALIDATIONS: Global rule always evaluated and matched testutil.AssertRuleMatched(t, result, ruleID) diff --git a/tests/integration/05_limits_verification_test.go b/tests/integration/05_limits_verification_test.go index 45b2dcf..9be212e 100644 --- a/tests/integration/05_limits_verification_test.go +++ b/tests/integration/05_limits_verification_test.go @@ -19,6 +19,7 @@ import ( "tracer/internal/testutil" "tracer/pkg/model" + "github.com/google/uuid" "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -88,7 +89,7 @@ func TestLimitsVerification_5_1_1_FindsApplicableLimitsByScope(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -138,7 +139,7 @@ func TestLimitsVerification_5_1_2_CalculatesProjectedUsage(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation should succeed: %s", string(body1)) // Second validation with amount = 300 // Projected usage = 400 + 300 = 700 @@ -156,7 +157,7 @@ func TestLimitsVerification_5_1_2_CalculatesProjectedUsage(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, secondReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err := json.Unmarshal(body2, &result) @@ -204,7 +205,7 @@ func TestLimitsVerification_5_1_3_ReturnsExceededWhenProjectedGreaterThanLimit(t resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation should succeed: %s", string(body1)) // Second validation with amount = 300 (800 + 300 = 1100 > 1000) secondReq := &testutil.ValidationRequest{ @@ -221,7 +222,7 @@ func TestLimitsVerification_5_1_3_ReturnsExceededWhenProjectedGreaterThanLimit(t resp2, body2 := testutil.CreateValidation(t, secondReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err := json.Unmarshal(body2, &result) @@ -271,7 +272,7 @@ func TestLimitsVerification_5_1_4_ReturnsOKWhenProjectedEqualsLimit(t *testing.T resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation should succeed: %s", string(body1)) // Second validation with amount = 300 (700 + 300 = 1000 == limit) secondReq := &testutil.ValidationRequest{ @@ -288,7 +289,7 @@ func TestLimitsVerification_5_1_4_ReturnsOKWhenProjectedEqualsLimit(t *testing.T resp2, body2 := testutil.CreateValidation(t, secondReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err := json.Unmarshal(body2, &result) @@ -335,7 +336,7 @@ func TestLimitsVerification_5_1_5_ReturnsOKWhenProjectedLessThanLimit(t *testing resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation should succeed: %s", string(body1)) // Second validation with amount = 300 (500 + 300 = 800 < 1000) secondReq := &testutil.ValidationRequest{ @@ -352,7 +353,7 @@ func TestLimitsVerification_5_1_5_ReturnsOKWhenProjectedLessThanLimit(t *testing resp2, body2 := testutil.CreateValidation(t, secondReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err := json.Unmarshal(body2, &result) @@ -407,7 +408,7 @@ func TestLimitsVerification_5_1_6_ChecksMultipleLimits(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -460,7 +461,7 @@ func TestLimitsVerification_5_1_6_ChecksMultipleLimits(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -521,7 +522,7 @@ func TestLimitsVerification_5_1_9_PerTransactionLimitChecksValueOnly(t *testing. resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -582,7 +583,7 @@ func TestLimitsVerification_5_2_1_IncrementsUsageAtomically(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -635,7 +636,7 @@ func TestLimitsVerification_5_2_2_DoesNotIncrementOnRuleBasedDeny(t *testing.T) // First, establish some usage (500) firstReq := &testutil.ValidationRequest{ - RequestID: testutil.MustDeterministicUUID(50211).String(), + RequestID: uuid.New().String(), TransactionType: "PIX", Amount: decimal.RequireFromString("500"), Currency: "BRL", @@ -647,7 +648,7 @@ func TestLimitsVerification_5_2_2_DoesNotIncrementOnRuleBasedDeny(t *testing.T) resp1, body1 := testutil.CreateValidation(t, firstReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "First validation: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "First validation: %s", string(body1)) // Create DENY rule that will match CARD transactions with high amounts // Use valid transaction type and a specific expression @@ -675,7 +676,7 @@ func TestLimitsVerification_5_2_2_DoesNotIncrementOnRuleBasedDeny(t *testing.T) resp2, body2 := testutil.CreateValidation(t, denyReq) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode, "Expected 200 OK, got: %s", string(body2)) + require.Equal(t, http.StatusCreated, resp2.StatusCode, "Expected 201 Created, got: %s", string(body2)) var result testutil.ValidationResponse err := json.Unmarshal(body2, &result) @@ -739,7 +740,7 @@ func TestLimitsVerification_5_2_3_DoesNotIncrementOnReview(t *testing.T) { setupResp, setupBody := testutil.CreateValidation(t, setupReq) defer setupResp.Body.Close() - require.Equal(t, http.StatusOK, setupResp.StatusCode, "Setup validation should succeed: %s", string(setupBody)) + require.Equal(t, http.StatusCreated, setupResp.StatusCode, "Setup validation should succeed: %s", string(setupBody)) // Create REVIEW rule for WIRE transactions with medium amounts // Use valid transaction type @@ -767,7 +768,7 @@ func TestLimitsVerification_5_2_3_DoesNotIncrementOnReview(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Expected 200 OK, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Expected 201 Created, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -844,7 +845,7 @@ func TestLimitsVerification_5_2_4_ConcurrentTransactionsAccumulateCorrectly(t *t resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { + if resp.StatusCode != http.StatusCreated { errors <- nil // Not an error for this test return } @@ -947,7 +948,7 @@ func TestLimitsVerification_5_2_5_RaceConditionPrevented(t *testing.T) { respSetup, bodySetup := testutil.CreateValidation(t, setupReq) defer respSetup.Body.Close() - require.Equal(t, http.StatusOK, respSetup.StatusCode, "Setup validation should succeed: %s", string(bodySetup)) + require.Equal(t, http.StatusCreated, respSetup.StatusCode, "Setup validation should succeed: %s", string(bodySetup)) // Fire 3 parallel validations of amount=100 each // Expected: Only 1 should succeed (atomic check-and-increment) @@ -978,7 +979,7 @@ func TestLimitsVerification_5_2_5_RaceConditionPrevented(t *testing.T) { defer resp.Body.Close() // Use assert (not require) inside goroutines to avoid panics - if !assert.Equal(t, http.StatusOK, resp.StatusCode, "Validation request should return 200 OK") { + if !assert.Equal(t, http.StatusCreated, resp.StatusCode, "Validation request should return 201 Created") { return } @@ -1134,7 +1135,7 @@ func TestLimitsVerification_DailyLimitPeriodFormat(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Validation should succeed: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Validation should succeed: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -1181,7 +1182,7 @@ func TestLimitsVerification_MonthlyLimitPeriodFormat(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Validation should succeed: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Validation should succeed: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -1235,7 +1236,7 @@ func TestLimitsVerification_5_2_6_RollbackWorks(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, setupReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) // Verify initial usage is 500 apiKey := testutil.GetAPIKey() @@ -1374,7 +1375,7 @@ func TestLimitsVerification_5_3_2_UsageResetsInNewDailyPeriod(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, setupReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) // Verify the limit has resetAt in the future apiKey := testutil.GetAPIKey() @@ -1470,7 +1471,7 @@ func TestLimitsVerification_5_3_3_UsageResetsInNewMonthlyPeriod(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, setupReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) // Verify the MONTHLY limit structure apiKey := testutil.GetAPIKey() @@ -1576,7 +1577,7 @@ func TestLimitsVerification_5_3_4_OldCountersCleanedUp(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, setupReq) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) + require.Equal(t, http.StatusCreated, resp1.StatusCode, "Setup validation should succeed: %s", string(body1)) // Query the usage endpoint to verify counter structure apiKey := testutil.GetAPIKey() @@ -1681,7 +1682,7 @@ func TestLimitsVerification_5_2_7_HighConcurrencyAtomicEnforcement(t *testing.T) defer resp.Body.Close() // Use assert (not require) inside goroutines to avoid panics - if !assert.Equal(t, http.StatusOK, resp.StatusCode, "Validation request should return 200 OK") { + if !assert.Equal(t, http.StatusCreated, resp.StatusCode, "Validation request should return 201 Created") { return } @@ -1782,7 +1783,7 @@ func TestLimitsVerification_5_4_1_BackdatedTimestampBypass(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Transaction %d should succeed: %s", i+1, string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction %d should succeed: %s", i+1, string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -1818,7 +1819,7 @@ func TestLimitsVerification_5_4_1_BackdatedTimestampBypass(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Backdated transaction %d should return 200: %s", i+1, string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Backdated transaction %d should return 201: %s", i+1, string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -1902,8 +1903,8 @@ func TestLimitsVerification_5_4_3_MaxTimestampAgeBoundary(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - // Should return HTTP 200 (accepted) - require.Equal(t, http.StatusOK, resp.StatusCode, + // Should return HTTP 201 (accepted) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Timestamp (24h - 1min) in past should be accepted: %s", string(body)) var result testutil.ValidationResponse @@ -1976,7 +1977,7 @@ func TestLimitsVerification_5_4_4_AuditTrailPreservation(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction with 2h-old timestamp should be accepted: %s", string(body)) var result testutil.ValidationResponse @@ -2048,8 +2049,8 @@ func TestLimitsVerification_5_4_5_PerTransactionUnaffected(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, - "Transaction under limit should return 200: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, + "Transaction under limit should return 201: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -2084,8 +2085,8 @@ func TestLimitsVerification_5_4_5_PerTransactionUnaffected(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, - "Transaction with past timestamp under limit should return 200: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, + "Transaction with past timestamp under limit should return 201: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -2118,8 +2119,8 @@ func TestLimitsVerification_5_4_5_PerTransactionUnaffected(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, - "Transaction over limit should return 200 with DENY: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, + "Transaction over limit should return 201 with DENY: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) diff --git a/tests/integration/06_authentication_test.go b/tests/integration/06_authentication_test.go index f194854..8a7f280 100644 --- a/tests/integration/06_authentication_test.go +++ b/tests/integration/06_authentication_test.go @@ -15,6 +15,7 @@ import ( "testing" "time" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -42,7 +43,7 @@ func validPayload(t *testing.T) []byte { t.Helper() payload := map[string]any{ - "requestId": "550e8400-e29b-41d4-a716-446655440000", + "requestId": uuid.New().String(), "transactionType": "PIX", "amount": "100.00", "currency": "BRL", @@ -91,7 +92,7 @@ func TestAuth_6_1_1_AcceptsValidAPIKey(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode, "expected 200 OK, body: %s", string(body)) + assert.Equal(t, http.StatusCreated, resp.StatusCode, "expected 201 Created, body: %s", string(body)) var result map[string]any err = json.Unmarshal(body, &result) @@ -99,7 +100,7 @@ func TestAuth_6_1_1_AcceptsValidAPIKey(t *testing.T) { // Validate response fields assert.Contains(t, result, "validationId", "missing validationId") - assert.Equal(t, "550e8400-e29b-41d4-a716-446655440000", result["requestId"], "requestId should echo input") + assert.Contains(t, result, "requestId", "missing requestId") decision, ok := result["decision"].(string) require.True(t, ok, "decision should be a string") assert.Contains(t, []string{"ALLOW", "DENY", "REVIEW"}, decision, "invalid decision value") @@ -198,7 +199,7 @@ func TestAuth_6_1_5_WhitespaceAPIKeyAccepted(t *testing.T) { require.NoError(t, err) // RFC 7230: OWS (Optional WhiteSpace) around field-value must be trimmed - assert.Equal(t, http.StatusOK, resp.StatusCode, + assert.Equal(t, http.StatusCreated, resp.StatusCode, "API should accept key with whitespace (trimmed per RFC 7230), body: %s", string(body)) // Verify response contains expected fields @@ -239,7 +240,7 @@ func TestAuth_6_1_6_HeaderCaseInsensitivity(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode, + assert.Equal(t, http.StatusCreated, resp.StatusCode, "header %s should be accepted (HTTP headers are case-insensitive), body: %s", tc.headerName, string(body)) }) } @@ -317,7 +318,7 @@ func TestAuth_6_1_8_DuplicateAPIKeyHeaders(t *testing.T) { // Go's http middleware uses the first header value for authentication if tc.firstKey == validKey { - assert.Equal(t, http.StatusOK, resp.StatusCode, "first valid header should authenticate") + assert.Equal(t, http.StatusCreated, resp.StatusCode, "first valid header should authenticate") } else { assertAuthError(t, resp, body) } @@ -574,7 +575,7 @@ func TestAuth_6_1_12_DevModeAuthDisabled(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode, + assert.Equal(t, http.StatusCreated, resp.StatusCode, "with API_KEY_ENABLED=false, request should succeed regardless of API key, body: %s", string(body)) var result map[string]any @@ -613,8 +614,8 @@ func TestAuth_6_1_12_DevModeAuthDisabled(t *testing.T) { require.NoError(t, err) defer respWithKey.Body.Close() - require.Equal(t, http.StatusOK, respWithKey.StatusCode, - "after cleanup, request with valid API key should return 200") + require.Equal(t, http.StatusCreated, respWithKey.StatusCode, + "after cleanup, request with valid API key should return 201") }) } diff --git a/tests/integration/07_audit_events_test.go b/tests/integration/07_audit_events_test.go index 830e18d..871b453 100644 --- a/tests/integration/07_audit_events_test.go +++ b/tests/integration/07_audit_events_test.go @@ -438,10 +438,10 @@ func TestAuditEvents_11_2_7_FiltersByAccountId(t *testing.T) { } resp, body := testutil.CreateValidation(t, validationReq) - if resp.StatusCode != http.StatusOK { + if resp.StatusCode != http.StatusCreated { t.Logf("Validation failed with status %d: %s", resp.StatusCode, string(body)) } - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) time.Sleep(100 * time.Millisecond) @@ -493,7 +493,7 @@ func TestAuditEvents_11_2_8_FiltersByTransactionType(t *testing.T) { }, } respPIX, _ := testutil.CreateValidation(t, pixReq) - require.Equal(t, http.StatusOK, respPIX.StatusCode) + require.Equal(t, http.StatusCreated, respPIX.StatusCode) // Create CARD validation cardReq := &testutil.ValidationRequest{ @@ -509,7 +509,7 @@ func TestAuditEvents_11_2_8_FiltersByTransactionType(t *testing.T) { }, } respCard, _ := testutil.CreateValidation(t, cardReq) - require.Equal(t, http.StatusOK, respCard.StatusCode) + require.Equal(t, http.StatusCreated, respCard.StatusCode) time.Sleep(100 * time.Millisecond) @@ -570,7 +570,7 @@ func TestAuditEvents_11_2_9_FiltersByMatchedRuleId(t *testing.T) { } resp, body := testutil.CreateValidation(t, validationReq) - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var validation testutil.ValidationResponse json.Unmarshal(body, &validation) @@ -1442,7 +1442,7 @@ func TestAuditEvents_11_4_5_GeneratesAuditForTransactionValidation(t *testing.T) } resp, body := testutil.CreateValidation(t, validationReq) - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var validation testutil.ValidationResponse json.Unmarshal(body, &validation) @@ -2180,7 +2180,7 @@ func TestAuditEvents_11_10_2_ValidationTriggersAuditEvent(t *testing.T) { } resp, body := testutil.CreateValidation(t, validationReq) - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var validation testutil.ValidationResponse json.Unmarshal(body, &validation) @@ -2636,7 +2636,7 @@ func TestAuditEvents_11_6_1_FiltersBySegmentId(t *testing.T) { } resp, _ := testutil.CreateValidation(t, validationReq) - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) time.Sleep(100 * time.Millisecond) @@ -2705,7 +2705,7 @@ func TestAuditEvents_11_6_2_FiltersByPortfolioId(t *testing.T) { } resp, _ := testutil.CreateValidation(t, validationReq) - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) time.Sleep(100 * time.Millisecond) @@ -2781,7 +2781,7 @@ func TestAuditEvents_11_6_3_CombinesMultipleJSONBFilters(t *testing.T) { } resp, _ := testutil.CreateValidation(t, validationReq) - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) time.Sleep(100 * time.Millisecond) diff --git a/tests/integration/08_limits_time_window_test.go b/tests/integration/08_limits_time_window_test.go index 9d32a9a..bb3ad96 100644 --- a/tests/integration/08_limits_time_window_test.go +++ b/tests/integration/08_limits_time_window_test.go @@ -74,7 +74,7 @@ func TestTimeWindow_InsideWindow(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Transaction should be allowed, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -128,7 +128,7 @@ func TestTimeWindow_OutsideWindow(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Transaction should be allowed, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed, got: %s", string(body)) var result testutil.ValidationResponse err := json.Unmarshal(body, &result) @@ -183,7 +183,7 @@ func TestTimeWindow_OvernightWindow(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -230,7 +230,7 @@ func TestTimeWindow_OvernightWindow(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -277,7 +277,7 @@ func TestTimeWindow_OvernightWindow(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -330,7 +330,7 @@ func TestTimeWindow_Boundaries(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -376,7 +376,7 @@ func TestTimeWindow_Boundaries(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -423,7 +423,7 @@ func TestTimeWindow_Boundaries(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -469,7 +469,7 @@ func TestTimeWindow_Boundaries(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -582,7 +582,7 @@ func TestCustomPeriod_BeforePeriod(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -633,7 +633,7 @@ func TestCustomPeriod_DuringPeriod(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -684,7 +684,7 @@ func TestCustomPeriod_AfterPeriod(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -734,7 +734,7 @@ func TestCustomPeriod_Boundaries(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -778,7 +778,7 @@ func TestCustomPeriod_Boundaries(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -822,7 +822,7 @@ func TestCustomPeriod_Boundaries(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -872,7 +872,7 @@ func TestCustomPeriod_AccumulationAcrossDays(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, req1) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode) + require.Equal(t, http.StatusCreated, resp1.StatusCode) var result1 testutil.ValidationResponse err = json.Unmarshal(body1, &result1) @@ -901,7 +901,7 @@ func TestCustomPeriod_AccumulationAcrossDays(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, req2) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode) + require.Equal(t, http.StatusCreated, resp2.StatusCode) var result2 testutil.ValidationResponse err = json.Unmarshal(body2, &result2) @@ -1010,7 +1010,7 @@ func TestSkipLogic_NoCounterCreated(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1087,7 +1087,7 @@ func TestSkipLogic_MixedInsideOutside(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1171,7 +1171,7 @@ func TestSkipLogic_SkippedNeverBlocks(t *testing.T) { defer resp.Body.Close() // CRITICAL: Should return 200 OK (allowed), NOT 400/422 (blocked) - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed even with amount > maxAmount because limit is skipped") var result testutil.ValidationResponse @@ -1232,7 +1232,7 @@ func TestEvaluatedAt_PresentAndValid(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1294,7 +1294,7 @@ func TestEvaluatedAt_UsesServerNow(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1364,7 +1364,7 @@ func TestWeekly_YearBoundary(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1424,7 +1424,7 @@ func TestWeekly_YearBoundary(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1504,7 +1504,7 @@ func TestCustomPeriodWithTimeWindow_AC09(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Transaction should be allowed: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed: %s", string(body)) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1549,7 +1549,7 @@ func TestCustomPeriodWithTimeWindow_AC09(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Transaction should be allowed (skipped): %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed (skipped): %s", string(body)) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1594,7 +1594,7 @@ func TestCustomPeriodWithTimeWindow_AC09(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Transaction should be allowed (skipped): %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed (skipped): %s", string(body)) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1766,7 +1766,7 @@ func TestPIXCompliancePattern_AC11(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Morning transaction should be allowed, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Morning transaction should be allowed, got: %s", string(body)) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1848,7 +1848,7 @@ func TestPIXCompliancePattern_AC11(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Night transaction should be allowed, got: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Night transaction should be allowed, got: %s", string(body)) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -1931,7 +1931,7 @@ func TestPIXCompliancePattern_AC11(t *testing.T) { resp, body := testutil.CreateValidation(t, req) resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Transaction should be allowed") + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed") err := json.Unmarshal(body, &lastResult) require.NoError(t, err) diff --git a/tests/integration/09_mock_time_validation_test.go b/tests/integration/09_mock_time_validation_test.go index 77abae3..93c451f 100644 --- a/tests/integration/09_mock_time_validation_test.go +++ b/tests/integration/09_mock_time_validation_test.go @@ -61,7 +61,7 @@ func TestMockTime_TimeWindow_Explicit_22h(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(body)) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -113,7 +113,7 @@ func TestMockTime_TimeWindow_Explicit_10h(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, "Response: %s", string(body)) + require.Equal(t, http.StatusCreated, resp.StatusCode, "Response: %s", string(body)) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) diff --git a/tests/integration/10_usage_counter_expires_at_test.go b/tests/integration/10_usage_counter_expires_at_test.go index da8c83f..3a7487b 100644 --- a/tests/integration/10_usage_counter_expires_at_test.go +++ b/tests/integration/10_usage_counter_expires_at_test.go @@ -81,7 +81,7 @@ func TestUsageCounter_ExpiresAt_DAILY(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should succeed, got: %s", string(body)) // Query database for expires_at @@ -151,7 +151,7 @@ func TestUsageCounter_ExpiresAt_MONTHLY(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should succeed, got: %s", string(body)) // Query database for expires_at @@ -221,7 +221,7 @@ func TestUsageCounter_ExpiresAt_WEEKLY(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should succeed, got: %s", string(body)) // Query database for expires_at @@ -293,7 +293,7 @@ func TestUsageCounter_ExpiresAt_CUSTOM(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should succeed, got: %s", string(body)) // Query database for expires_at @@ -359,7 +359,7 @@ func TestUsageCounter_ExpiresAt_PER_TRANSACTION(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "PER_TRANSACTION transaction should succeed, got: %s", string(body)) // Verify no counter was created in the database @@ -419,7 +419,7 @@ func TestUsageCounter_NullExpiresAt_NeverDeleted(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode, + require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should succeed, got: %s", string(body)) db := testutil.SetupIntegrationDB(t) diff --git a/tests/integration/11_backward_compatibility_test.go b/tests/integration/11_backward_compatibility_test.go index 3827362..08c9bf8 100644 --- a/tests/integration/11_backward_compatibility_test.go +++ b/tests/integration/11_backward_compatibility_test.go @@ -70,7 +70,7 @@ func TestBackwardCompatibility_DAILY_NoTimeWindow(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, req1) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode) + require.Equal(t, http.StatusCreated, resp1.StatusCode) var result1 testutil.ValidationResponse err = json.Unmarshal(body1, &result1) @@ -101,7 +101,7 @@ func TestBackwardCompatibility_DAILY_NoTimeWindow(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, req2) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode) + require.Equal(t, http.StatusCreated, resp2.StatusCode) var result2 testutil.ValidationResponse err = json.Unmarshal(body2, &result2) @@ -158,7 +158,7 @@ func TestBackwardCompatibility_MONTHLY_NoTimeWindow(t *testing.T) { resp, body := testutil.CreateValidation(t, req) resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) @@ -187,7 +187,7 @@ func TestBackwardCompatibility_MONTHLY_NoTimeWindow(t *testing.T) { finalResp, finalBody := testutil.CreateValidation(t, finalReq) defer finalResp.Body.Close() - require.Equal(t, http.StatusOK, finalResp.StatusCode) + require.Equal(t, http.StatusCreated, finalResp.StatusCode) var finalResult testutil.ValidationResponse err = json.Unmarshal(finalBody, &finalResult) @@ -239,7 +239,7 @@ func TestBackwardCompatibility_WEEKLY_NoTimeWindow(t *testing.T) { resp1, body1 := testutil.CreateValidation(t, req1) defer resp1.Body.Close() - require.Equal(t, http.StatusOK, resp1.StatusCode) + require.Equal(t, http.StatusCreated, resp1.StatusCode) var result1 testutil.ValidationResponse err = json.Unmarshal(body1, &result1) @@ -268,7 +268,7 @@ func TestBackwardCompatibility_WEEKLY_NoTimeWindow(t *testing.T) { resp2, body2 := testutil.CreateValidation(t, req2) defer resp2.Body.Close() - require.Equal(t, http.StatusOK, resp2.StatusCode) + require.Equal(t, http.StatusCreated, resp2.StatusCode) var result2 testutil.ValidationResponse err = json.Unmarshal(body2, &result2) @@ -319,7 +319,7 @@ func TestBackwardCompatibility_API_Fields(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) // Parse response as generic map to check all fields var rawResponse map[string]interface{} @@ -420,7 +420,7 @@ func TestBackwardCompatibility_MultipleTraditionalLimits(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - require.Equal(t, http.StatusOK, resp.StatusCode) + require.Equal(t, http.StatusCreated, resp.StatusCode) var result testutil.ValidationResponse err = json.Unmarshal(body, &result) From b5e86db0c629a34aba7a9f0ffe56e5520d26e37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Mon, 23 Mar 2026 15:54:54 -0300 Subject: [PATCH 6/9] test: verify cached response fields in idempotency test Assert that duplicate requestId returns identical decision, reason, matchedRuleIds, evaluatedRuleIds, and limitUsageDetails, proving the response is truly cached and not re-processed. --- tests/integration/01_validation_1140_1154_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/integration/01_validation_1140_1154_test.go b/tests/integration/01_validation_1140_1154_test.go index 551277e..9a346a2 100644 --- a/tests/integration/01_validation_1140_1154_test.go +++ b/tests/integration/01_validation_1140_1154_test.go @@ -886,11 +886,16 @@ func TestValidation_1_1_52_UniqueValidationIdPerRequest(t *testing.T) { assert.Equal(t, result1.ValidationID, result2.ValidationID, "Duplicate request should return the same validationId (cached)") + // Verify cached response is identical in all stable fields + assert.Equal(t, result1.Decision, result2.Decision, "Cached decision must match") + assert.Equal(t, result1.Reason, result2.Reason, "Cached reason must match") + assert.Equal(t, result1.MatchedRuleIDs, result2.MatchedRuleIDs, "Cached matchedRuleIds must match") + assert.Equal(t, result1.EvaluatedRuleIDs, result2.EvaluatedRuleIDs, "Cached evaluatedRuleIds must match") + assert.Equal(t, result1.LimitUsageDetails, result2.LimitUsageDetails, "Cached limitUsageDetails must match") + // Verify validationId is different from requestId assert.NotEqual(t, result1.ValidationID, requestID, "validationId should be different from requestId") - assert.NotEqual(t, result2.ValidationID, requestID, - "validationId should be different from requestId") } // Test 1.1.53: Idempotent behavior — duplicate requestId returns cached result From e12e32678b5dfa17b52d0759e40627054b83d25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Mon, 23 Mar 2026 15:59:48 -0300 Subject: [PATCH 7/9] test: mutate pointee instead of pointer in deep copy test Dereference the copied AccountID pointer before mutation so the assertion actually detects shared pointer targets between original and copy. --- pkg/model/transaction_validation_response_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/model/transaction_validation_response_test.go b/pkg/model/transaction_validation_response_test.go index da6e8dd..8b5df10 100644 --- a/pkg/model/transaction_validation_response_test.go +++ b/pkg/model/transaction_validation_response_test.go @@ -250,9 +250,10 @@ func TestTransactionValidation_ToValidationResponse_DefensiveCopy_Scopes(t *test // Mutate response's Scopes slice (append) resp.LimitUsageDetails[0].Scopes = append(resp.LimitUsageDetails[0].Scopes, Scope{}) - // Mutate a nested pointer inside the copied Scope to verify deep copy - mutatedID := uuid.MustParse("99999999-9999-9999-9999-999999999999") - resp.LimitUsageDetails[0].Scopes[0].AccountID = &mutatedID + // Mutate the pointee (not the pointer) to detect shared pointer targets + require.NotNil(t, resp.LimitUsageDetails[0].Scopes[0].AccountID, + "copied AccountID pointer should not be nil") + *resp.LimitUsageDetails[0].Scopes[0].AccountID = uuid.MustParse("99999999-9999-9999-9999-999999999999") // Original must remain unchanged require.Len(t, tv.LimitUsageDetails[0].Scopes, 1, From 5de8c279d61c2e1de80fe0b5569800f9c5621c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Mon, 23 Mar 2026 17:25:11 -0300 Subject: [PATCH 8/9] test: add follow-up request to prove no double-count on dedup After the duplicate returns cached ALLOW, send a new 400 request that would DENY if the original 600 had been counted twice (600+600+400 > 1000). ALLOW proves usage was consumed only once. --- .../01_validation_1140_1154_test.go | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/integration/01_validation_1140_1154_test.go b/tests/integration/01_validation_1140_1154_test.go index 9a346a2..36bcedc 100644 --- a/tests/integration/01_validation_1140_1154_test.go +++ b/tests/integration/01_validation_1140_1154_test.go @@ -948,6 +948,30 @@ func TestValidation_1_1_53_IdempotentBehavior(t *testing.T) { // With idempotency, duplicate returns the same validationId assert.Equal(t, result1.ValidationID, result2.ValidationID, "Duplicate request should return the same validationId (cached)") + + // Follow-up: prove no double-count by consuming remaining headroom (400 of 1000). + // If the duplicate had consumed 600 again, this 400 would exceed 1000 and DENY. + followUpReq := &testutil.ValidationRequest{ + RequestID: testutil.MustDeterministicUUID(1132).String(), + TransactionType: "CARD", + Amount: decimal.RequireFromString("400"), + Currency: "BRL", + TransactionTimestamp: testutil.FixedTime().UTC().Format(time.RFC3339), + Account: &testutil.AccountContext{ + ID: accountID, + }, + } + + resp3, body3 := testutil.CreateValidation(t, followUpReq) + defer resp3.Body.Close() + require.Equal(t, http.StatusCreated, resp3.StatusCode, "Follow-up should return 201: %s", string(body3)) + + var result3 testutil.ValidationResponse + err = json.Unmarshal(body3, &result3) + require.NoError(t, err) + + assert.Equal(t, "ALLOW", result3.Decision, + "Follow-up 400 should ALLOW (usage=600+400=1000, proves duplicate did not double-count)") } // Test 1.1.54: Validation with all rule actions matching From bc03a23231e34f480348f7b45d41fcb3fe200c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Sandro=20Garz=C3=A3o?= Date: Mon, 23 Mar 2026 17:25:50 -0300 Subject: [PATCH 9/9] docs: fix stale comment referencing 200 OK instead of 201 Created --- tests/integration/08_limits_time_window_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/08_limits_time_window_test.go b/tests/integration/08_limits_time_window_test.go index bb3ad96..72df189 100644 --- a/tests/integration/08_limits_time_window_test.go +++ b/tests/integration/08_limits_time_window_test.go @@ -1170,7 +1170,7 @@ func TestSkipLogic_SkippedNeverBlocks(t *testing.T) { resp, body := testutil.CreateValidation(t, req) defer resp.Body.Close() - // CRITICAL: Should return 200 OK (allowed), NOT 400/422 (blocked) + // CRITICAL: Should return 201 Created (allowed), NOT 400/422 (blocked) require.Equal(t, http.StatusCreated, resp.StatusCode, "Transaction should be allowed even with amount > maxAmount because limit is skipped")