diff --git a/components/ledger/api/docs.go b/components/ledger/api/docs.go index 73c4d63f4..65d9115f8 100644 --- a/components/ledger/api/docs.go +++ b/components/ledger/api/docs.go @@ -9929,6 +9929,12 @@ const docTemplate = ` "maxLength": 100, "example": "ROUTE-001" }, + "routeDescription": { + "description": "Human-readable description of the operation route for accounting traceability\nexample: Settlement route for service charges\nmaxLength: 250", + "type": "string", + "maxLength": 250, + "example": "Settlement route for service charges" + }, "routeId": { "description": "UUID of the operation route that generated this operation. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", "type": "string", @@ -10148,11 +10154,17 @@ const docTemplate = ` "example": "00000000-0000-0000-0000-000000000000" }, "route": { - "description": "Deprecated: legacy route identifier, use routeId on individual operations instead. Contains the operation route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", + "description": "Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", "type": "string", "maxLength": 250, "example": "00000000-0000-0000-0000-000000000000" }, + "routeId": { + "description": "UUID of the transaction route. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", + "type": "string", + "format": "uuid", + "example": "00000000-0000-0000-0000-000000000000" + }, "source": { "description": "List of source account aliases or identifiers\nexample: [\"@person1\"]", "type": "array", diff --git a/components/ledger/api/swagger.json b/components/ledger/api/swagger.json index 87ee797f9..14817ddab 100644 --- a/components/ledger/api/swagger.json +++ b/components/ledger/api/swagger.json @@ -9905,6 +9905,12 @@ "maxLength": 100, "example": "ROUTE-001" }, + "routeDescription": { + "description": "Human-readable description of the operation route for accounting traceability\nexample: Settlement route for service charges\nmaxLength: 250", + "type": "string", + "maxLength": 250, + "example": "Settlement route for service charges" + }, "routeId": { "description": "UUID of the operation route that generated this operation. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", "type": "string", @@ -10124,11 +10130,17 @@ "example": "00000000-0000-0000-0000-000000000000" }, "route": { - "description": "Deprecated: legacy route identifier, use routeId on individual operations instead. Contains the operation route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", + "description": "Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", "type": "string", "maxLength": 250, "example": "00000000-0000-0000-0000-000000000000" }, + "routeId": { + "description": "UUID of the transaction route. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", + "type": "string", + "format": "uuid", + "example": "00000000-0000-0000-0000-000000000000" + }, "source": { "description": "List of source account aliases or identifiers\nexample: [\"@person1\"]", "type": "array", diff --git a/components/ledger/api/swagger.yaml b/components/ledger/api/swagger.yaml index e9c0744d9..b24deb2c6 100644 --- a/components/ledger/api/swagger.yaml +++ b/components/ledger/api/swagger.yaml @@ -7694,6 +7694,16 @@ definitions: type: string maxLength: 100 example: ROUTE-001 + routeDescription: + description: 'Human-readable description of the operation route for accounting + traceability + + example: Settlement route for service charges + + maxLength: 250' + type: string + maxLength: 250 + example: Settlement route for service charges routeId: description: 'UUID of the operation route that generated this operation. Primary field for route identification, validation, and accounting. @@ -7933,9 +7943,8 @@ definitions: format: uuid example: 00000000-0000-0000-0000-000000000000 route: - description: 'Deprecated: legacy route identifier, use routeId on individual - operations instead. Contains the operation route UUID as a free-form string - for backwards compatibility. + description: 'Deprecated: legacy route identifier, use routeId instead. Contains + the transaction route UUID as a free-form string for backwards compatibility. example: 00000000-0000-0000-0000-000000000000 @@ -7945,6 +7954,16 @@ definitions: type: string maxLength: 250 example: 00000000-0000-0000-0000-000000000000 + routeId: + description: 'UUID of the transaction route. Primary field for route identification, + validation, and accounting. + + example: 00000000-0000-0000-0000-000000000000 + + format: uuid' + type: string + format: uuid + example: 00000000-0000-0000-0000-000000000000 source: description: 'List of source account aliases or identifiers diff --git a/components/transaction/api/openapi.yaml b/components/transaction/api/openapi.yaml index 9b8f552b9..5060a265d 100644 --- a/components/transaction/api/openapi.yaml +++ b/components/transaction/api/openapi.yaml @@ -3744,6 +3744,7 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' @@ -3903,6 +3904,14 @@ components: example: ROUTE-001 maxLength: 100 type: string + routeDescription: + description: |- + Human-readable description of the operation route for accounting traceability + example: Settlement route for service charges + maxLength: 250 + example: Settlement route for service charges + maxLength: 250 + type: string routeId: description: |- UUID of the operation route that generated this operation. Primary field for route identification, validation, and accounting. @@ -4104,6 +4113,7 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' @@ -4130,10 +4140,12 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' route: 00000000-0000-0000-0000-000000000000 + routeId: 00000000-0000-0000-0000-000000000000 id: 00000000-0000-0000-0000-000000000000 status: '{}' updatedAt: 2021-01-01T00:00:00Z @@ -4243,13 +4255,21 @@ components: type: string route: description: |- - Deprecated: legacy route identifier, use routeId on individual operations instead. Contains the operation route UUID as a free-form string for backwards compatibility. + Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. example: 00000000-0000-0000-0000-000000000000 maxLength: 250 deprecated: true example: 00000000-0000-0000-0000-000000000000 maxLength: 250 type: string + routeId: + description: |- + UUID of the transaction route. Primary field for route identification, validation, and accounting. + example: 00000000-0000-0000-0000-000000000000 + format: uuid + example: 00000000-0000-0000-0000-000000000000 + format: uuid + type: string source: description: |- List of source account aliases or identifiers @@ -4830,6 +4850,7 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' @@ -4856,6 +4877,7 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' @@ -5112,6 +5134,7 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' @@ -5138,10 +5161,12 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' route: 00000000-0000-0000-0000-000000000000 + routeId: 00000000-0000-0000-0000-000000000000 id: 00000000-0000-0000-0000-000000000000 status: '{}' updatedAt: 2021-01-01T00:00:00Z @@ -5184,6 +5209,7 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' @@ -5210,10 +5236,12 @@ components: accountId: 00000000-0000-0000-0000-000000000000 deletedAt: 2021-01-01T00:00:00Z route: 00000000-0000-0000-0000-000000000000 + routeDescription: Settlement route for service charges balanceKey: asset-freeze balanceId: 00000000-0000-0000-0000-000000000000 status: '{}' route: 00000000-0000-0000-0000-000000000000 + routeId: 00000000-0000-0000-0000-000000000000 id: 00000000-0000-0000-0000-000000000000 status: '{}' updatedAt: 2021-01-01T00:00:00Z diff --git a/components/transaction/api/transaction_docs.go b/components/transaction/api/transaction_docs.go index f8edf7ad3..a83392cc2 100644 --- a/components/transaction/api/transaction_docs.go +++ b/components/transaction/api/transaction_docs.go @@ -4493,6 +4493,12 @@ const docTemplatetransaction = `{ "maxLength": 100, "example": "ROUTE-001" }, + "routeDescription": { + "description": "Human-readable description of the operation route for accounting traceability\nexample: Settlement route for service charges\nmaxLength: 250", + "type": "string", + "maxLength": 250, + "example": "Settlement route for service charges" + }, "routeId": { "description": "UUID of the operation route that generated this operation. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", "type": "string", @@ -4730,11 +4736,17 @@ const docTemplatetransaction = `{ "example": "00000000-0000-0000-0000-000000000000" }, "route": { - "description": "Deprecated: legacy route identifier, use routeId on individual operations instead. Contains the operation route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", + "description": "Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", "type": "string", "maxLength": 250, "example": "00000000-0000-0000-0000-000000000000" }, + "routeId": { + "description": "UUID of the transaction route. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", + "type": "string", + "format": "uuid", + "example": "00000000-0000-0000-0000-000000000000" + }, "source": { "description": "List of source account aliases or identifiers\nexample: [\"@person1\"]", "type": "array", diff --git a/components/transaction/api/transaction_swagger.json b/components/transaction/api/transaction_swagger.json index d368fa86b..c387f496a 100644 --- a/components/transaction/api/transaction_swagger.json +++ b/components/transaction/api/transaction_swagger.json @@ -4487,6 +4487,12 @@ "maxLength": 100, "example": "ROUTE-001" }, + "routeDescription": { + "description": "Human-readable description of the operation route for accounting traceability\nexample: Settlement route for service charges\nmaxLength: 250", + "type": "string", + "maxLength": 250, + "example": "Settlement route for service charges" + }, "routeId": { "description": "UUID of the operation route that generated this operation. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", "type": "string", @@ -4724,11 +4730,17 @@ "example": "00000000-0000-0000-0000-000000000000" }, "route": { - "description": "Deprecated: legacy route identifier, use routeId on individual operations instead. Contains the operation route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", + "description": "Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility.\nexample: 00000000-0000-0000-0000-000000000000\nmaxLength: 250\ndeprecated: true", "type": "string", "maxLength": 250, "example": "00000000-0000-0000-0000-000000000000" }, + "routeId": { + "description": "UUID of the transaction route. Primary field for route identification, validation, and accounting.\nexample: 00000000-0000-0000-0000-000000000000\nformat: uuid", + "type": "string", + "format": "uuid", + "example": "00000000-0000-0000-0000-000000000000" + }, "source": { "description": "List of source account aliases or identifiers\nexample: [\"@person1\"]", "type": "array", diff --git a/components/transaction/api/transaction_swagger.yaml b/components/transaction/api/transaction_swagger.yaml index b6ae5c235..de5704158 100644 --- a/components/transaction/api/transaction_swagger.yaml +++ b/components/transaction/api/transaction_swagger.yaml @@ -943,6 +943,14 @@ definitions: example: ROUTE-001 maxLength: 100 type: string + routeDescription: + description: |- + Human-readable description of the operation route for accounting traceability + example: Settlement route for service charges + maxLength: 250 + example: Settlement route for service charges + maxLength: 250 + type: string routeId: description: |- UUID of the operation route that generated this operation. Primary field for route identification, validation, and accounting. @@ -1188,13 +1196,21 @@ definitions: type: string route: description: |- - Deprecated: legacy route identifier, use routeId on individual operations instead. Contains the operation route UUID as a free-form string for backwards compatibility. + Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. example: 00000000-0000-0000-0000-000000000000 maxLength: 250 deprecated: true example: 00000000-0000-0000-0000-000000000000 maxLength: 250 type: string + routeId: + description: |- + UUID of the transaction route. Primary field for route identification, validation, and accounting. + example: 00000000-0000-0000-0000-000000000000 + format: uuid + example: 00000000-0000-0000-0000-000000000000 + format: uuid + type: string source: description: |- List of source account aliases or identifiers diff --git a/components/transaction/internal/adapters/http/in/transaction-creation-helpers.go b/components/transaction/internal/adapters/http/in/transaction-creation-helpers.go index 16a6bac9c..2829737ab 100644 --- a/components/transaction/internal/adapters/http/in/transaction-creation-helpers.go +++ b/components/transaction/internal/adapters/http/in/transaction-creation-helpers.go @@ -867,6 +867,7 @@ func (handler *TransactionHandler) createTransaction(c *fiber.Ctx, transactionIn CreatedAt: transactionDate, UpdatedAt: time.Now(), Route: transactionInput.Route, + RouteID: transactionInput.RouteID, Metadata: transactionInput.Metadata, Status: transaction.Status{ Code: transactionStatus, diff --git a/components/transaction/internal/adapters/postgres/transaction/transaction.go b/components/transaction/internal/adapters/postgres/transaction/transaction.go index 2b8b2a6bd..09c2075d6 100644 --- a/components/transaction/internal/adapters/postgres/transaction/transaction.go +++ b/components/transaction/internal/adapters/postgres/transaction/transaction.go @@ -37,7 +37,8 @@ type TransactionPostgreSQLModel struct { CreatedAt time.Time // Creation timestamp UpdatedAt time.Time // Last update timestamp DeletedAt sql.NullTime // Deletion timestamp (if soft-deleted) - Route *string // Route + Route *string // Deprecated: legacy route identifier. Use RouteID instead. + RouteID *string // UUID of the transaction route (FK to transaction_route.id) Metadata map[string]any // Additional custom attributes } @@ -92,11 +93,16 @@ type CreateTransactionInput struct { // swagger:type object Metadata map[string]any `json:"metadata" validate:"dive,keys,keymax=100,endkeys,omitempty,nonested,valuemax=2000" example:"{\"reference\": \"TRANSACTION-001\", \"source\": \"api\"}"` - // Deprecated: legacy route identifier, use routeId on FromTo entries instead. Contains the operation route UUID as a free-form string for backwards compatibility. + // Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. // example: "00000000-0000-0000-0000-000000000000" // maxLength: 250 Route string `json:"route,omitempty" validate:"omitempty,valuemax=250" example:"00000000-0000-0000-0000-000000000000"` + // UUID of the transaction route. Used instead of route for proper UUID validation and referential integrity. + // example: 00000000-0000-0000-0000-000000000000 + // format: uuid + RouteID *string `json:"routeId,omitempty" validate:"omitempty,uuid" example:"00000000-0000-0000-0000-000000000000" format:"uuid"` + // TransactionDate Period from transaction creation date until now // Example "2021-01-01T00:00:00Z" // format: date-time @@ -187,6 +193,17 @@ type CreateTransactionSwaggerModel struct { // example: {"reference": "TRANSACTION-001", "source": "api"} Metadata map[string]any `json:"metadata,omitempty"` + // Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. + // example: 00000000-0000-0000-0000-000000000000 + // maxLength: 250 + // deprecated: true + Route string `json:"route,omitempty" example:"00000000-0000-0000-0000-000000000000" maxLength:"250"` + + // UUID of the transaction route. Used instead of route for proper UUID validation and referential integrity. + // example: 00000000-0000-0000-0000-000000000000 + // format: uuid + RouteID *string `json:"routeId,omitempty" example:"00000000-0000-0000-0000-000000000000" format:"uuid"` + // TransactionDate Period from transaction creation date until now // Example "2021-01-01T00:00:00Z" // swagger: type string @@ -381,12 +398,17 @@ type Transaction struct { // Transaction body containing detailed operation data (not exposed in JSON) Body pkgTransaction.Transaction `json:"-"` - // Deprecated: legacy route identifier, use routeId on individual operations instead. Contains the operation route UUID as a free-form string for backwards compatibility. + // Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. // example: 00000000-0000-0000-0000-000000000000 // maxLength: 250 // deprecated: true Route string `json:"route" example:"00000000-0000-0000-0000-000000000000" maxLength:"250"` + // UUID of the transaction route. Primary field for route identification, validation, and accounting. + // example: 00000000-0000-0000-0000-000000000000 + // format: uuid + RouteID *string `json:"routeId,omitempty" example:"00000000-0000-0000-0000-000000000000" format:"uuid"` + // Timestamp when the transaction was created // example: 2021-01-01T00:00:00Z // format: date-time @@ -444,6 +466,10 @@ func (t *TransactionPostgreSQLModel) ToEntity() *Transaction { transaction.Route = *t.Route } + if t.RouteID != nil { + transaction.RouteID = t.RouteID + } + if !t.DeletedAt.Time.IsZero() { deletedAtCopy := t.DeletedAt.Time transaction.DeletedAt = &deletedAtCopy @@ -482,6 +508,10 @@ func (t *TransactionPostgreSQLModel) FromEntity(transaction *Transaction) { t.Route = &transaction.Route } + if transaction.RouteID != nil { + t.RouteID = transaction.RouteID + } + if transaction.DeletedAt != nil { deletedAtCopy := *transaction.DeletedAt t.DeletedAt = sql.NullTime{Time: deletedAtCopy, Valid: true} @@ -498,6 +528,7 @@ func (cti *CreateTransactionInput) BuildTransaction() *pkgTransaction.Transactio Metadata: cti.Metadata, TransactionDate: cti.TransactionDate, Route: cti.Route, + RouteID: cti.RouteID, } if cti.Send != nil { @@ -574,6 +605,7 @@ func (t Transaction) TransactionRevert() pkgTransaction.Transaction { Pending: false, Metadata: t.Metadata, Route: t.Route, + RouteID: t.RouteID, Send: send, } @@ -657,11 +689,16 @@ type CreateTransactionInflowInput struct { // swagger:type object Metadata map[string]any `json:"metadata" validate:"dive,keys,keymax=100,endkeys,omitempty,nonested,valuemax=2000" example:"{\"reference\": \"TRANSACTION-001\", \"source\": \"api\"}"` - // Deprecated: legacy route identifier, use routeId on FromTo entries instead. Contains the operation route UUID as a free-form string for backwards compatibility. + // Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. // example: 00000000-0000-0000-0000-000000000000 // maxLength: 250 Route string `json:"route,omitempty" validate:"omitempty,valuemax=250" example:"00000000-0000-0000-0000-000000000000"` + // UUID of the transaction route. Used instead of route for proper UUID validation and referential integrity. + // example: 00000000-0000-0000-0000-000000000000 + // format: uuid + RouteID *string `json:"routeId,omitempty" validate:"omitempty,uuid" example:"00000000-0000-0000-0000-000000000000" format:"uuid"` + // TransactionDate Period from transaction creation date until now // Example "2021-01-01T00:00:00Z" // format: date-time @@ -740,6 +777,17 @@ type CreateTransactionInflowSwaggerModel struct { // example: {"reference": "TRANSACTION-001", "source": "api"} Metadata map[string]any `json:"metadata,omitempty"` + // Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. + // example: 00000000-0000-0000-0000-000000000000 + // maxLength: 250 + // deprecated: true + Route string `json:"route,omitempty" example:"00000000-0000-0000-0000-000000000000" maxLength:"250"` + + // UUID of the transaction route. Used instead of route for proper UUID validation and referential integrity. + // example: 00000000-0000-0000-0000-000000000000 + // format: uuid + RouteID *string `json:"routeId,omitempty" example:"00000000-0000-0000-0000-000000000000" format:"uuid"` + // TransactionDate Period from transaction creation date until now // Example "2021-01-01T00:00:00Z" // swagger: type string @@ -822,6 +870,7 @@ func (c *CreateTransactionInflowInput) BuildInflowEntry() *pkgTransaction.Transa Metadata: c.Metadata, TransactionDate: c.TransactionDate, Route: c.Route, + RouteID: c.RouteID, Send: pkgTransaction.Send{ Asset: c.Send.Asset, Value: c.Send.Value, @@ -863,11 +912,16 @@ type CreateTransactionOutflowInput struct { // swagger:type object Metadata map[string]any `json:"metadata" validate:"dive,keys,keymax=100,endkeys,omitempty,nonested,valuemax=2000" example:"{\"reference\": \"TRANSACTION-001\", \"source\": \"api\"}"` - // Deprecated: legacy route identifier, use routeId on FromTo entries instead. Contains the operation route UUID as a free-form string for backwards compatibility. + // Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. // example: 00000000-0000-0000-0000-000000000000 // maxLength: 250 Route string `json:"route,omitempty" validate:"omitempty,valuemax=250" example:"00000000-0000-0000-0000-000000000000"` + // UUID of the transaction route. Used instead of route for proper UUID validation and referential integrity. + // example: 00000000-0000-0000-0000-000000000000 + // format: uuid + RouteID *string `json:"routeId,omitempty" validate:"omitempty,uuid" example:"00000000-0000-0000-0000-000000000000" format:"uuid"` + // TransactionDate Period from transaction creation date until now // Example "2021-01-01T00:00:00Z" // format: date-time @@ -951,6 +1005,17 @@ type CreateTransactionOutflowSwaggerModel struct { // example: {"reference": "TRANSACTION-001", "source": "api"} Metadata map[string]any `json:"metadata,omitempty"` + // Deprecated: legacy route identifier, use routeId instead. Contains the transaction route UUID as a free-form string for backwards compatibility. + // example: 00000000-0000-0000-0000-000000000000 + // maxLength: 250 + // deprecated: true + Route string `json:"route,omitempty" example:"00000000-0000-0000-0000-000000000000" maxLength:"250"` + + // UUID of the transaction route. Used instead of route for proper UUID validation and referential integrity. + // example: 00000000-0000-0000-0000-000000000000 + // format: uuid + RouteID *string `json:"routeId,omitempty" example:"00000000-0000-0000-0000-000000000000" format:"uuid"` + // TransactionDate Period from transaction creation date until now // Example "2021-01-01T00:00:00Z" // swagger: type string @@ -1034,6 +1099,7 @@ func (c *CreateTransactionOutflowInput) BuildOutflowEntry() *pkgTransaction.Tran Metadata: c.Metadata, TransactionDate: c.TransactionDate, Route: c.Route, + RouteID: c.RouteID, Send: pkgTransaction.Send{ Asset: c.Send.Asset, Value: c.Send.Value, diff --git a/components/transaction/internal/adapters/postgres/transaction/transaction.postgresql.go b/components/transaction/internal/adapters/postgres/transaction/transaction.postgresql.go index 09ebd4bc1..f99c091e1 100644 --- a/components/transaction/internal/adapters/postgres/transaction/transaction.postgresql.go +++ b/components/transaction/internal/adapters/postgres/transaction/transaction.postgresql.go @@ -50,6 +50,7 @@ var transactionColumnList = []string{ "updated_at", "deleted_at", "route", + "route_id", } var transactionColumnListPrefixed = []string{ @@ -68,6 +69,7 @@ var transactionColumnListPrefixed = []string{ "t.updated_at", "t.deleted_at", "t.route", + "t.route_id", } // Repository provides an interface for operations related to transaction template entities. @@ -144,7 +146,7 @@ func (r *TransactionPostgreSQLRepository) Create(ctx context.Context, transactio ctx, spanExec := tracer.Start(ctx, "postgres.create.exec") defer spanExec.End() - result, err := db.ExecContext(ctx, `INSERT INTO transaction VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING *`, + result, err := db.ExecContext(ctx, `INSERT INTO transaction VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING *`, record.ID, record.ParentTransactionID, record.Description, @@ -160,6 +162,7 @@ func (r *TransactionPostgreSQLRepository) Create(ctx context.Context, transactio record.UpdatedAt, record.DeletedAt, record.Route, + record.RouteID, ) if err != nil { var pgErr *pgconn.PgError @@ -291,6 +294,7 @@ func (r *TransactionPostgreSQLRepository) FindAll(ctx context.Context, organizat &transaction.UpdatedAt, &transaction.DeletedAt, &transaction.Route, + &transaction.RouteID, ); err != nil { libOpentelemetry.HandleSpanError(span, "Failed to scan row", err) @@ -411,6 +415,7 @@ func (r *TransactionPostgreSQLRepository) ListByIDs(ctx context.Context, organiz &transaction.UpdatedAt, &transaction.DeletedAt, &transaction.Route, + &transaction.RouteID, ); err != nil { libOpentelemetry.HandleSpanError(span, "Failed to scan row", err) @@ -502,6 +507,7 @@ func (r *TransactionPostgreSQLRepository) Find(ctx context.Context, organization &transaction.UpdatedAt, &transaction.DeletedAt, &transaction.Route, + &transaction.RouteID, ); err != nil { if errors.Is(err, sql.ErrNoRows) { err := pkg.ValidateBusinessError(constant.ErrEntityNotFound, reflect.TypeOf(Transaction{}).Name()) @@ -592,6 +598,7 @@ func (r *TransactionPostgreSQLRepository) FindByParentID(ctx context.Context, or &transaction.UpdatedAt, &transaction.DeletedAt, &transaction.Route, + &transaction.RouteID, ); err != nil { if errors.Is(err, sql.ErrNoRows) { libOpentelemetry.HandleSpanBusinessErrorEvent(span, "No transaction found", err) @@ -844,6 +851,7 @@ func (r *TransactionPostgreSQLRepository) FindWithOperations(ctx context.Context &tran.UpdatedAt, &tran.DeletedAt, &tran.Route, + &tran.RouteID, &op.ID, &op.TransactionID, &op.Description, @@ -1045,6 +1053,7 @@ func (r *TransactionPostgreSQLRepository) FindOrListAllWithOperations(ctx contex &tran.UpdatedAt, &tran.DeletedAt, &tran.Route, + &tran.RouteID, &opID, &opTransactionID, &opDescription, diff --git a/components/transaction/internal/bootstrap/redis.consumer.go b/components/transaction/internal/bootstrap/redis.consumer.go index bf57f6127..fea6fea0c 100644 --- a/components/transaction/internal/bootstrap/redis.consumer.go +++ b/components/transaction/internal/bootstrap/redis.consumer.go @@ -362,6 +362,7 @@ func (r *RedisQueueConsumer) processMessage(ctx context.Context, key string, m m CreatedAt: m.TransactionDate, UpdatedAt: time.Now(), Route: m.TransactionInput.Route, + RouteID: m.TransactionInput.RouteID, Metadata: m.TransactionInput.Metadata, Status: postgreTransaction.Status{ Code: m.TransactionStatus, @@ -392,11 +393,25 @@ func (r *RedisQueueConsumer) processMessage(ctx context.Context, key string, m m var routeCache *mmodel.TransactionRouteCache - if ledgerSettings.Accounting.ValidateRoutes && m.Validate.TransactionRoute != "" { - trID, parseErr := uuid.Parse(m.Validate.TransactionRoute) - if parseErr != nil { - logger.Log(ctx, libLog.LevelDebug, fmt.Sprintf("Failed to parse TransactionRoute UUID %s: %v", m.Validate.TransactionRoute, parseErr)) - } else { + if ledgerSettings.Accounting.ValidateRoutes { + // Prefer the new TransactionRouteID (UUID) over the deprecated TransactionRoute string. + var trID uuid.UUID + + var parseErr error + + if !libCommons.IsNilOrEmpty(m.Validate.TransactionRouteID) { + trID, parseErr = uuid.Parse(*m.Validate.TransactionRouteID) + if parseErr != nil { + logger.Log(ctx, libLog.LevelDebug, fmt.Sprintf("Failed to parse TransactionRouteID %s: %v", *m.Validate.TransactionRouteID, parseErr)) + } + } else if m.Validate.TransactionRoute != "" { + trID, parseErr = uuid.Parse(m.Validate.TransactionRoute) + if parseErr != nil { + logger.Log(ctx, libLog.LevelDebug, fmt.Sprintf("Failed to parse TransactionRoute UUID %s: %v", m.Validate.TransactionRoute, parseErr)) + } + } + + if parseErr == nil && trID != uuid.Nil { cache, cacheErr := r.TransactionHandler.Query.GetOrCreateTransactionRouteCache(msgCtxWithSpan, m.OrganizationID, m.LedgerID, trID) if cacheErr != nil { logger.Log(ctx, libLog.LevelDebug, fmt.Sprintf("Failed to get route cache for org=%s ledger=%s route=%s: %v", m.OrganizationID, m.LedgerID, trID, cacheErr)) diff --git a/components/transaction/internal/services/query/validate-accounting-routes.go b/components/transaction/internal/services/query/validate-accounting-routes.go index 129858a27..3aaf04099 100644 --- a/components/transaction/internal/services/query/validate-accounting-routes.go +++ b/components/transaction/internal/services/query/validate-accounting-routes.go @@ -47,24 +47,41 @@ func (uc *UseCase) ValidateAccountingRules(ctx context.Context, organizationID, logger.Log(ctx, libLog.LevelDebug, fmt.Sprintf("Route validation enabled for ledger %s, validating accounting rules", ledgerID.String())) - if libCommons.IsNilOrEmpty(&validate.TransactionRoute) { - err := pkg.ValidateBusinessError(constant.ErrTransactionRouteNotInformed, "") - libOpentelemetry.HandleSpanBusinessErrorEvent(span, "Transaction route is empty", err) + // Resolve the transaction route UUID: prefer the new routeId field, fall back to the deprecated route string. + var ( + transactionRouteID uuid.UUID + err error + ) - logger.Log(ctx, libLog.LevelWarn, "Transaction route is empty") + if !libCommons.IsNilOrEmpty(validate.TransactionRouteID) { + transactionRouteID, err = uuid.Parse(*validate.TransactionRouteID) + if err != nil { + validationErr := pkg.ValidateBusinessError(constant.ErrInvalidTransactionRouteID, "") - return nil, err - } + libOpentelemetry.HandleSpanBusinessErrorEvent(span, "Invalid transaction route ID format", validationErr) - transactionRouteID, err := uuid.Parse(validate.TransactionRoute) - if err != nil { - validationErr := pkg.ValidateBusinessError(constant.ErrInvalidTransactionRouteID, "") + logger.Log(ctx, libLog.LevelWarn, fmt.Sprintf("Invalid transaction route ID format: %v", err)) + + return nil, validationErr + } + } else if !libCommons.IsNilOrEmpty(&validate.TransactionRoute) { + transactionRouteID, err = uuid.Parse(validate.TransactionRoute) + if err != nil { + validationErr := pkg.ValidateBusinessError(constant.ErrInvalidTransactionRouteID, "") - libOpentelemetry.HandleSpanBusinessErrorEvent(span, "Invalid transaction route ID format", validationErr) + libOpentelemetry.HandleSpanBusinessErrorEvent(span, "Invalid transaction route ID format", validationErr) - logger.Log(ctx, libLog.LevelWarn, fmt.Sprintf("Invalid transaction route ID format: %v", err)) + logger.Log(ctx, libLog.LevelWarn, fmt.Sprintf("Invalid transaction route ID format: %v", err)) - return nil, validationErr + return nil, validationErr + } + } else { + err := pkg.ValidateBusinessError(constant.ErrTransactionRouteNotInformed, "") + libOpentelemetry.HandleSpanBusinessErrorEvent(span, "Transaction route is empty", err) + + logger.Log(ctx, libLog.LevelWarn, "Transaction route is empty") + + return nil, err } transactionRouteCache, err := uc.GetOrCreateTransactionRouteCache(ctx, organizationID, ledgerID, transactionRouteID) diff --git a/components/transaction/migrations/000027_add_route_id_to_transaction.down.sql b/components/transaction/migrations/000027_add_route_id_to_transaction.down.sql new file mode 100644 index 000000000..609593fd6 --- /dev/null +++ b/components/transaction/migrations/000027_add_route_id_to_transaction.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE "transaction" DROP CONSTRAINT IF EXISTS fk_transaction_route_id; +ALTER TABLE "transaction" DROP COLUMN IF EXISTS route_id; diff --git a/components/transaction/migrations/000027_add_route_id_to_transaction.up.sql b/components/transaction/migrations/000027_add_route_id_to_transaction.up.sql new file mode 100644 index 000000000..40cee05a4 --- /dev/null +++ b/components/transaction/migrations/000027_add_route_id_to_transaction.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE "transaction" ADD COLUMN IF NOT EXISTS route_id UUID; +ALTER TABLE "transaction" DROP CONSTRAINT IF EXISTS fk_transaction_route_id; +ALTER TABLE "transaction" ADD CONSTRAINT fk_transaction_route_id FOREIGN KEY (route_id) REFERENCES transaction_route(id) NOT VALID; diff --git a/components/transaction/migrations/000028_create_idx_transaction_route_id.down.sql b/components/transaction/migrations/000028_create_idx_transaction_route_id.down.sql new file mode 100644 index 000000000..54914253a --- /dev/null +++ b/components/transaction/migrations/000028_create_idx_transaction_route_id.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS idx_transaction_route_id; diff --git a/components/transaction/migrations/000028_create_idx_transaction_route_id.up.sql b/components/transaction/migrations/000028_create_idx_transaction_route_id.up.sql new file mode 100644 index 000000000..8ea1f87ae --- /dev/null +++ b/components/transaction/migrations/000028_create_idx_transaction_route_id.up.sql @@ -0,0 +1 @@ +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_transaction_route_id ON "transaction" (route_id) WHERE route_id IS NOT NULL; diff --git a/components/transaction/migrations/000029_validate_fk_transaction_route_id.down.sql b/components/transaction/migrations/000029_validate_fk_transaction_route_id.down.sql new file mode 100644 index 000000000..b442aff49 --- /dev/null +++ b/components/transaction/migrations/000029_validate_fk_transaction_route_id.down.sql @@ -0,0 +1,3 @@ +-- VALIDATE CONSTRAINT is idempotent and non-destructive; nothing to undo. +-- The constraint remains valid after rollback. Re-running 000027 with NOT VALID +-- would re-create it as not-yet-validated if a full rollback is needed. diff --git a/components/transaction/migrations/000029_validate_fk_transaction_route_id.up.sql b/components/transaction/migrations/000029_validate_fk_transaction_route_id.up.sql new file mode 100644 index 000000000..0232e3c52 --- /dev/null +++ b/components/transaction/migrations/000029_validate_fk_transaction_route_id.up.sql @@ -0,0 +1 @@ +ALTER TABLE "transaction" VALIDATE CONSTRAINT fk_transaction_route_id; diff --git a/pkg/transaction/transaction.go b/pkg/transaction/transaction.go index 7f5d5f168..78c0761d5 100644 --- a/pkg/transaction/transaction.go +++ b/pkg/transaction/transaction.go @@ -46,6 +46,7 @@ type Responses struct { Aliases []string Pending bool TransactionRoute string + TransactionRouteID *string OperationRoutesFrom map[string]string OperationRoutesTo map[string]string } @@ -180,8 +181,10 @@ type Transaction struct { Code string `json:"code,omitempty" example:"00000000-0000-0000-0000-000000000000"` Pending bool `json:"pending,omitempty" example:"false"` Metadata map[string]any `json:"metadata,omitempty" validate:"dive,keys,keymax=100,endkeys,nonested,valuemax=2000"` - // Deprecated: legacy route identifier, duplicates the operation route UUID. Prefer routeId on FromTo entries instead. - Route string `json:"route,omitempty" validate:"omitempty,max=250" example:"00000000-0000-0000-0000-000000000000"` + // Deprecated: legacy route identifier, contains the transaction route UUID as a string. Use routeId instead. + Route string `json:"route,omitempty" validate:"omitempty,max=250" example:"00000000-0000-0000-0000-000000000000"` + // UUID of the transaction route. Primary field replacing the deprecated Route string. + RouteID *string `json:"routeId,omitempty" validate:"omitempty,uuid" example:"00000000-0000-0000-0000-000000000000"` TransactionDate *TransactionDate `json:"transactionDate,omitempty" example:"2021-01-01T00:00:00Z"` Send Send `json:"send" validate:"required"` } // @name TransactionInput diff --git a/pkg/transaction/validations.go b/pkg/transaction/validations.go index 9101b0ce7..115268053 100644 --- a/pkg/transaction/validations.go +++ b/pkg/transaction/validations.go @@ -413,6 +413,7 @@ func ValidateSendSourceAndDistribute(ctx context.Context, transaction Transactio Aliases: make([]string, 0, sizeFrom+sizeTo), Pending: transaction.Pending, TransactionRoute: transaction.Route, + TransactionRouteID: transaction.RouteID, OperationRoutesFrom: make(map[string]string, sizeFrom), OperationRoutesTo: make(map[string]string, sizeTo), } diff --git a/postman/MIDAZ.postman_collection.json b/postman/MIDAZ.postman_collection.json index 7c1c061c6..129175935 100644 --- a/postman/MIDAZ.postman_collection.json +++ b/postman/MIDAZ.postman_collection.json @@ -55,7 +55,7 @@ { "listen": "test", "script": { - "id": "d17a29eb-af1e-425d-9af3-5365198c953b", + "id": "1f8fa2e5-3281-4829-8391-182a432d30c0", "exec": [ "\n// ===== STEP 1: Create Organization =====\nconsole.log(\"🔍 Executing Step 1: Create Organization\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_1_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_1\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"🏢 Business Logic: Organization has required fields\", function() {\n const jsonData = pm.response.json();\n let requestData = {};\n try {\n requestData = JSON.parse(pm.request.body.raw || '{}');\n } catch (e) {\n console.warn(\"⚠️ Could not parse request body as JSON; skipping request/response field equality checks\");\n }\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('legalName');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Organization ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Organization createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Organization updatedAt should be ISO timestamp\");\n \n // Data consistency validation\n if (requestData.legalName) {\n pm.expect(jsonData.legalName).to.equal(requestData.legalName, \"Response legalName should match request\");\n }\n \n // Store organization ID for subsequent requests\n pm.environment.set(\"organizationId\", jsonData.id);\n \n console.log(\"🏢 Organization creation validation passed:\", jsonData.legalName);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n console.log(\"💾 Stored organizationId:\", jsonData.id);\n});" ], @@ -65,7 +65,7 @@ { "listen": "prerequest", "script": { - "id": "9f9d6f23-dfe9-4f42-b9fb-6bc3a3bf4cd8", + "id": "592b6c4e-efa8-480f-9dc1-de8a6fd2252f", "exec": [ "\n// ===== PRE-REQUEST STEP 1: Create Organization =====\nconsole.log(\"⚙️ Setting up Step 1: Create Organization\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_1_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 1\");\n" ], @@ -123,7 +123,7 @@ { "listen": "test", "script": { - "id": "5c88b23c-feb0-48f6-a52e-58e7469c3f75", + "id": "fe0ff57d-2c9d-4c06-86ee-51c28ef95009", "exec": [ "\n// ===== STEP 2: Get Organization =====\nconsole.log(\"🔍 Executing Step 2: Get Organization\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_2_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_2\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -133,7 +133,7 @@ { "listen": "prerequest", "script": { - "id": "ba1c15f0-afeb-4a72-af6d-cbe8e93e26c8", + "id": "218f2f85-f51e-41f1-8648-d1ab69687836", "exec": [ "\n// ===== PRE-REQUEST STEP 2: Get Organization =====\nconsole.log(\"⚙️ Setting up Step 2: Get Organization\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_2_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 2\");\n" ], @@ -200,7 +200,7 @@ { "listen": "test", "script": { - "id": "c7ebd281-a5ce-4fc8-b92e-4c8d44bf2ecc", + "id": "13c36e6a-db34-4ca8-87db-5076595a96be", "exec": [ "\n// ===== STEP 3: Update Organization =====\nconsole.log(\"🔍 Executing Step 3: Update Organization\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_3_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_3\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -210,7 +210,7 @@ { "listen": "prerequest", "script": { - "id": "03e94d4f-d77f-4eb7-87ec-f0f50339fc4c", + "id": "b9ed4ddb-51c7-4cb3-aafe-0014f397a8c2", "exec": [ "\n// ===== PRE-REQUEST STEP 3: Update Organization =====\nconsole.log(\"⚙️ Setting up Step 3: Update Organization\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_3_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 3\");\n" ], @@ -310,7 +310,7 @@ { "listen": "test", "script": { - "id": "a708d59e-95c2-4ace-8e95-423e47b9d763", + "id": "0c6af983-ccb6-4d08-a53c-df5347ab9807", "exec": [ "\n// ===== STEP 4: List Organizations =====\nconsole.log(\"🔍 Executing Step 4: List Organizations\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_4_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_4\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -320,7 +320,7 @@ { "listen": "prerequest", "script": { - "id": "e23f713f-5820-4e9a-910b-34bcf5615d27", + "id": "8c65561f-fbe5-4081-90a3-4c96f8752b38", "exec": [ "\n// ===== PRE-REQUEST STEP 4: List Organizations =====\nconsole.log(\"⚙️ Setting up Step 4: List Organizations\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_4_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 4\");\n" ], @@ -388,7 +388,7 @@ { "listen": "test", "script": { - "id": "37b45520-091d-4e97-a5e9-cd4c81e4c622", + "id": "fe04e070-d9a2-431d-9c6d-5f6f41d60fb8", "exec": [ "\n// ===== STEP 5: Create Ledger =====\nconsole.log(\"🔍 Executing Step 5: Create Ledger\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_5_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_5\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"📒 Business Logic: Ledger has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping ledger validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n let requestData = {};\n try {\n requestData = JSON.parse(pm.request.body.raw || '{}');\n } catch (e) {\n console.warn(\"⚠️ Could not parse request body as JSON; skipping request/response field equality checks\");\n }\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('name');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Ledger ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Ledger createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Ledger updatedAt should be ISO timestamp\");\n \n // Store ledger ID for subsequent requests\n pm.environment.set(\"ledgerId\", jsonData.id);\n \n console.log(\"📒 Ledger creation validation passed:\", jsonData.name);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n console.log(\"💾 Stored ledgerId:\", jsonData.id);\n});" ], @@ -398,7 +398,7 @@ { "listen": "prerequest", "script": { - "id": "7546e296-7fa5-4fc1-b3ab-9073903c2ede", + "id": "b52f6b83-238c-406d-8f60-074f33abb49f", "exec": [ "\n// ===== PRE-REQUEST STEP 5: Create Ledger =====\nconsole.log(\"⚙️ Setting up Step 5: Create Ledger\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_5_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 5\");\n" ], @@ -463,7 +463,7 @@ { "listen": "test", "script": { - "id": "eed267fb-6ca3-4ea6-a20f-b066d39ddce8", + "id": "152ddeee-e9ad-4463-a11a-dd834b801a2d", "exec": [ "\n// ===== STEP 6: Get Ledger =====\nconsole.log(\"🔍 Executing Step 6: Get Ledger\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_6_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_6\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -473,7 +473,7 @@ { "listen": "prerequest", "script": { - "id": "0dcde366-a5ad-4716-b537-a8d2087575a6", + "id": "706a67e6-ca27-4825-be1c-c849aad6e57a", "exec": [ "\n// ===== PRE-REQUEST STEP 6: Get Ledger =====\nconsole.log(\"⚙️ Setting up Step 6: Get Ledger\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_6_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 6\");\n" ], @@ -547,7 +547,7 @@ { "listen": "test", "script": { - "id": "ec84ecb6-4861-452e-a441-ed5c9235cf7d", + "id": "7422ee12-f305-4b10-9852-54e3590f564c", "exec": [ "\n// ===== STEP 7: Update Ledger =====\nconsole.log(\"🔍 Executing Step 7: Update Ledger\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_7_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_7\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -557,7 +557,7 @@ { "listen": "prerequest", "script": { - "id": "0dde018b-7328-4599-a0e5-ec59ed659624", + "id": "d3580cf8-e446-4ec8-ba36-0fa4480f77e4", "exec": [ "\n// ===== PRE-REQUEST STEP 7: Update Ledger =====\nconsole.log(\"⚙️ Setting up Step 7: Update Ledger\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_7_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 7\");\n" ], @@ -660,7 +660,7 @@ { "listen": "test", "script": { - "id": "433775e5-38e0-471f-ac61-242dcf359a10", + "id": "d7a06f3b-69bc-4c6a-96c0-d630b6705c4d", "exec": [ "\n// ===== STEP 8: List Ledgers =====\nconsole.log(\"🔍 Executing Step 8: List Ledgers\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_8_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_8\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -670,7 +670,7 @@ { "listen": "prerequest", "script": { - "id": "75e569d5-5c70-4c45-9f1e-c25125f2e72a", + "id": "639c6702-4552-4016-b021-d2282f603fd9", "exec": [ "\n// ===== PRE-REQUEST STEP 8: List Ledgers =====\nconsole.log(\"⚙️ Setting up Step 8: List Ledgers\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_8_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 8\");\n" ], @@ -745,7 +745,7 @@ { "listen": "test", "script": { - "id": "d093476e-3751-4b2e-a31f-78d4901930ea", + "id": "b89d61e1-0b01-4b40-aa3e-684b96acbeae", "exec": [ "\n// ===== STEP 9: Create Asset =====\nconsole.log(\"🔍 Executing Step 9: Create Asset\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_9_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_9\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"💰 Business Logic: Asset has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping asset validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n let requestData = {};\n try {\n requestData = JSON.parse(pm.request.body.raw || '{}');\n } catch (e) {\n console.warn(\"⚠️ Could not parse request body as JSON; skipping request/response field equality checks\");\n }\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('name');\n pm.expect(jsonData).to.have.property('code');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Asset ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Asset createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Asset updatedAt should be ISO timestamp\");\n \n // Store asset ID for subsequent requests\n pm.environment.set(\"assetId\", jsonData.id);\n \n console.log(\"💰 Asset creation validation passed:\", jsonData.name);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n console.log(\"💾 Stored assetId:\", jsonData.id);\n});" ], @@ -755,7 +755,7 @@ { "listen": "prerequest", "script": { - "id": "635cf752-4d48-4903-8d62-6ef003b01ab3", + "id": "765eb939-f896-4569-83e7-5a51d86379f6", "exec": [ "\n// ===== PRE-REQUEST STEP 9: Create Asset =====\nconsole.log(\"⚙️ Setting up Step 9: Create Asset\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_9_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 9\");\n" ], @@ -827,7 +827,7 @@ { "listen": "test", "script": { - "id": "badb7282-cdc0-4244-81fb-983337310460", + "id": "f3c94a98-69e9-45df-bd55-57b3a3e93d43", "exec": [ "\n// ===== STEP 10: Get Asset =====\nconsole.log(\"🔍 Executing Step 10: Get Asset\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_10_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_10\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -837,7 +837,7 @@ { "listen": "prerequest", "script": { - "id": "f6e6ce3d-8ffd-478e-9a28-dc977bbf3119", + "id": "560648da-f456-4c8d-9cba-ad29ead3680a", "exec": [ "\n// ===== PRE-REQUEST STEP 10: Get Asset =====\nconsole.log(\"⚙️ Setting up Step 10: Get Asset\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_10_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 10\");\n" ], @@ -918,7 +918,7 @@ { "listen": "test", "script": { - "id": "ee98aa99-f60d-435b-93b4-893e98585395", + "id": "71d0c8a4-e872-4f33-8771-a187ec8da0ef", "exec": [ "\n// ===== STEP 11: Update Asset =====\nconsole.log(\"🔍 Executing Step 11: Update Asset\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_11_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_11\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -928,7 +928,7 @@ { "listen": "prerequest", "script": { - "id": "9a94a024-303c-4b9d-b553-227b7b96a36a", + "id": "bd639e30-c217-402a-b66a-3c291c5816eb", "exec": [ "\n// ===== PRE-REQUEST STEP 11: Update Asset =====\nconsole.log(\"⚙️ Setting up Step 11: Update Asset\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_11_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 11\");\n" ], @@ -1032,7 +1032,7 @@ { "listen": "test", "script": { - "id": "28d7a49e-32d8-497a-84e6-be947a2fc04e", + "id": "72d0e8f4-4a18-474b-a648-26d46215cceb", "exec": [ "\n// ===== STEP 12: List Assets =====\nconsole.log(\"🔍 Executing Step 12: List Assets\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_12_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_12\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1042,7 +1042,7 @@ { "listen": "prerequest", "script": { - "id": "44c41553-9545-403f-9495-402b6fc2bf8e", + "id": "848d7ed4-b310-4256-b0d3-f1f088a1c914", "exec": [ "\n// ===== PRE-REQUEST STEP 12: List Assets =====\nconsole.log(\"⚙️ Setting up Step 12: List Assets\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_12_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 12\");\n" ], @@ -1117,7 +1117,7 @@ { "listen": "test", "script": { - "id": "208c609d-f0ed-41c8-8765-d465e0e330cd", + "id": "f75c178b-6b47-49ef-b183-98df4e71c3c0", "exec": [ "\n// ===== STEP 13: Create Account =====\nconsole.log(\"🔍 Executing Step 13: Create Account\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_13_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_13\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"👤 Business Logic: Account has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping account validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n let requestData = {};\n try {\n requestData = JSON.parse(pm.request.body.raw || '{}');\n } catch (e) {\n console.warn(\"⚠️ Could not parse request body as JSON; skipping request/response field equality checks\");\n }\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('name');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Account ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Account createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Account updatedAt should be ISO timestamp\");\n \n // Store account ID and alias for subsequent requests\n pm.environment.set(\"accountId\", jsonData.id);\n if (jsonData.alias) {\n pm.environment.set(\"accountAlias\", jsonData.alias);\n console.log(\"💾 Stored accountAlias:\", jsonData.alias);\n }\n \n console.log(\"👤 Account creation validation passed:\", jsonData.name);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n console.log(\"💾 Stored accountId:\", jsonData.id);\n});" ], @@ -1127,7 +1127,7 @@ { "listen": "prerequest", "script": { - "id": "c8b08f54-3866-4f3e-af69-270346f1bee3", + "id": "a87c936f-d0c1-41db-808c-0ddf8b1a2e0f", "exec": [ "\n// ===== PRE-REQUEST STEP 13: Create Account =====\nconsole.log(\"⚙️ Setting up Step 13: Create Account\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_13_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 13\");\n" ], @@ -1199,7 +1199,7 @@ { "listen": "test", "script": { - "id": "b6433224-3557-4fbc-9e8b-fa40d713ecc8", + "id": "5638a0be-c47d-44ce-8c16-0bdd966d3b86", "exec": [ "\n// ===== STEP 14: Get Account =====\nconsole.log(\"🔍 Executing Step 14: Get Account\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_14_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_14\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1209,7 +1209,7 @@ { "listen": "prerequest", "script": { - "id": "ee8e6c8a-1c11-4f8d-85f7-a9868aac5652", + "id": "551f98b6-bfc5-4fcd-a5db-197b29549ac1", "exec": [ "\n// ===== PRE-REQUEST STEP 14: Get Account =====\nconsole.log(\"⚙️ Setting up Step 14: Get Account\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_14_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 14\");\n" ], @@ -1290,7 +1290,7 @@ { "listen": "test", "script": { - "id": "943e7e98-1b01-4690-aabc-d4ef79231e51", + "id": "3c67b461-c626-49db-bbd6-6a3b01f5ea00", "exec": [ "\n// ===== STEP 15: Update Account =====\nconsole.log(\"🔍 Executing Step 15: Update Account\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_15_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_15\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1300,7 +1300,7 @@ { "listen": "prerequest", "script": { - "id": "7707a395-6bb1-4e01-8318-ebc8a9d2ba54", + "id": "6795f4ad-aa16-44c4-bd27-8e8c66341a74", "exec": [ "\n// ===== PRE-REQUEST STEP 15: Update Account =====\nconsole.log(\"⚙️ Setting up Step 15: Update Account\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_15_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 15\");\n" ], @@ -1404,7 +1404,7 @@ { "listen": "test", "script": { - "id": "6a3b7bc6-91aa-4d78-a528-1bcd37296d47", + "id": "032a4d51-ffc7-48d8-ab28-f9c2247fafa1", "exec": [ "\n// ===== STEP 16: List Accounts =====\nconsole.log(\"🔍 Executing Step 16: List Accounts\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_16_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_16\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1414,7 +1414,7 @@ { "listen": "prerequest", "script": { - "id": "14113793-e731-4399-9ec7-965a549252d8", + "id": "5e71b753-d88a-40a3-ac5f-b66d2274e6e4", "exec": [ "\n// ===== PRE-REQUEST STEP 16: List Accounts =====\nconsole.log(\"⚙️ Setting up Step 16: List Accounts\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_16_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 16\");\n" ], @@ -1489,7 +1489,7 @@ { "listen": "test", "script": { - "id": "f5796f24-43d9-4425-a075-8046c687d78e", + "id": "b8ab1d90-67f0-4daf-bc0d-6a7275092dbc", "exec": [ "\n// ===== STEP 17: Create Portfolio =====\nconsole.log(\"🔍 Executing Step 17: Create Portfolio\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_17_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_17\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"📁 Business Logic: Portfolio has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping portfolio validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n let requestData = {};\n try {\n requestData = JSON.parse(pm.request.body.raw || '{}');\n } catch (e) {\n console.warn(\"⚠️ Could not parse request body as JSON; skipping request/response field equality checks\");\n }\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('name');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Portfolio ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Portfolio createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Portfolio updatedAt should be ISO timestamp\");\n \n // Store portfolio ID for subsequent requests\n pm.environment.set(\"portfolioId\", jsonData.id);\n \n console.log(\"📁 Portfolio creation validation passed:\", jsonData.name);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n console.log(\"💾 Stored portfolioId:\", jsonData.id);\n});" ], @@ -1499,7 +1499,7 @@ { "listen": "prerequest", "script": { - "id": "12a30ead-bf5d-4f3e-8418-27f1c807937e", + "id": "ee0ff049-b133-420e-a386-a03c6bd035af", "exec": [ "\n// ===== PRE-REQUEST STEP 17: Create Portfolio =====\nconsole.log(\"⚙️ Setting up Step 17: Create Portfolio\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_17_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 17\");\n" ], @@ -1571,7 +1571,7 @@ { "listen": "test", "script": { - "id": "e3931969-c925-4b00-aaeb-a0b472d3c658", + "id": "7f70607c-bdd9-477c-945f-c32799df882d", "exec": [ "\n// ===== STEP 18: Get Portfolio =====\nconsole.log(\"🔍 Executing Step 18: Get Portfolio\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_18_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_18\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1581,7 +1581,7 @@ { "listen": "prerequest", "script": { - "id": "392932f9-4b8e-41ad-9985-4f540623d193", + "id": "1455bc07-4b8d-4cc5-87fc-c90449b1e58a", "exec": [ "\n// ===== PRE-REQUEST STEP 18: Get Portfolio =====\nconsole.log(\"⚙️ Setting up Step 18: Get Portfolio\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_18_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 18\");\n" ], @@ -1662,7 +1662,7 @@ { "listen": "test", "script": { - "id": "2001b3c3-8603-4cf0-8a5a-096415d07b1c", + "id": "dbabb967-136b-489d-b2a5-2c3f26defba7", "exec": [ "\n// ===== STEP 19: Update Portfolio =====\nconsole.log(\"🔍 Executing Step 19: Update Portfolio\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_19_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_19\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1672,7 +1672,7 @@ { "listen": "prerequest", "script": { - "id": "d05ef5ef-fec5-4812-89fb-bf0a096e4ed7", + "id": "34f02159-5aa3-4670-a3af-524f8963b0e8", "exec": [ "\n// ===== PRE-REQUEST STEP 19: Update Portfolio =====\nconsole.log(\"⚙️ Setting up Step 19: Update Portfolio\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_19_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 19\");\n" ], @@ -1776,7 +1776,7 @@ { "listen": "test", "script": { - "id": "17072cc2-d51d-472f-8347-7b64212fa372", + "id": "ae4eed11-bc3f-464d-b9cb-971f41c683e4", "exec": [ "\n// ===== STEP 20: List Portfolios =====\nconsole.log(\"🔍 Executing Step 20: List Portfolios\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_20_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_20\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1786,7 +1786,7 @@ { "listen": "prerequest", "script": { - "id": "44b77108-d51f-42d6-933b-1f880e84148d", + "id": "886d5ab7-f3e3-413b-975c-9a37a6bfbd23", "exec": [ "\n// ===== PRE-REQUEST STEP 20: List Portfolios =====\nconsole.log(\"⚙️ Setting up Step 20: List Portfolios\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_20_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 20\");\n" ], @@ -1861,7 +1861,7 @@ { "listen": "test", "script": { - "id": "5b8a7320-a716-48a5-a7b5-be45c28218b4", + "id": "78120dfd-07b1-4e31-9fe3-d49690a62642", "exec": [ "\n// ===== STEP 21: Create Segment =====\nconsole.log(\"🔍 Executing Step 21: Create Segment\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_21_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_21\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"🏷️ Business Logic: Segment has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping segment validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n let requestData = {};\n try {\n requestData = JSON.parse(pm.request.body.raw || '{}');\n } catch (e) {\n console.warn(\"⚠️ Could not parse request body as JSON; skipping request/response field equality checks\");\n }\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('name');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Segment ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Segment createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Segment updatedAt should be ISO timestamp\");\n \n // Store segment ID for subsequent requests\n pm.environment.set(\"segmentId\", jsonData.id);\n \n console.log(\"🏷️ Segment creation validation passed:\", jsonData.name);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n console.log(\"💾 Stored segmentId:\", jsonData.id);\n});" ], @@ -1871,7 +1871,7 @@ { "listen": "prerequest", "script": { - "id": "44271091-1ca5-4fb0-baf2-1fde4b9c1a34", + "id": "5c81ffcc-bc5e-4e5b-a8b5-46acb18fc5be", "exec": [ "\n// ===== PRE-REQUEST STEP 21: Create Segment =====\nconsole.log(\"⚙️ Setting up Step 21: Create Segment\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_21_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 21\");\n" ], @@ -1943,7 +1943,7 @@ { "listen": "test", "script": { - "id": "a24859d2-ba39-4538-a02e-97a67979e05f", + "id": "07624a60-de75-48a5-b96b-d81e89cc7e32", "exec": [ "\n// ===== STEP 22: Get Segment =====\nconsole.log(\"🔍 Executing Step 22: Get Segment\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_22_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_22\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -1953,7 +1953,7 @@ { "listen": "prerequest", "script": { - "id": "751ce687-18d0-4d1c-bac1-4cba4c9ccd88", + "id": "2f31563e-673e-406d-b265-8e05a9470d4d", "exec": [ "\n// ===== PRE-REQUEST STEP 22: Get Segment =====\nconsole.log(\"⚙️ Setting up Step 22: Get Segment\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_22_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 22\");\n" ], @@ -2034,7 +2034,7 @@ { "listen": "test", "script": { - "id": "54fdfc10-696f-4589-9b82-324f9fcb1e50", + "id": "4c521dcf-ab05-4987-86bc-c454f76ff9a4", "exec": [ "\n// ===== STEP 23: Update Segment =====\nconsole.log(\"🔍 Executing Step 23: Update Segment\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_23_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_23\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -2044,7 +2044,7 @@ { "listen": "prerequest", "script": { - "id": "279072e5-1f6f-4202-9d7e-c7e3888960ac", + "id": "04e95de5-2d69-4707-b1ab-8f7bb31856e4", "exec": [ "\n// ===== PRE-REQUEST STEP 23: Update Segment =====\nconsole.log(\"⚙️ Setting up Step 23: Update Segment\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_23_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 23\");\n" ], @@ -2148,7 +2148,7 @@ { "listen": "test", "script": { - "id": "a8335720-0f39-43c4-875e-2692be439a26", + "id": "7b287ebc-8ceb-436d-937d-0e6243c14718", "exec": [ "\n// ===== STEP 24: List Segments =====\nconsole.log(\"🔍 Executing Step 24: List Segments\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_24_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_24\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -2158,7 +2158,7 @@ { "listen": "prerequest", "script": { - "id": "cd9c59ec-1333-4416-a5d4-85b61c496692", + "id": "f860089f-97b1-47a0-9b33-ccec54dcd55f", "exec": [ "\n// ===== PRE-REQUEST STEP 24: List Segments =====\nconsole.log(\"⚙️ Setting up Step 24: List Segments\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_24_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 24\");\n" ], @@ -2210,7 +2210,7 @@ { "listen": "test", "script": { - "id": "0d2df72d-f914-42b6-8981-5c72a28b2df3", + "id": "809386e5-38fa-4425-92fb-5a176d26ba78", "exec": [ "\n// ===== STEP 25: Count Organizations =====\nconsole.log(\"🔍 Executing Step 25: Count Organizations\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_25_start\", Date.now());\n\npm.test(\"✅ Status: HEAD request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ HEAD operation completed successfully - Count: \" + pm.response.headers.get(\"X-Total-Count\"));\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_25\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -2220,7 +2220,7 @@ { "listen": "prerequest", "script": { - "id": "03b7e5b6-3428-4e81-ab26-0d79a9fe76c3", + "id": "ceb45a3d-edc3-47ae-a56b-baa702ce5a27", "exec": [ "\n// ===== PRE-REQUEST STEP 25: Count Organizations =====\nconsole.log(\"⚙️ Setting up Step 25: Count Organizations\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_25_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 25\");\n" ], @@ -2281,7 +2281,7 @@ { "listen": "test", "script": { - "id": "f5f3fba8-cd28-420b-9ea1-474f71a76624", + "id": "fba2f136-7fac-4108-bf6b-6baddf1df2c0", "exec": [ "\n// ===== STEP 26: Count Ledgers =====\nconsole.log(\"🔍 Executing Step 26: Count Ledgers\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_26_start\", Date.now());\n\npm.test(\"✅ Status: HEAD request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ HEAD operation completed successfully - Count: \" + pm.response.headers.get(\"X-Total-Count\"));\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_26\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -2291,7 +2291,7 @@ { "listen": "prerequest", "script": { - "id": "aef4dd29-56d4-4a7c-b32e-35a5a253798d", + "id": "a01b6d87-011b-4d69-a735-996c3f65e232", "exec": [ "\n// ===== PRE-REQUEST STEP 26: Count Ledgers =====\nconsole.log(\"⚙️ Setting up Step 26: Count Ledgers\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_26_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 26\");\n" ], @@ -2359,7 +2359,7 @@ { "listen": "test", "script": { - "id": "ce6efaf7-da2b-4980-8d29-0d17f6f06c03", + "id": "445f0869-abcd-4376-9094-de9909584cc5", "exec": [ "\n// ===== STEP 27: Count Accounts =====\nconsole.log(\"🔍 Executing Step 27: Count Accounts\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_27_start\", Date.now());\n\npm.test(\"✅ Status: HEAD request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ HEAD operation completed successfully - Count: \" + pm.response.headers.get(\"X-Total-Count\"));\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_27\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -2369,7 +2369,7 @@ { "listen": "prerequest", "script": { - "id": "46549183-9c18-4f7c-804f-9f3f90cc9470", + "id": "62e0664e-8da3-43f0-abb4-a97b98a666c5", "exec": [ "\n// ===== PRE-REQUEST STEP 27: Count Accounts =====\nconsole.log(\"⚙️ Setting up Step 27: Count Accounts\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_27_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 27\");\n" ], @@ -2437,7 +2437,7 @@ { "listen": "test", "script": { - "id": "a20c6339-cc4d-4543-9001-ea25c42a0732", + "id": "63a5ace0-cb77-4389-91af-cad6c5b5a5c9", "exec": [ "\n// ===== STEP 28: Count Assets =====\nconsole.log(\"🔍 Executing Step 28: Count Assets\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_28_start\", Date.now());\n\npm.test(\"✅ Status: HEAD request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ HEAD operation completed successfully - Count: \" + pm.response.headers.get(\"X-Total-Count\"));\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_28\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -2447,7 +2447,7 @@ { "listen": "prerequest", "script": { - "id": "227d30e9-bdb6-4516-b658-c1c06412c831", + "id": "acc0005b-deea-4493-b46d-ceb58147d66a", "exec": [ "\n// ===== PRE-REQUEST STEP 28: Count Assets =====\nconsole.log(\"⚙️ Setting up Step 28: Count Assets\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_28_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 28\");\n" ], @@ -2515,7 +2515,7 @@ { "listen": "test", "script": { - "id": "1cd2b442-d890-4127-89b6-77a65b5b9e0e", + "id": "f11d84c8-be12-4ca8-bbd8-dac4cefd54e4", "exec": [ "\n// ===== STEP 29: Count Portfolios =====\nconsole.log(\"🔍 Executing Step 29: Count Portfolios\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_29_start\", Date.now());\n\npm.test(\"✅ Status: HEAD request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ HEAD operation completed successfully - Count: \" + pm.response.headers.get(\"X-Total-Count\"));\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_29\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -2525,7 +2525,7 @@ { "listen": "prerequest", "script": { - "id": "70aa527d-ea87-4f76-ab20-dba2bf9d87ee", + "id": "17e93da7-89ce-4156-862a-80b7ef8c0c8e", "exec": [ "\n// ===== PRE-REQUEST STEP 29: Count Portfolios =====\nconsole.log(\"⚙️ Setting up Step 29: Count Portfolios\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_29_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 29\");\n" ], @@ -2593,7 +2593,7 @@ { "listen": "test", "script": { - "id": "8c56835f-cb47-4545-a013-fbd491b17d3e", + "id": "891c2914-5485-42ab-aeb9-f160475b80ed", "exec": [ "\n// ===== STEP 30: Count Segments =====\nconsole.log(\"🔍 Executing Step 30: Count Segments\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_30_start\", Date.now());\n\npm.test(\"✅ Status: HEAD request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ HEAD operation completed successfully - Count: \" + pm.response.headers.get(\"X-Total-Count\"));\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_30\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -2603,7 +2603,7 @@ { "listen": "prerequest", "script": { - "id": "13756252-5e7b-41ce-935e-e22dc8d6ba27", + "id": "604375fe-fb08-4abd-aa8c-34c15c15e3d9", "exec": [ "\n// ===== PRE-REQUEST STEP 30: Count Segments =====\nconsole.log(\"⚙️ Setting up Step 30: Count Segments\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_30_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 30\");\n" ], @@ -2676,7 +2676,7 @@ { "listen": "test", "script": { - "id": "48d8d1ef-cf01-4d67-ba46-93cbdebce7f9", + "id": "de941435-8678-46c9-8501-1e8311b4fe3e", "exec": [ "\n// ===== STEP 31: Get Account by Alias =====\nconsole.log(\"🔍 Executing Step 31: Get Account by Alias\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_31_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_31\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -2686,7 +2686,7 @@ { "listen": "prerequest", "script": { - "id": "ce9d9c08-42ca-4389-8744-0c34b151b14b", + "id": "2e046e51-dd9e-4fa2-906a-150c55abd4c8", "exec": [ "\n// ===== PRE-REQUEST STEP 31: Get Account by Alias =====\nconsole.log(\"⚙️ Setting up Step 31: Get Account by Alias\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_31_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 31\");\n" ], @@ -2759,7 +2759,7 @@ { "listen": "test", "script": { - "id": "6a9f218c-5f96-41a5-9d85-0c4a920dc412", + "id": "acff8f0b-c990-4b91-92e7-496bb31d7ae4", "exec": [ "\n// ===== STEP 32: Get Account by External Code =====\nconsole.log(\"🔍 Executing Step 32: Get Account by External Code\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_32_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_32\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -2769,7 +2769,7 @@ { "listen": "prerequest", "script": { - "id": "91df7209-ba11-4b8f-b18a-1cfacd86d3b4", + "id": "014bf4d5-9d28-44b6-a2fd-8ab8e77156a7", "exec": [ "\n// ===== PRE-REQUEST STEP 32: Get Account by External Code =====\nconsole.log(\"⚙️ Setting up Step 32: Get Account by External Code\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_32_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 32\");\n" ], @@ -2851,7 +2851,7 @@ { "listen": "test", "script": { - "id": "1c9b3b0a-e1f8-40b5-834f-2694903fd525", + "id": "5fdc0461-adf5-433c-8155-ed96249e6a18", "exec": [ "\n// ===== STEP 33: Create Transaction =====\nconsole.log(\"🔍 Executing Step 33: Create Transaction\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_33_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_33\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"💸 Business Logic: Transaction has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping transaction validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Transaction ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Transaction createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Transaction updatedAt should be ISO timestamp\");\n \n // Determine transaction type and set appropriate variable names\n const currentUrl = pm.request.url.toString();\n let varPrefix = \"\";\n \n if (currentUrl.includes(\"/transactions/json\")) {\n varPrefix = \"\"; // Standard transaction - use original variable names\n } else if (currentUrl.includes(\"/transactions/dsl\")) {\n varPrefix = \"dsl\"; // DSL transaction\n } else if (currentUrl.includes(\"/transactions/inflow\")) {\n varPrefix = \"inflow\"; // Inflow transaction\n } else if (currentUrl.includes(\"/transactions/outflow\")) {\n varPrefix = \"outflow\"; // Outflow transaction\n }\n \n // Store transaction ID with appropriate prefix\n const transactionIdVar = varPrefix ? varPrefix + \"TransactionId\" : \"transactionId\";\n const operationIdVar = varPrefix ? varPrefix + \"OperationId\" : \"operationId\";\n const balanceIdVar = varPrefix ? varPrefix + \"BalanceId\" : \"balanceId\";\n const accountIdVar = varPrefix ? varPrefix + \"AccountId\" : \"accountId\";\n \n pm.environment.set(transactionIdVar, jsonData.id);\n console.log(\"💾 Stored \" + transactionIdVar + \":\", jsonData.id);\n \n // Extract operation and balance IDs if available\n if (jsonData.operations && jsonData.operations.length > 0) {\n // Find the user operation (non-external account) first\n const userOperation = jsonData.operations.find(op => \n op.accountAlias && !op.accountAlias.startsWith(\"@external/\") \n ) || jsonData.operations[0]; // fallback to first operation if no user account found\n \n // Extract operation ID from the user operation (not external account)\n if (userOperation && userOperation.id) {\n pm.environment.set(operationIdVar, userOperation.id);\n console.log(\"💾 Stored \" + operationIdVar + \" from user account:\", userOperation.id);\n console.log(\" Operation belongs to account:\", userOperation.accountAlias || userOperation.accountId);\n } else {\n // Fallback to first operation if no user operation found\n pm.environment.set(operationIdVar, jsonData.operations[0].id);\n console.log(\"⚠️ Stored \" + operationIdVar + \" from first operation (might be external):\", jsonData.operations[0].id);\n }\n \n // Extract and store accountId - prefer non-external accounts\n if (userOperation && userOperation.accountId) {\n // Only store if we do not already have an accountId for this variable (preserve treasury account ID)\n const existingAccountId = pm.environment.get(accountIdVar);\n if (!existingAccountId) {\n pm.environment.set(accountIdVar, userOperation.accountId);\n console.log(\"💾 Stored \" + accountIdVar + \":\", userOperation.accountId);\n } else {\n console.log(\"⚠️ Preserving existing \" + accountIdVar + \":\", existingAccountId, \"(not overwriting with:\", userOperation.accountId + \")\");\n }\n }\n \n // Extract balance ID from user operation as well\n if (userOperation && userOperation.balanceId) {\n pm.environment.set(balanceIdVar, userOperation.balanceId);\n console.log(\"💾 Stored \" + balanceIdVar + \" from user account:\", userOperation.balanceId);\n } else if (jsonData.operations[0].balanceId) {\n pm.environment.set(balanceIdVar, jsonData.operations[0].balanceId);\n console.log(\"⚠️ Stored \" + balanceIdVar + \" from first operation (might be external):\", jsonData.operations[0].balanceId);\n }\n }\n \n console.log(\"💸 Transaction creation validation passed:\", jsonData.id);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n});" ], @@ -2861,7 +2861,7 @@ { "listen": "prerequest", "script": { - "id": "cbf22a14-cf08-4233-adcb-5a8c15b0eb7b", + "id": "1ff8e4b6-ba79-4d47-846e-676651872584", "exec": [ "\n// ===== PRE-REQUEST STEP 33: Create Transaction =====\nconsole.log(\"⚙️ Setting up Step 33: Create Transaction\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_33_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 33\");\n" ], @@ -2937,7 +2937,7 @@ { "listen": "test", "script": { - "id": "0abaebe1-0c9a-43ca-a9bd-8f35cb8999d0", + "id": "eddeb7f9-3bcf-4fcf-a220-3231de38302c", "exec": [ "\n// ===== STEP 34: Create Transaction (Inflow) =====\nconsole.log(\"🔍 Executing Step 34: Create Transaction (Inflow)\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_34_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_34\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"💸 Business Logic: Transaction has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping transaction validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Transaction ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Transaction createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Transaction updatedAt should be ISO timestamp\");\n \n // Determine transaction type and set appropriate variable names\n const currentUrl = pm.request.url.toString();\n let varPrefix = \"\";\n \n if (currentUrl.includes(\"/transactions/json\")) {\n varPrefix = \"\"; // Standard transaction - use original variable names\n } else if (currentUrl.includes(\"/transactions/dsl\")) {\n varPrefix = \"dsl\"; // DSL transaction\n } else if (currentUrl.includes(\"/transactions/inflow\")) {\n varPrefix = \"inflow\"; // Inflow transaction\n } else if (currentUrl.includes(\"/transactions/outflow\")) {\n varPrefix = \"outflow\"; // Outflow transaction\n }\n \n // Store transaction ID with appropriate prefix\n const transactionIdVar = varPrefix ? varPrefix + \"TransactionId\" : \"transactionId\";\n const operationIdVar = varPrefix ? varPrefix + \"OperationId\" : \"operationId\";\n const balanceIdVar = varPrefix ? varPrefix + \"BalanceId\" : \"balanceId\";\n const accountIdVar = varPrefix ? varPrefix + \"AccountId\" : \"accountId\";\n \n pm.environment.set(transactionIdVar, jsonData.id);\n console.log(\"💾 Stored \" + transactionIdVar + \":\", jsonData.id);\n \n // Extract operation and balance IDs if available\n if (jsonData.operations && jsonData.operations.length > 0) {\n // Find the user operation (non-external account) first\n const userOperation = jsonData.operations.find(op => \n op.accountAlias && !op.accountAlias.startsWith(\"@external/\") \n ) || jsonData.operations[0]; // fallback to first operation if no user account found\n \n // Extract operation ID from the user operation (not external account)\n if (userOperation && userOperation.id) {\n pm.environment.set(operationIdVar, userOperation.id);\n console.log(\"💾 Stored \" + operationIdVar + \" from user account:\", userOperation.id);\n console.log(\" Operation belongs to account:\", userOperation.accountAlias || userOperation.accountId);\n } else {\n // Fallback to first operation if no user operation found\n pm.environment.set(operationIdVar, jsonData.operations[0].id);\n console.log(\"⚠️ Stored \" + operationIdVar + \" from first operation (might be external):\", jsonData.operations[0].id);\n }\n \n // Extract and store accountId - prefer non-external accounts\n if (userOperation && userOperation.accountId) {\n // Only store if we do not already have an accountId for this variable (preserve treasury account ID)\n const existingAccountId = pm.environment.get(accountIdVar);\n if (!existingAccountId) {\n pm.environment.set(accountIdVar, userOperation.accountId);\n console.log(\"💾 Stored \" + accountIdVar + \":\", userOperation.accountId);\n } else {\n console.log(\"⚠️ Preserving existing \" + accountIdVar + \":\", existingAccountId, \"(not overwriting with:\", userOperation.accountId + \")\");\n }\n }\n \n // Extract balance ID from user operation as well\n if (userOperation && userOperation.balanceId) {\n pm.environment.set(balanceIdVar, userOperation.balanceId);\n console.log(\"💾 Stored \" + balanceIdVar + \" from user account:\", userOperation.balanceId);\n } else if (jsonData.operations[0].balanceId) {\n pm.environment.set(balanceIdVar, jsonData.operations[0].balanceId);\n console.log(\"⚠️ Stored \" + balanceIdVar + \" from first operation (might be external):\", jsonData.operations[0].balanceId);\n }\n }\n \n console.log(\"💸 Transaction creation validation passed:\", jsonData.id);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n});" ], @@ -2947,7 +2947,7 @@ { "listen": "prerequest", "script": { - "id": "319b55f4-4732-48f0-b564-5c3eb4ddc93f", + "id": "2d3b35c4-ef3c-430f-b731-b8c55a271e7e", "exec": [ "\n// ===== PRE-REQUEST STEP 34: Create Transaction (Inflow) =====\nconsole.log(\"⚙️ Setting up Step 34: Create Transaction (Inflow)\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_34_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 34\");\n" ], @@ -3023,7 +3023,7 @@ { "listen": "test", "script": { - "id": "4c70d9fd-9b97-4e1c-820a-c1aacca646e0", + "id": "1d026eed-edd0-417f-90d6-1e9cc89629dc", "exec": [ "\n// ===== STEP 35: Create Transaction (Outflow) =====\nconsole.log(\"🔍 Executing Step 35: Create Transaction (Outflow)\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_35_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_35\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"💸 Business Logic: Transaction has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping transaction validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Transaction ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Transaction createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Transaction updatedAt should be ISO timestamp\");\n \n // Determine transaction type and set appropriate variable names\n const currentUrl = pm.request.url.toString();\n let varPrefix = \"\";\n \n if (currentUrl.includes(\"/transactions/json\")) {\n varPrefix = \"\"; // Standard transaction - use original variable names\n } else if (currentUrl.includes(\"/transactions/dsl\")) {\n varPrefix = \"dsl\"; // DSL transaction\n } else if (currentUrl.includes(\"/transactions/inflow\")) {\n varPrefix = \"inflow\"; // Inflow transaction\n } else if (currentUrl.includes(\"/transactions/outflow\")) {\n varPrefix = \"outflow\"; // Outflow transaction\n }\n \n // Store transaction ID with appropriate prefix\n const transactionIdVar = varPrefix ? varPrefix + \"TransactionId\" : \"transactionId\";\n const operationIdVar = varPrefix ? varPrefix + \"OperationId\" : \"operationId\";\n const balanceIdVar = varPrefix ? varPrefix + \"BalanceId\" : \"balanceId\";\n const accountIdVar = varPrefix ? varPrefix + \"AccountId\" : \"accountId\";\n \n pm.environment.set(transactionIdVar, jsonData.id);\n console.log(\"💾 Stored \" + transactionIdVar + \":\", jsonData.id);\n \n // Extract operation and balance IDs if available\n if (jsonData.operations && jsonData.operations.length > 0) {\n // Find the user operation (non-external account) first\n const userOperation = jsonData.operations.find(op => \n op.accountAlias && !op.accountAlias.startsWith(\"@external/\") \n ) || jsonData.operations[0]; // fallback to first operation if no user account found\n \n // Extract operation ID from the user operation (not external account)\n if (userOperation && userOperation.id) {\n pm.environment.set(operationIdVar, userOperation.id);\n console.log(\"💾 Stored \" + operationIdVar + \" from user account:\", userOperation.id);\n console.log(\" Operation belongs to account:\", userOperation.accountAlias || userOperation.accountId);\n } else {\n // Fallback to first operation if no user operation found\n pm.environment.set(operationIdVar, jsonData.operations[0].id);\n console.log(\"⚠️ Stored \" + operationIdVar + \" from first operation (might be external):\", jsonData.operations[0].id);\n }\n \n // Extract and store accountId - prefer non-external accounts\n if (userOperation && userOperation.accountId) {\n // Only store if we do not already have an accountId for this variable (preserve treasury account ID)\n const existingAccountId = pm.environment.get(accountIdVar);\n if (!existingAccountId) {\n pm.environment.set(accountIdVar, userOperation.accountId);\n console.log(\"💾 Stored \" + accountIdVar + \":\", userOperation.accountId);\n } else {\n console.log(\"⚠️ Preserving existing \" + accountIdVar + \":\", existingAccountId, \"(not overwriting with:\", userOperation.accountId + \")\");\n }\n }\n \n // Extract balance ID from user operation as well\n if (userOperation && userOperation.balanceId) {\n pm.environment.set(balanceIdVar, userOperation.balanceId);\n console.log(\"💾 Stored \" + balanceIdVar + \" from user account:\", userOperation.balanceId);\n } else if (jsonData.operations[0].balanceId) {\n pm.environment.set(balanceIdVar, jsonData.operations[0].balanceId);\n console.log(\"⚠️ Stored \" + balanceIdVar + \" from first operation (might be external):\", jsonData.operations[0].balanceId);\n }\n }\n \n console.log(\"💸 Transaction creation validation passed:\", jsonData.id);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n});" ], @@ -3033,7 +3033,7 @@ { "listen": "prerequest", "script": { - "id": "2499e2fd-e977-4035-a2bc-b63515c4b3e5", + "id": "b06a4099-1ffb-47b7-88ca-6ad94ee0ca54", "exec": [ "\n// ===== PRE-REQUEST STEP 35: Create Transaction (Outflow) =====\nconsole.log(\"⚙️ Setting up Step 35: Create Transaction (Outflow)\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_35_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 35\");\n" ], @@ -3105,7 +3105,7 @@ { "listen": "test", "script": { - "id": "c94a60a7-2932-437a-ba1b-a2b5adbe61e6", + "id": "a7c6c495-eeff-4aa0-95db-574daa6a7d53", "exec": [ "\n// ===== STEP 36: Get Transaction =====\nconsole.log(\"🔍 Executing Step 36: Get Transaction\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_36_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_36\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3115,7 +3115,7 @@ { "listen": "prerequest", "script": { - "id": "2ffdbce8-c60b-49bf-b929-ac3317ad1603", + "id": "5247e9ea-23e0-4316-9e84-35a2610b6815", "exec": [ "\n// ===== PRE-REQUEST STEP 36: Get Transaction =====\nconsole.log(\"⚙️ Setting up Step 36: Get Transaction\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_36_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 36\");\n" ], @@ -3196,7 +3196,7 @@ { "listen": "test", "script": { - "id": "c1323624-6373-4e3e-b37a-3bd9aa1ebef3", + "id": "f8ded1fb-c081-4aea-b88b-028ac81af7a5", "exec": [ "\n// ===== STEP 37: Update Transaction =====\nconsole.log(\"🔍 Executing Step 37: Update Transaction\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_37_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_37\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3206,7 +3206,7 @@ { "listen": "prerequest", "script": { - "id": "0ecb809c-ceec-49a4-b31a-186f4b4d44b4", + "id": "24744d10-c9b4-49d7-af73-ea3d17a8d2d6", "exec": [ "\n// ===== PRE-REQUEST STEP 37: Update Transaction =====\nconsole.log(\"⚙️ Setting up Step 37: Update Transaction\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_37_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 37\");\n" ], @@ -3304,7 +3304,7 @@ { "listen": "test", "script": { - "id": "bf31c57a-ef37-410b-9c0d-f2ae35071d11", + "id": "9252ed3c-0c5a-4265-8da9-a36d1a327217", "exec": [ "\n// ===== STEP 38: List Transactions =====\nconsole.log(\"🔍 Executing Step 38: List Transactions\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_38_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_38\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3314,7 +3314,7 @@ { "listen": "prerequest", "script": { - "id": "91f9f8e6-5610-4843-9969-744f3b1445ca", + "id": "260947c2-8d44-4e30-b65b-28ac025e4ca1", "exec": [ "\n// ===== PRE-REQUEST STEP 38: List Transactions =====\nconsole.log(\"⚙️ Setting up Step 38: List Transactions\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_38_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 38\");\n" ], @@ -3393,7 +3393,7 @@ { "listen": "test", "script": { - "id": "dc75041e-0737-440b-aae8-34ff21a8a5be", + "id": "95fdc9a1-4048-454f-b0ae-7ddcdfee707a", "exec": [ "\n// ===== STEP 39: Get Operation =====\nconsole.log(\"🔍 Executing Step 39: Get Operation\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_39_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_39\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3403,7 +3403,7 @@ { "listen": "prerequest", "script": { - "id": "fa0b7cc7-c465-4ae9-893b-0824f5085656", + "id": "1c9f8dde-2449-46ea-b637-b96a2edd9a45", "exec": [ "\n// ===== PRE-REQUEST STEP 39: Get Operation =====\nconsole.log(\"⚙️ Setting up Step 39: Get Operation\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_39_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 39\");\n" ], @@ -3526,7 +3526,7 @@ { "listen": "test", "script": { - "id": "24f04f4a-cb31-48d1-9a16-5e2e0fe5ca30", + "id": "c9ab72cf-dfbf-4aaa-a6cb-e454a9fe4463", "exec": [ "\n// ===== STEP 40: List Operations by Account =====\nconsole.log(\"🔍 Executing Step 40: List Operations by Account\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_40_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_40\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3536,7 +3536,7 @@ { "listen": "prerequest", "script": { - "id": "918b0544-ccc8-4553-a1e1-2624fd9ab755", + "id": "ac4f0d76-8505-4370-8a41-3e9fa665312d", "exec": [ "\n// ===== PRE-REQUEST STEP 40: List Operations by Account =====\nconsole.log(\"⚙️ Setting up Step 40: List Operations by Account\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_40_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 40\");\n" ], @@ -3624,7 +3624,7 @@ { "listen": "test", "script": { - "id": "8fc717d2-9c88-48a0-8f34-c93320b0d557", + "id": "73017a21-c9ed-4586-a362-c49bf1b32963", "exec": [ "\n// ===== STEP 41: Update Operation Metadata =====\nconsole.log(\"🔍 Executing Step 41: Update Operation Metadata\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_41_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_41\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3634,7 +3634,7 @@ { "listen": "prerequest", "script": { - "id": "c9458303-fd4a-4656-ba4e-aa995f913f86", + "id": "f63e4ab7-8c06-4482-8068-c0c4eddaab32", "exec": [ "\n// ===== PRE-REQUEST STEP 41: Update Operation Metadata =====\nconsole.log(\"⚙️ Setting up Step 41: Update Operation Metadata\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_41_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 41\");\n" ], @@ -3706,7 +3706,7 @@ { "listen": "test", "script": { - "id": "9134854f-74f1-4bde-afcd-f98df7cc3a77", + "id": "e7693fcf-2157-4860-aff8-ad10f76375cd", "exec": [ "\n// ===== STEP 42: Get Balance =====\nconsole.log(\"🔍 Executing Step 42: Get Balance\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_42_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_42\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3716,7 +3716,7 @@ { "listen": "prerequest", "script": { - "id": "e33f3f6f-68c0-45cb-b016-55da433a707e", + "id": "197d7667-38a1-452a-b73e-8e0daa5c1a5e", "exec": [ "\n// ===== PRE-REQUEST STEP 42: Get Balance =====\nconsole.log(\"⚙️ Setting up Step 42: Get Balance\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_42_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 42\");\n" ], @@ -3821,7 +3821,7 @@ { "listen": "test", "script": { - "id": "d0111e10-b610-42a9-b79f-6b2b1592f779", + "id": "ae015f80-d43f-4840-9a65-f24aa3e67bf8", "exec": [ "\n// ===== STEP 43: List Balances by Account =====\nconsole.log(\"🔍 Executing Step 43: List Balances by Account\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_43_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_43\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3831,7 +3831,7 @@ { "listen": "prerequest", "script": { - "id": "bb41702d-32b2-4496-8f1d-22c245987096", + "id": "88ff301a-dfa9-4b8d-8d38-c8da3c4cf89e", "exec": [ "\n// ===== PRE-REQUEST STEP 43: List Balances by Account =====\nconsole.log(\"⚙️ Setting up Step 43: List Balances by Account\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_43_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 43\");\n" ], @@ -3905,7 +3905,7 @@ { "listen": "test", "script": { - "id": "3525622e-f533-4fc4-acee-c905678f8307", + "id": "ab941e67-3d80-46b2-a8f9-fa1028df9dbb", "exec": [ "\n// ===== STEP 44: List Balances by Account Alias =====\nconsole.log(\"🔍 Executing Step 44: List Balances by Account Alias\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_44_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_44\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3915,7 +3915,7 @@ { "listen": "prerequest", "script": { - "id": "7e069567-e28e-4604-a163-ecb4f1bfae64", + "id": "a4f5dd81-c6cb-4754-9e6a-cb1b8f3733fc", "exec": [ "\n// ===== PRE-REQUEST STEP 44: List Balances by Account Alias =====\nconsole.log(\"⚙️ Setting up Step 44: List Balances by Account Alias\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_44_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 44\");\n" ], @@ -3989,7 +3989,7 @@ { "listen": "test", "script": { - "id": "421a604b-cead-4c4c-8a65-275bf6ca9796", + "id": "4cec447f-bff4-4246-9a84-4379fccfdb98", "exec": [ "\n// ===== STEP 45: List Balances by External Code =====\nconsole.log(\"🔍 Executing Step 45: List Balances by External Code\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_45_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_45\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -3999,7 +3999,7 @@ { "listen": "prerequest", "script": { - "id": "f68a9f09-a4ff-43ba-979b-b59ad72c0429", + "id": "30599b22-6b2b-46d1-bb2c-13ea5b7b5a1a", "exec": [ "\n// ===== PRE-REQUEST STEP 45: List Balances by External Code =====\nconsole.log(\"⚙️ Setting up Step 45: List Balances by External Code\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_45_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 45\");\n" ], @@ -4080,7 +4080,7 @@ { "listen": "test", "script": { - "id": "19b3d89b-5cf1-4422-8983-4da95e0dd9ea", + "id": "899e41cd-6581-41f7-bc84-d01ad4a780e3", "exec": [ "\n// ===== STEP 46: Update Balance =====\nconsole.log(\"🔍 Executing Step 46: Update Balance\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_46_start\", Date.now());\n\npm.test(\"✅ Status: PATCH request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ PATCH operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_46\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -4090,7 +4090,7 @@ { "listen": "prerequest", "script": { - "id": "2cbaf662-0faf-4cfc-811c-823055a39468", + "id": "316ba33f-4ab0-402c-ab97-40bd71cbd390", "exec": [ "\n// ===== PRE-REQUEST STEP 46: Update Balance =====\nconsole.log(\"⚙️ Setting up Step 46: Update Balance\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_46_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 46\");\n" ], @@ -4188,7 +4188,7 @@ { "listen": "test", "script": { - "id": "cf1b7c7e-b6cf-47c4-bfb8-3a1fa0c28432", + "id": "93f8e2c8-ba11-45b9-b975-9fe08dabc25d", "exec": [ "\n// ===== STEP 47: List All Balances =====\nconsole.log(\"🔍 Executing Step 47: List All Balances\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_47_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_47\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});" ], @@ -4198,7 +4198,7 @@ { "listen": "prerequest", "script": { - "id": "54f80b2c-39f7-43ab-b71f-06714a74b774", + "id": "d8576eaa-d1ba-4fa0-a15f-91162c43b650", "exec": [ "\n// ===== PRE-REQUEST STEP 47: List All Balances =====\nconsole.log(\"⚙️ Setting up Step 47: List All Balances\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_47_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 47\");\n" ], @@ -4272,7 +4272,7 @@ { "listen": "test", "script": { - "id": "7e30ad75-216d-4c53-92ca-1b290dcf5b3e", + "id": "6b9211c3-3311-44f0-a4a2-7607a4c53ad2", "exec": [ "\n// ===== STEP 48: Check Account Balance Before Zeroing =====\nconsole.log(\"🔍 Executing Step 48: Check Account Balance Before Zeroing\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_48_start\", Date.now());\n\npm.test(\"✅ Status: GET request successful (200)\", function () {\n pm.expect(pm.response.code).to.equal(200);\n console.log(\"✅ GET operation completed successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_48\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n// Extract balance information for zero-out transaction\nif (pm.response.code === 200) {\n const responseJson = pm.response.json();\n const debug = pm.environment.get(\"debug_logs\") === \"true\";\n\n // Log only item count to avoid leaking sensitive financial data in CI logs\n console.log(\"🏦 Balance items found:\", (responseJson.items || []).length);\n if (debug) {\n console.log(\"🔍 [DEBUG] Balance response keys:\", Object.keys(responseJson));\n }\n\n if (responseJson.items && responseJson.items.length > 0) {\n const balance = responseJson.items[0];\n if (balance.available !== undefined) {\n const balanceAmount = Math.abs(balance.available);\n pm.environment.set(\"currentBalanceAmount\", balanceAmount);\n if (debug) {\n console.log(\"💰 [DEBUG] Extracted balance amount:\", balanceAmount);\n } else {\n console.log(\"💰 Extracted balance amount (redacted)\");\n }\n console.log(\"✅ Balance amount variable set for zero-out transaction\");\n } else {\n console.warn(\"⚠️ No balance amount found in response\");\n pm.environment.set(\"currentBalanceAmount\", 0);\n }\n } else {\n console.warn(\"⚠️ No balance items found in response\");\n pm.environment.set(\"currentBalanceAmount\", 0);\n }\n}" ], @@ -4282,7 +4282,7 @@ { "listen": "prerequest", "script": { - "id": "5043b6fb-7e6b-4b82-b481-145a0a8e0353", + "id": "8115e619-5f01-4b77-9fbd-3bb065b274fe", "exec": [ "\n// ===== PRE-REQUEST STEP 48: Check Account Balance Before Zeroing =====\nconsole.log(\"⚙️ Setting up Step 48: Check Account Balance Before Zeroing\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_48_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 48\");\n" ], @@ -4364,7 +4364,7 @@ { "listen": "test", "script": { - "id": "1333bdc7-122c-472f-aaa5-c337395e470a", + "id": "57ce5404-7948-4fc0-a0a4-1d7548074630", "exec": [ "\n// ===== STEP 49: Zero Out Balance =====\nconsole.log(\"🔍 Executing Step 49: Zero Out Balance\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_49_start\", Date.now());\n\npm.test(\"✅ Status: POST request successful (200/201)\", function () {\n pm.expect(pm.response.code).to.be.oneOf([200, 201]);\n if (pm.response.code === 201) {\n console.log(\"✅ Resource created successfully\");\n } else {\n console.log(\"✅ POST operation completed successfully\");\n }\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_49\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});\n\npm.test(\"📋 Structure: Response has valid JSON structure\", function() {\n pm.response.to.be.json;\n \n const jsonData = pm.response.json();\n pm.expect(jsonData).to.be.an('object');\n \n // Log response structure for debugging\n console.log(\"📋 Response structure keys:\", Object.keys(jsonData));\n});\n\npm.test(\"💸 Business Logic: Transaction has required fields\", function() {\n // Only validate if response was successful\n if (pm.response.code !== 200 && pm.response.code !== 201) {\n console.log(\"⚠️ Skipping transaction validation - response code:\", pm.response.code);\n return;\n }\n \n const jsonData = pm.response.json();\n \n // Required fields validation\n pm.expect(jsonData).to.have.property('id');\n pm.expect(jsonData).to.have.property('status');\n pm.expect(jsonData).to.have.property('createdAt');\n pm.expect(jsonData).to.have.property('updatedAt');\n \n // UUID format validation\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n pm.expect(jsonData.id).to.match(uuidRegex, \"Transaction ID should be a valid UUID\");\n \n // ISO timestamp format validation\n const isoTimestampRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/;\n pm.expect(jsonData.createdAt).to.match(isoTimestampRegex, \"Transaction createdAt should be ISO timestamp\");\n pm.expect(jsonData.updatedAt).to.match(isoTimestampRegex, \"Transaction updatedAt should be ISO timestamp\");\n \n // Determine transaction type and set appropriate variable names\n const currentUrl = pm.request.url.toString();\n let varPrefix = \"\";\n \n if (currentUrl.includes(\"/transactions/json\")) {\n varPrefix = \"\"; // Standard transaction - use original variable names\n } else if (currentUrl.includes(\"/transactions/dsl\")) {\n varPrefix = \"dsl\"; // DSL transaction\n } else if (currentUrl.includes(\"/transactions/inflow\")) {\n varPrefix = \"inflow\"; // Inflow transaction\n } else if (currentUrl.includes(\"/transactions/outflow\")) {\n varPrefix = \"outflow\"; // Outflow transaction\n }\n \n // Store transaction ID with appropriate prefix\n const transactionIdVar = varPrefix ? varPrefix + \"TransactionId\" : \"transactionId\";\n const operationIdVar = varPrefix ? varPrefix + \"OperationId\" : \"operationId\";\n const balanceIdVar = varPrefix ? varPrefix + \"BalanceId\" : \"balanceId\";\n const accountIdVar = varPrefix ? varPrefix + \"AccountId\" : \"accountId\";\n \n pm.environment.set(transactionIdVar, jsonData.id);\n console.log(\"💾 Stored \" + transactionIdVar + \":\", jsonData.id);\n \n // Extract operation and balance IDs if available\n if (jsonData.operations && jsonData.operations.length > 0) {\n // Find the user operation (non-external account) first\n const userOperation = jsonData.operations.find(op => \n op.accountAlias && !op.accountAlias.startsWith(\"@external/\") \n ) || jsonData.operations[0]; // fallback to first operation if no user account found\n \n // Extract operation ID from the user operation (not external account)\n if (userOperation && userOperation.id) {\n pm.environment.set(operationIdVar, userOperation.id);\n console.log(\"💾 Stored \" + operationIdVar + \" from user account:\", userOperation.id);\n console.log(\" Operation belongs to account:\", userOperation.accountAlias || userOperation.accountId);\n } else {\n // Fallback to first operation if no user operation found\n pm.environment.set(operationIdVar, jsonData.operations[0].id);\n console.log(\"⚠️ Stored \" + operationIdVar + \" from first operation (might be external):\", jsonData.operations[0].id);\n }\n \n // Extract and store accountId - prefer non-external accounts\n if (userOperation && userOperation.accountId) {\n // Only store if we do not already have an accountId for this variable (preserve treasury account ID)\n const existingAccountId = pm.environment.get(accountIdVar);\n if (!existingAccountId) {\n pm.environment.set(accountIdVar, userOperation.accountId);\n console.log(\"💾 Stored \" + accountIdVar + \":\", userOperation.accountId);\n } else {\n console.log(\"⚠️ Preserving existing \" + accountIdVar + \":\", existingAccountId, \"(not overwriting with:\", userOperation.accountId + \")\");\n }\n }\n \n // Extract balance ID from user operation as well\n if (userOperation && userOperation.balanceId) {\n pm.environment.set(balanceIdVar, userOperation.balanceId);\n console.log(\"💾 Stored \" + balanceIdVar + \" from user account:\", userOperation.balanceId);\n } else if (jsonData.operations[0].balanceId) {\n pm.environment.set(balanceIdVar, jsonData.operations[0].balanceId);\n console.log(\"⚠️ Stored \" + balanceIdVar + \" from first operation (might be external):\", jsonData.operations[0].balanceId);\n }\n }\n \n console.log(\"💸 Transaction creation validation passed:\", jsonData.id);\n console.log(\"✅ Required fields verified, UUID and timestamp validation completed\");\n});" ], @@ -4374,7 +4374,7 @@ { "listen": "prerequest", "script": { - "id": "39b1f5b9-a38d-4efd-9236-04461ac1c4e9", + "id": "30018823-5bbf-43a2-b2e2-daf9b333e536", "exec": [ "\n// ===== PRE-REQUEST STEP 49: Zero Out Balance =====\nconsole.log(\"⚙️ Setting up Step 49: Zero Out Balance\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_49_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 49\");\n" ], @@ -4446,7 +4446,7 @@ { "listen": "test", "script": { - "id": "0ab3163d-9f09-4a31-a23d-01cd1d2b664b", + "id": "1459fcc2-38c8-4861-aa85-ba072314081e", "exec": [ "\n// ===== STEP 50: Delete Balance =====\nconsole.log(\"🔍 Executing Step 50: Delete Balance\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_50_start\", Date.now());\n\npm.test(\"✅ Status: DELETE request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ Resource deleted successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_50\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -4456,7 +4456,7 @@ { "listen": "prerequest", "script": { - "id": "106e1da6-32db-4f5a-9a57-b80f0c5ec0a5", + "id": "ed457e42-4855-4048-a41f-c485ee50337a", "exec": [ "\n// ===== PRE-REQUEST STEP 50: Delete Balance =====\nconsole.log(\"⚙️ Setting up Step 50: Delete Balance\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_50_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 50\");\n" ], @@ -4528,7 +4528,7 @@ { "listen": "test", "script": { - "id": "1ca462ce-78c9-4dc8-96d4-1ad4b743e04b", + "id": "c024d8ce-93d1-4b48-ba94-6566ef2cf865", "exec": [ "\n// ===== STEP 51: Delete Segment =====\nconsole.log(\"🔍 Executing Step 51: Delete Segment\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_51_start\", Date.now());\n\npm.test(\"✅ Status: DELETE request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ Resource deleted successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_51\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -4538,7 +4538,7 @@ { "listen": "prerequest", "script": { - "id": "9d99dd00-0908-4052-834a-2128d1af3adb", + "id": "67fecad7-c53f-45a1-8b6c-0611c1818f46", "exec": [ "\n// ===== PRE-REQUEST STEP 51: Delete Segment =====\nconsole.log(\"⚙️ Setting up Step 51: Delete Segment\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_51_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 51\");\n" ], @@ -4610,7 +4610,7 @@ { "listen": "test", "script": { - "id": "cad005e0-a163-40cf-abfc-ae9fefdbdb59", + "id": "e3d444a1-2776-4f4c-8c0c-4656b4d21032", "exec": [ "\n// ===== STEP 52: Delete Portfolio =====\nconsole.log(\"🔍 Executing Step 52: Delete Portfolio\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_52_start\", Date.now());\n\npm.test(\"✅ Status: DELETE request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ Resource deleted successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_52\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -4620,7 +4620,7 @@ { "listen": "prerequest", "script": { - "id": "809e9d52-e20a-41d9-bfca-7b1ddcb84fd8", + "id": "668c9c80-40e5-4421-b229-ff1461dae269", "exec": [ "\n// ===== PRE-REQUEST STEP 52: Delete Portfolio =====\nconsole.log(\"⚙️ Setting up Step 52: Delete Portfolio\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_52_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 52\");\n" ], @@ -4692,7 +4692,7 @@ { "listen": "test", "script": { - "id": "e5f61e31-3110-451f-9f66-d115b6bd073c", + "id": "f37c1dc7-6f8f-48b0-a236-d0167da4e84d", "exec": [ "\n// ===== STEP 53: Delete Account =====\nconsole.log(\"🔍 Executing Step 53: Delete Account\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_53_start\", Date.now());\n\npm.test(\"✅ Status: DELETE request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ Resource deleted successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_53\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -4702,7 +4702,7 @@ { "listen": "prerequest", "script": { - "id": "b71d9f9b-134b-47aa-acaf-cd61366334ac", + "id": "685dd245-99ad-456b-9cf2-2e1b972cdf1a", "exec": [ "\n// ===== PRE-REQUEST STEP 53: Delete Account =====\nconsole.log(\"⚙️ Setting up Step 53: Delete Account\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_53_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 53\");\n" ], @@ -4774,7 +4774,7 @@ { "listen": "test", "script": { - "id": "30d30eda-1f02-4308-a2c3-645e15828258", + "id": "4f71b1e6-3f1c-43b8-96f0-77f33d288b0a", "exec": [ "\n// ===== STEP 54: Delete Asset =====\nconsole.log(\"🔍 Executing Step 54: Delete Asset\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_54_start\", Date.now());\n\npm.test(\"✅ Status: DELETE request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ Resource deleted successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_54\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -4784,7 +4784,7 @@ { "listen": "prerequest", "script": { - "id": "45dc6da1-5c2c-478c-b3ab-2d6b215b4d86", + "id": "f29f65f8-788f-4a19-9032-caada20b7bbc", "exec": [ "\n// ===== PRE-REQUEST STEP 54: Delete Asset =====\nconsole.log(\"⚙️ Setting up Step 54: Delete Asset\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_54_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 54\");\n" ], @@ -4849,7 +4849,7 @@ { "listen": "test", "script": { - "id": "ee53ab7c-8677-4a95-aaa2-95b8a72a97a3", + "id": "54e02757-5343-4cb2-ab0f-d3a39f718a43", "exec": [ "\n// ===== STEP 55: Delete Ledger =====\nconsole.log(\"🔍 Executing Step 55: Delete Ledger\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_55_start\", Date.now());\n\npm.test(\"✅ Status: DELETE request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ Resource deleted successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_55\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -4859,7 +4859,7 @@ { "listen": "prerequest", "script": { - "id": "16ce7cb1-26bd-4b99-8239-4aeab2dedfd0", + "id": "dd25ac80-6836-4cb0-807b-ff81f4fe8900", "exec": [ "\n// ===== PRE-REQUEST STEP 55: Delete Ledger =====\nconsole.log(\"⚙️ Setting up Step 55: Delete Ledger\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_55_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 55\");\n" ], @@ -4917,7 +4917,7 @@ { "listen": "test", "script": { - "id": "79493434-b496-4aae-a5e2-ce9749880bb3", + "id": "e40f5b4a-19ab-4ea6-b56b-0c52ad844b61", "exec": [ "\n// ===== STEP 56: Delete Organization =====\nconsole.log(\"🔍 Executing Step 56: Delete Organization\");\n\n// Record start time for performance tracking\npm.globals.set(\"step_56_start\", Date.now());\n\npm.test(\"✅ Status: DELETE request successful (204)\", function () {\n pm.expect(pm.response.code).to.equal(204);\n console.log(\"✅ Resource deleted successfully\");\n});\n\npm.test(\"⚡ Performance: Response time acceptable\", function () {\n const responseTime = pm.response.responseTime;\n const maxTime = pm.environment.get(\"max_response_time\") || 5000;\n \n pm.expect(responseTime).to.be.below(maxTime);\n console.log(\"⚡ Response time: \" + responseTime + \"ms (max: \" + maxTime + \"ms)\");\n \n // Track performance for regression detection\n const perfKey = \"perf_step_56\";\n const previousTime = pm.environment.get(perfKey);\n pm.environment.set(perfKey, responseTime);\n \n if (previousTime) {\n const increase = ((responseTime - previousTime) / previousTime) * 100;\n if (increase > 50) {\n console.warn(\"⚠️ Performance regression: \" + increase.toFixed(1) + \"% slower than previous run\");\n }\n }\n});" ], @@ -4927,7 +4927,7 @@ { "listen": "prerequest", "script": { - "id": "7867396d-7a98-4faa-aa8f-280280465258", + "id": "88d59f72-bfb9-4350-beed-94bb385d5ab1", "exec": [ "\n// ===== PRE-REQUEST STEP 56: Delete Organization =====\nconsole.log(\"⚙️ Setting up Step 56: Delete Organization\");\n\n// Set step start timestamp for performance tracking\npm.globals.set(\"step_56_start\", Date.now());\n\n// Generate unique idempotency key for each POST/PUT/PATCH request\nif (['POST', 'PUT', 'PATCH'].includes(pm.request.method)) {\n // Always generate a new unique idempotency key for each transaction\n const newIdempotencyKey = pm.variables.replaceIn('{{$guid}}');\n pm.environment.set('idempotencyKey', newIdempotencyKey);\n console.log('🔑 Generated new idempotency key:', newIdempotencyKey);\n}\n\n// Check for required variables based on operation type\nconst requestUrl = pm.request.url.toString();\nconst method = pm.request.method;\n\n// Parse URL path segments for more robust variable detection\n// pm.request.url.path is an array like [\"v1\", \"organizations\", \"{{organizationId}}\", \"ledgers\"]\nconst pathSegments = pm.request.url.path || [];\n\n// Helper: Check if a path segment is a template variable for a specific ID\nfunction hasTemplateVar(segments, varPatterns) {\n return segments.some(seg => varPatterns.some(pattern => seg.includes(pattern)));\n}\n\n// Helper: Check if resource ID is in a path position (after the resource name, not just listed)\n// e.g., /organizations/{{id}}/ledgers requires organizationId, but /organizations does not\nfunction requiresResourceId(segments, resourceName, varPatterns) {\n const resourceIndex = segments.findIndex(seg => seg === resourceName);\n if (resourceIndex === -1) return false;\n // Check if next segment is a variable pattern\n const nextSegment = segments[resourceIndex + 1];\n if (!nextSegment) return false;\n return varPatterns.some(pattern => nextSegment.includes(pattern));\n}\n\n// Base required variables - start empty and add based on URL patterns\nconst requiredVars = [];\n\n// Require organizationId when URL has it as a path parameter (not for list/create endpoints)\nconst orgIdPatterns = ['{{organizationId}}', '{organization_id}', ':organization_id'];\nif (hasTemplateVar(pathSegments, orgIdPatterns) || requiresResourceId(pathSegments, 'organizations', orgIdPatterns)) {\n requiredVars.push('organizationId');\n}\n\n// Require ledgerId when URL has it as a path parameter (not for list/create ledger endpoints)\nconst ledgerIdPatterns = ['{{ledgerId}}', '{ledger_id}', ':ledger_id'];\nif (hasTemplateVar(pathSegments, ledgerIdPatterns) || requiresResourceId(pathSegments, 'ledgers', ledgerIdPatterns)) {\n requiredVars.push('ledgerId');\n}\n\n// Add specific variables based on the operation and URL path\nconst transactionIdPatterns = ['{{transactionId}}', '{transaction_id}', ':transaction_id'];\nif (hasTemplateVar(pathSegments, transactionIdPatterns) || requiresResourceId(pathSegments, 'transactions', transactionIdPatterns)) {\n requiredVars.push('transactionId');\n}\n\nconst operationIdPatterns = ['{{operationId}}', '{operation_id}', ':operation_id'];\nif (hasTemplateVar(pathSegments, operationIdPatterns) || requiresResourceId(pathSegments, 'operations', operationIdPatterns)) {\n requiredVars.push('operationId');\n}\n\nconst balanceIdPatterns = ['{{balanceId}}', '{balance_id}', ':balance_id'];\nif (hasTemplateVar(pathSegments, balanceIdPatterns) || requiresResourceId(pathSegments, 'balances', balanceIdPatterns)) {\n requiredVars.push('balanceId');\n}\n\nconst accountIdPatterns = ['{{accountId}}', '{account_id}', ':account_id'];\nif (hasTemplateVar(pathSegments, accountIdPatterns) || requiresResourceId(pathSegments, 'accounts', accountIdPatterns)) {\n requiredVars.push('accountId');\n}\n\n// Check all required variables\nlet hasAllRequiredVars = true;\nrequiredVars.forEach(varName => {\n const value = pm.environment.get(varName);\n if (!value || value === '') {\n console.error(\"❌ CRITICAL: Required environment variable '\" + varName + \"' is not set for \" + method + \" operation\");\n hasAllRequiredVars = false;\n } else {\n console.log(\"✅ \" + varName + \": \" + value);\n }\n});\n\nif (!hasAllRequiredVars) {\n console.error(\"❌ Missing required variables - this request will likely fail with 404/405 error\");\n console.log(\"💡 Suggestion: Ensure previous steps completed successfully and extracted required IDs\");\n}\n\nconsole.log(\"✅ Pre-request setup completed for Step 56\");\n" ], @@ -4961,7 +4961,7 @@ { "listen": "test", "script": { - "id": "cb33f243-e4aa-4555-9670-a5c1cc7a990b", + "id": "548e0b3c-5041-4c98-a6f6-638d6d0f659f", "exec": [ "\n// ===== WORKFLOW SUMMARY =====\nconsole.log(\"📊 Workflow Execution Summary\");\nconsole.log(\"Total Steps: 56\");\n\n// Calculate total execution time\nconst startTime = pm.globals.get(\"workflow_start_time\");\nif (startTime) {\n const totalTime = Date.now() - startTime;\n console.log(\"⏱️ Total Execution Time: \" + totalTime + \"ms\");\n pm.globals.set(\"workflow_total_time\", totalTime);\n}\n\n// Summary of step performance\nconsole.log(\"📈 Performance Summary:\");\nfor (let i = 1; i <= 56; i++) {\n const stepTime = pm.environment.get(\"perf_step_\" + i);\n if (stepTime) {\n console.log(\" Step \" + i + \": \" + stepTime + \"ms\");\n }\n}\n\nconsole.log(\"✅ Workflow completed successfully\");\n" ], @@ -4971,7 +4971,7 @@ ] } ], - "_postman_id": "5da914f7-2bcf-4c3c-a379-ab8a8cfdd087", + "_postman_id": "89c1f70a-2110-47a5-805d-60ede62b12a0", "event": [] }, {