{
  "info": {
    "name": "Toolshop API",
    "description": "API test suite for Toolshop (Practice Software Testing, sprint5). Folders follow the run order in PLAN-DE-RULARE.md; each request is named after its test case (T-XXX).",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "00 Setup",
      "item": [
        {
          "name": "S1 · 1 register A",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const runId = Date.now();",
                  "pm.environment.set(\"runId\", runId);",
                  "pm.environment.set(\"emailA\", \"qa+a-\" + runId + \"@test.local\");",
                  "pm.environment.set(\"passwordA\", \"Qa-Test!2026#A\" + runId);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Register A: 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"userIdA\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"UserA\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S1 · 2 login A",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Login A: 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenA\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S2 · 1 register B",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const runId = pm.environment.get(\"runId\");",
                  "pm.environment.set(\"emailB\", \"qa+b-\" + runId + \"@test.local\");",
                  "pm.environment.set(\"passwordB\", \"Qa-Test!2026#B\" + runId);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Register B: 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"userIdB\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"UserB\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{emailB}}\",\n  \"password\": \"{{passwordB}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S2 · 2 login B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Login B: 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenB\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailB}}\",\n  \"password\": \"{{passwordB}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S3 · login admin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Login admin: 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenAdmin\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{adminEmail}}\",\n  \"password\": \"{{adminPassword}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S3b · products",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Products: 200\", () => pm.response.to.have.status(200));",
                  "const p = pm.response.json().data;",
                  "// The first two products that are not rentals (rental discounts would complicate the totals).",
                  "const buy = p.filter(x => !x.is_rental);",
                  "pm.environment.set(\"productId\", buy[0].id);",
                  "pm.environment.set(\"productPrice\", buy[0].price);",
                  "pm.environment.set(\"productId2\", buy[1].id);",
                  "pm.environment.set(\"productPrice2\", buy[1].price);",
                  "pm.environment.set(\"categoryId\", buy[0].category.id);",
                  "pm.environment.set(\"brandId\", buy[0].brand.id);",
                  "pm.environment.set(\"productImageId\", buy[0].product_image.id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products"
          }
        },
        {
          "name": "S4 · 1 create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Cart created: 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartId\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S4 · 2 add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Product added: 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S5 · 1 address lookup",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Lookup: 200\", () => pm.response.to.have.status(200));",
                  "const a = pm.response.json();",
                  "pm.environment.set(\"billingCity\", a.city);",
                  "pm.environment.set(\"billingState\", a.state ?? \"\");"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/postcode-lookup?country=NL&postcode=1011%20AB"
          }
        },
        {
          "name": "S5 · 2 order A",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Order: 201\", () => pm.response.to.have.status(201));",
                  "const inv = pm.response.json();",
                  "pm.environment.set(\"invoiceIdA\", inv.id);",
                  "pm.environment.set(\"invoiceNumberA\", inv.invoice_number);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S5 · 3 create cart B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Cart B created: 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartIdB\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S5 · 4 add product B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Product added: 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartIdB}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId2}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "S5 · 5 order B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Order B: 201\", () => pm.response.to.have.status(201));",
                  "const inv = pm.response.json();",
                  "pm.environment.set(\"invoiceIdB\", inv.id);",
                  "pm.environment.set(\"invoiceNumberB\", inv.invoice_number);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartIdB}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "01 Environment",
      "item": [
        {
          "name": "T-K11 Status",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Has version, environment, app_name\", () => pm.expect(json).to.include.keys(\"version\", \"environment\", \"app_name\"));",
                  "console.log(\"Version under test:\", json.version, json.environment); // goes into the execution report"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/status"
          }
        }
      ]
    },
    {
      "name": "02 Catalog",
      "item": [
        {
          "name": "T-C01 Product list",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"data not empty\", () => pm.expect(json.data).to.be.an(\"array\").that.is.not.empty);",
                  "pm.test(\"Pagination fields\", () => [\"current_page\", \"last_page\", \"per_page\", \"total\"].forEach(k => pm.expect(json).to.have.property(k)));",
                  "pm.test(\"Each product has id, name, price\", () => json.data.forEach(p => pm.expect(p).to.include.keys(\"id\", \"name\", \"price\")));",
                  "// Used by T-C07 to check that \"%\" does not return the whole catalog.",
                  "pm.environment.set(\"productTotal\", json.total);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products"
          }
        },
        {
          "name": "T-C02 Product details",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Correct id\", () => pm.expect(json.id).to.eql(pm.environment.get(\"productId\")));",
                  "pm.test(\"Has name, price, category, brand\", () => pm.expect(json).to.include.keys(\"name\", \"price\", \"category\", \"brand\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}"
          }
        },
        {
          "name": "T-C03 Non-existent product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 404\", () => pm.response.to.have.status(404));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/01AAAAAAAAAAAAAAAAAAAAAAAA"
          }
        },
        {
          "name": "T-C04 Invalid id format · text",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"idLung\", \"x\".repeat(300));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/abc"
          }
        },
        {
          "name": "T-C04 Invalid id format · negative",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"idLung\", \"x\".repeat(300));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/-1"
          }
        },
        {
          "name": "T-C04 Invalid id format · SQL injection",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"idLung\", \"x\".repeat(300));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/%27%20OR%201%3D1%20--"
          }
        },
        {
          "name": "T-C04 Invalid id format · 300 characters",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"idLung\", \"x\".repeat(300));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{idLung}}"
          }
        },
        {
          "name": "T-C05 Search with matches",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Results found\", () => pm.expect(json.data).to.not.be.empty);",
                  "pm.test(\"Each name contains the term\", () => json.data.forEach(p => pm.expect(p.name.toLowerCase()).to.include(\"hammer\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=hammer"
          }
        },
        {
          "name": "T-C06 Search without matches",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Empty list\", () => pm.expect(json.data).to.be.an(\"array\").that.is.empty);",
                  "pm.test(\"total = 0\", () => pm.expect(json.total).to.eql(0));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=zzzxxyyqqq"
          }
        },
        {
          "name": "T-C07 Edge-case search · partial",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "const json = pm.response.json();",
                  "pm.test(\"Does not return the whole catalog\", () => pm.expect(json.total || 0).to.be.below(Number(pm.environment.get(\"productTotal\"))));",
                  "console.log(pm.info.requestName, \"→\", pm.response.code, \"results:\", json.total); // record in the exploration table"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=ham"
          }
        },
        {
          "name": "T-C07 Edge-case search · uppercase",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "const json = pm.response.json();",
                  "pm.test(\"Does not return the whole catalog\", () => pm.expect(json.total || 0).to.be.below(Number(pm.environment.get(\"productTotal\"))));",
                  "console.log(pm.info.requestName, \"→\", pm.response.code, \"results:\", json.total); // record in the exploration table"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=HAMMER"
          }
        },
        {
          "name": "T-C07 Edge-case search · diacritics",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "const json = pm.response.json();",
                  "pm.test(\"Does not return the whole catalog\", () => pm.expect(json.total || 0).to.be.below(Number(pm.environment.get(\"productTotal\"))));",
                  "console.log(pm.info.requestName, \"→\", pm.response.code, \"results:\", json.total); // record in the exploration table"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=hámmer"
          }
        },
        {
          "name": "T-C07 Edge-case search · percent sign",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "const json = pm.response.json();",
                  "pm.test(\"Does not return the whole catalog\", () => pm.expect(json.total || 0).to.be.below(Number(pm.environment.get(\"productTotal\"))));",
                  "console.log(pm.info.requestName, \"→\", pm.response.code, \"results:\", json.total); // record in the exploration table"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=%25"
          }
        },
        {
          "name": "T-C07 Edge-case search · underscore",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "const json = pm.response.json();",
                  "pm.test(\"Does not return the whole catalog\", () => pm.expect(json.total || 0).to.be.below(Number(pm.environment.get(\"productTotal\"))));",
                  "console.log(pm.info.requestName, \"→\", pm.response.code, \"results:\", json.total); // record in the exploration table"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=_"
          }
        },
        {
          "name": "T-C07 Edge-case search · script",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "const json = pm.response.json();",
                  "pm.test(\"Does not return the whole catalog\", () => pm.expect(json.total || 0).to.be.below(Number(pm.environment.get(\"productTotal\"))));",
                  "console.log(pm.info.requestName, \"→\", pm.response.code, \"results:\", json.total); // record in the exploration table"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=<script>"
          }
        },
        {
          "name": "T-C07 Edge-case search · empty",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "const json = pm.response.json();",
                  "pm.test(\"Does not return the whole catalog\", () => pm.expect(json.total || 0).to.be.below(Number(pm.environment.get(\"productTotal\"))));",
                  "console.log(pm.info.requestName, \"→\", pm.response.code, \"results:\", json.total); // record in the exploration table"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q="
          }
        },
        {
          "name": "T-C08 · 1 price ascending",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const prices = json.data.map(p => Number(p.price));",
                  "pm.test(\"Prices sorted ascending\", () => {",
                  "  for (let i = 1; i < prices.length; i++) pm.expect(prices[i]).to.be.at.least(prices[i - 1]);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?sort=price,asc"
          }
        },
        {
          "name": "T-C08 · 2 price descending",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const prices = json.data.map(p => Number(p.price));",
                  "pm.test(\"Prices sorted descending\", () => {",
                  "  for (let i = 1; i < prices.length; i++) pm.expect(prices[i]).to.be.at.most(prices[i - 1]);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?sort=price,desc"
          }
        },
        {
          "name": "T-C09 Full pagination",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "// Fetch the remaining pages from the script and check the count and uniqueness of ids at the end.",
                  "const ids = json.data.map(p => p.id);",
                  "const last = json.last_page, total = json.total;",
                  "let pending = last - 1;",
                  "function finish() {",
                  "  pm.test(\"Number of ids = total\", () => pm.expect(ids.length).to.eql(total));",
                  "  pm.test(\"No duplicates\", () => pm.expect(new Set(ids).size).to.eql(ids.length));",
                  "}",
                  "if (pending === 0) finish();",
                  "for (let page = 2; page <= last; page++) {",
                  "  pm.sendRequest(pm.environment.get(\"baseUrl\") + \"/products?page=\" + page, (err, res) => {",
                  "    res.json().data.forEach(p => ids.push(p.id));",
                  "    if (--pending === 0) finish();",
                  "  });",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?page=1"
          }
        },
        {
          "name": "T-C10 · 1 by category",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"All from the requested category\", () => pm.response.json().data.forEach(p => pm.expect(p.category.id).to.eql(pm.environment.get(\"categoryId\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?by_category={{categoryId}}"
          }
        },
        {
          "name": "T-C10 · 2 by brand",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"All from the requested brand\", () => pm.response.json().data.forEach(p => pm.expect(p.brand.id).to.eql(pm.environment.get(\"brandId\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?by_brand={{brandId}}"
          }
        },
        {
          "name": "T-C10 · 3 price range",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Prices between 10 and 30\", () => pm.response.json().data.forEach(p => pm.expect(Number(p.price)).to.be.within(10, 30)));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?between=price,10,30"
          }
        },
        {
          "name": "T-C11 Category tree",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "function check(node) {",
                  "  (node.sub_categories || []).forEach(c => {",
                  "    pm.expect(c.parent_id, \"parent of \" + c.name).to.eql(node.id);",
                  "    check(c);",
                  "  });",
                  "}",
                  "pm.test(\"parent_id correct across the tree\", () => json.forEach(check));",
                  "// For T-C12: the first root category that has subcategories.",
                  "const parent = json.find(c => (c.sub_categories || []).length > 0);",
                  "pm.environment.set(\"parentCategoryId\", parent.id);",
                  "pm.environment.set(\"parentChildIds\", JSON.stringify(parent.sub_categories.map(c => c.id).sort()));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/categories/tree"
          }
        },
        {
          "name": "T-C12 Subtree",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const node = Array.isArray(json) ? json[0] : json;",
                  "pm.test(\"Root has the requested id\", () => pm.expect(node.id).to.eql(pm.environment.get(\"parentCategoryId\")));",
                  "pm.test(\"Children match the full tree\", () => {",
                  "  const ids = (node.sub_categories || []).map(c => c.id).sort();",
                  "  pm.expect(JSON.stringify(ids)).to.eql(pm.environment.get(\"parentChildIds\"));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/categories/tree/{{parentCategoryId}}"
          }
        },
        {
          "name": "T-C13 · 1 brand list",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Non-empty list\", () => pm.expect(json).to.be.an(\"array\").that.is.not.empty);",
                  "// Search term: the first 3 letters of the first brand.",
                  "pm.environment.set(\"brandQ\", json[0].name.slice(0, 3));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/brands"
          }
        },
        {
          "name": "T-C13 · 2 brand search",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const q = pm.environment.get(\"brandQ\").toLowerCase();",
                  "pm.test(\"Each brand contains the term\", () => pm.response.json().forEach(b => pm.expect(b.name.toLowerCase()).to.include(q)));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/brands/search?q={{brandQ}}"
          }
        },
        {
          "name": "T-C14 Related products",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const list = Array.isArray(json) ? json : json.data;",
                  "pm.test(\"Current product not listed\", () => list.forEach(p => pm.expect(p.id).to.not.eql(pm.environment.get(\"productId\"))));",
                  "pm.test(\"Same category\", () => list.forEach(p => pm.expect(p.category_id || (p.category && p.category.id)).to.eql(pm.environment.get(\"categoryId\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}/related"
          }
        },
        {
          "name": "T-C15 · 1 product specs",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Each has spec_name and spec_value\", () => pm.response.json().forEach(s => pm.expect(s).to.include.keys(\"spec_name\", \"spec_value\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}/specs"
          }
        },
        {
          "name": "T-C15 · 2 spec names",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/product-specs/names"
          }
        }
      ]
    },
    {
      "name": "03 Registration",
      "item": [
        {
          "name": "T-B01 · 1 register",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.environment.set(\"emailNEW\", \"qa+new-\" + id + \"@test.local\");",
                  "pm.environment.set(\"passwordNEW\", \"Qa-Test!2026#NEW\" + id);",
                  "pm.variables.set(\"email\", pm.environment.get(\"emailNEW\"));",
                  "pm.variables.set(\"password\", pm.environment.get(\"passwordNEW\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "const json = pm.response.json();",
                  "pm.test(\"Has id\", () => pm.expect(json.id).to.exist);",
                  "pm.test(\"Email = the one sent\", () => pm.expect(json.email).to.eql(pm.environment.get(\"emailNEW\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B01 · 2 login with new account",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailNEW}}\",\n  \"password\": \"{{passwordNEW}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B02 Register existing email",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + Date.now());"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 409\", () => pm.response.to.have.status(409));",
                  "pm.test(\"Validation error on email\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"email\"] || (j.errors && j.errors[\"email\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B03 Weak password · short",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on password\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"password\"] || (j.errors && j.errors[\"password\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"Ab1!\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B03 Weak password · no uppercase",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on password\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"password\"] || (j.errors && j.errors[\"password\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"parolasimpla1!\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B03 Weak password · no digits",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on password\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"password\"] || (j.errors && j.errors[\"password\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"ParolaSimpla!\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B03 Weak password · no symbols",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on password\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"password\"] || (j.errors && j.errors[\"password\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"ParolaSimpla1\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B04 Register invalid email",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on email\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"email\"] || (j.errors && j.errors[\"email\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"qa-fara-arond-{{runId}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B05 Missing required fields · empty body",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"At least one validation error\", () => pm.expect(Object.keys(pm.response.json()).length).to.be.above(0));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B05 Missing required fields · without first_name",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"At least one validation error\", () => pm.expect(Object.keys(pm.response.json()).length).to.be.above(0));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B05 Missing required fields · without password",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"At least one validation error\", () => pm.expect(Object.keys(pm.response.json()).length).to.be.above(0));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B06 Register no password in response",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.test(\"No sensitive fields\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"\\\"password\\\"\", \"\\\"role\\\"\", \"\\\"totp_secret\\\"\", \"\\\"failed_login_attempts\\\"\", \"\\\"uid\\\"\"].forEach(key => pm.expect(t).to.not.include(key));",
                  "});",
                  "pm.test(\"No bcrypt hash value\", () => pm.expect(pm.response.text()).to.not.include(\"$2y$\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B07 · 1 register with role=admin",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.environment.set(\"emailRA\", \"qa+ra-\" + id + \"@test.local\");",
                  "pm.environment.set(\"passwordRA\", \"Qa-Test!2026#RA\" + id);",
                  "pm.variables.set(\"email\", pm.environment.get(\"emailRA\"));",
                  "pm.variables.set(\"password\", pm.environment.get(\"passwordRA\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [201, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([201, 422]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\",\n  \"role\": \"admin\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B07 · 2 login",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenRA\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailRA}}\",\n  \"password\": \"{{passwordRA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B07 · 3 user list",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenRA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users"
          }
        },
        {
          "name": "T-B07 · 4 admin report",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenRA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-of-years"
          }
        },
        {
          "name": "T-B10 Age under 18 · 10 years old",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "const d = new Date();",
                  "d.setFullYear(d.getFullYear() - 10);",
                  "pm.variables.set(\"dobMinor\", d.toISOString().slice(0, 10));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on dob\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"dob\"] || (j.errors && j.errors[\"dob\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"{{dobMinor}}\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B10 Age under 18 · wrong format",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "const d = new Date();",
                  "d.setFullYear(d.getFullYear() - 10);",
                  "pm.variables.set(\"dobMinor\", d.toISOString().slice(0, 10));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on dob\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"dob\"] || (j.errors && j.errors[\"dob\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"01-01-1990\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B11 Postal code invalid for country · NL with US code",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on address.postal_code\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"address.postal_code\"] || (j.errors && j.errors[\"address.postal_code\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"12345\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B11 Postal code invalid for country · US with Dutch code",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on address.postal_code\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"address.postal_code\"] || (j.errors && j.errors[\"address.postal_code\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"US\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B12 Values at maximum (must be accepted) · first_name 40",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "pm.variables.set(\"s20\", \"A\".repeat(20));",
                  "pm.variables.set(\"s21\", \"A\".repeat(21));",
                  "pm.variables.set(\"s40\", \"A\".repeat(40));",
                  "pm.variables.set(\"s41\", \"A\".repeat(41));",
                  "pm.variables.set(\"p24\", \"1\".repeat(24));",
                  "pm.variables.set(\"p25\", \"1\".repeat(25));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"{{s40}}\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B12 Values at maximum (must be accepted) · last_name 20",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "pm.variables.set(\"s20\", \"A\".repeat(20));",
                  "pm.variables.set(\"s21\", \"A\".repeat(21));",
                  "pm.variables.set(\"s40\", \"A\".repeat(40));",
                  "pm.variables.set(\"s41\", \"A\".repeat(41));",
                  "pm.variables.set(\"p24\", \"1\".repeat(24));",
                  "pm.variables.set(\"p25\", \"1\".repeat(25));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"{{s20}}\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B12 Values at maximum (must be accepted) · phone 24",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "pm.variables.set(\"s20\", \"A\".repeat(20));",
                  "pm.variables.set(\"s21\", \"A\".repeat(21));",
                  "pm.variables.set(\"s40\", \"A\".repeat(40));",
                  "pm.variables.set(\"s41\", \"A\".repeat(41));",
                  "pm.variables.set(\"p24\", \"1\".repeat(24));",
                  "pm.variables.set(\"p25\", \"1\".repeat(25));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"{{p24}}\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B12 Values over maximum (must be rejected) · first_name 41",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "pm.variables.set(\"s20\", \"A\".repeat(20));",
                  "pm.variables.set(\"s21\", \"A\".repeat(21));",
                  "pm.variables.set(\"s40\", \"A\".repeat(40));",
                  "pm.variables.set(\"s41\", \"A\".repeat(41));",
                  "pm.variables.set(\"p24\", \"1\".repeat(24));",
                  "pm.variables.set(\"p25\", \"1\".repeat(25));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"{{s41}}\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B12 Values over maximum (must be rejected) · last_name 21",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "pm.variables.set(\"s20\", \"A\".repeat(20));",
                  "pm.variables.set(\"s21\", \"A\".repeat(21));",
                  "pm.variables.set(\"s40\", \"A\".repeat(40));",
                  "pm.variables.set(\"s41\", \"A\".repeat(41));",
                  "pm.variables.set(\"p24\", \"1\".repeat(24));",
                  "pm.variables.set(\"p25\", \"1\".repeat(25));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"{{s21}}\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B12 Values over maximum (must be rejected) · phone 25",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);",
                  "pm.variables.set(\"s20\", \"A\".repeat(20));",
                  "pm.variables.set(\"s21\", \"A\".repeat(21));",
                  "pm.variables.set(\"s40\", \"A\".repeat(40));",
                  "pm.variables.set(\"s41\", \"A\".repeat(41));",
                  "pm.variables.set(\"p24\", \"1\".repeat(24));",
                  "pm.variables.set(\"p25\", \"1\".repeat(25));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"{{p25}}\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "04 Authentication",
      "item": [
        {
          "name": "T-A01 Valid login",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"access_token not empty\", () => pm.expect(json.access_token).to.be.a(\"string\").and.not.empty);",
                  "pm.test(\"token_type = bearer\", () => pm.expect(String(json.token_type).toLowerCase()).to.eql(\"bearer\"));",
                  "pm.test(\"expires_in positive\", () => pm.expect(json.expires_in).to.be.above(0));",
                  "pm.test(\"No sensitive fields\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"\\\"password\\\"\"].forEach(key => pm.expect(t).to.not.include(key));",
                  "});",
                  "// Save the token and its expiry time for T-J10 (run at the end).",
                  "pm.environment.set(\"tokenA\", json.access_token);",
                  "pm.environment.set(\"tokenOld\", json.access_token);",
                  "pm.environment.set(\"tokenOldExpiresAt\", Date.now() + json.expires_in * 1000);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A05 Me with valid token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Email = emailA\", () => pm.expect(json.email).to.eql(pm.environment.get(\"emailA\")));",
                  "pm.test(\"Id = userIdA\", () => pm.expect(json.id).to.eql(pm.environment.get(\"userIdA\")));",
                  "pm.test(\"No sensitive fields\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"\\\"password\\\"\", \"\\\"totp_secret\\\"\", \"\\\"failed_login_attempts\\\"\"].forEach(key => pm.expect(t).to.not.include(key));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A06 Me without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No user data\", () => pm.expect(pm.response.text()).to.not.include(\"email\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A07 · 1 tampered token",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Change the last character of token A.",
                  "const t = pm.environment.get(\"tokenA\");",
                  "pm.variables.set(\"tokenModificat\", t.slice(0, -1) + (t.slice(-1) === \"a\" ? \"b\" : \"a\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenModificat}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A07 · 2 made-up token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "abc.def.ghi",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A04 · 1 wrong password",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));",
                  "// Keep the response for the comparison in request 2.",
                  "pm.environment.set(\"a04Raspuns\", pm.response.text());"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"Gresita!123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A04 · 2 unknown user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));",
                  "pm.test(\"Same message as for the wrong password\", () => pm.expect(pm.response.text()).to.eql(pm.environment.get(\"a04Raspuns\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"nu-exista-{{runId}}@test.local\",\n  \"password\": \"Gresita!123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A04 · 3 successful login (resets counter)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenA\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A02 Login invalid email",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 422]));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{ \"email\": \"customer-fara-arond\", \"password\": \"welcome01\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A03 Login missing fields · without email",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 422]));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"password\": \"welcome01\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A03 Login missing fields · without password",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 422]));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"customer@practicesoftwaretesting.com\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A03 Login missing fields · empty body",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 422]));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A16 · 1 new login",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenR\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A16 · 2 refresh",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"New token, different from the old one\", () => pm.expect(json.access_token).to.be.a(\"string\").and.not.eql(pm.environment.get(\"tokenR\")));",
                  "pm.environment.set(\"tokenRefreshed\", json.access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenR}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/refresh"
          }
        },
        {
          "name": "T-A16 · 3 me with new token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenRefreshed}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A16 · 4 me with old token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [200, 401]\", () => pm.expect(pm.response.code).to.be.oneOf([200, 401]));",
                  "console.log(\"Old token after refresh got:\", pm.response.code); // record in the observed result"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenR}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A08 · 1 new login",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenLogout\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A08 · 2 me before",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenLogout}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A08 · 3 logout",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenLogout}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/logout"
          }
        },
        {
          "name": "T-A08 · 4 me after logout",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenLogout}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A09 · 1 change-password wrong current",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));",
                  "pm.test(\"success = false\", () => pm.expect(pm.response.json().success).to.eql(false));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/change-password",
            "body": {
              "mode": "raw",
              "raw": "{ \"current_password\": \"Gresita!123\", \"new_password\": \"Nou-Pass!2026x\", \"new_password_confirmation\": \"Nou-Pass!2026x\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A09 · 2 old password still works",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenA\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A13 Change-password same password",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));",
                  "pm.test(\"success = false\", () => pm.expect(pm.response.json().success).to.eql(false));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/change-password",
            "body": {
              "mode": "raw",
              "raw": "{ \"current_password\": \"{{passwordA}}\", \"new_password\": \"{{passwordA}}\", \"new_password_confirmation\": \"{{passwordA}}\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A14 Weak new password · too short",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on new_password\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"new_password\"] || (j.errors && j.errors[\"new_password\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/change-password",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"current_password\": \"{{passwordA}}\",\n  \"new_password\": \"abc\",\n  \"new_password_confirmation\": \"abc\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A14 Weak new password · no complexity",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on new_password\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"new_password\"] || (j.errors && j.errors[\"new_password\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/change-password",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"current_password\": \"{{passwordA}}\",\n  \"new_password\": \"parolasimpla\",\n  \"new_password_confirmation\": \"parolasimpla\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A14 Weak new password · confirmation mismatch",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on new_password\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"new_password\"] || (j.errors && j.errors[\"new_password\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/change-password",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"current_password\": \"{{passwordA}}\",\n  \"new_password\": \"Nou-Pass!2026x\",\n  \"new_password_confirmation\": \"Alta-Pass!2026y\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A14 · user A password unchanged",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenA\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "05 Profile",
      "item": [
        {
          "name": "T-B08 · 1 update profile",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"success = true\", () => pm.expect(pm.response.json().success).to.eql(true));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdA}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA-Modificat\",\n  \"last_name\": \"Tester\",\n  \"email\": \"{{emailA}}\",\n  \"phone\": \"0612345678\",\n  \"address\": {\n    \"street\": \"Teststraat 2\",\n    \"city\": \"Amsterdam\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B08 · 2 read profile",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"first_name updated\", () => pm.expect(pm.response.json().first_name).to.eql(\"QA-Modificat\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-B09 · 1 PUT with role=admin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "console.log(\"PUT with role:\", pm.response.code, pm.response.text());"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdA}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA-Modificat\",\n  \"last_name\": \"Tester\",\n  \"email\": \"{{emailA}}\",\n  \"phone\": \"0612345678\",\n  \"address\": {\n    \"street\": \"Teststraat 2\",\n    \"city\": \"Amsterdam\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"role\": \"admin\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B09 · 2 PATCH with role=admin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "console.log(\"PATCH with role:\", pm.response.code, pm.response.text());"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "PATCH",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdA}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"role\": \"admin\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B09 · 3 new login A",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenA\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailA}}\",\n  \"password\": \"{{passwordA}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-B09 · 4 user list with new token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users"
          }
        }
      ]
    },
    {
      "name": "06 Cart and payment",
      "item": [
        {
          "name": "T-D01 Create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "const json = pm.response.json();",
                  "pm.test(\"Has id\", () => pm.expect(json.id).to.be.a(\"string\").and.not.empty);",
                  "pm.environment.set(\"cartId\", json.id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D02 · 1 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D02 · 2 read cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "const line = items.filter(i => i.product_id === pm.environment.get(\"productId\"));",
                  "pm.test(\"Single line for the product\", () => pm.expect(line.length).to.eql(1));",
                  "pm.test(\"Quantity 1\", () => pm.expect(line[0].quantity).to.eql(1));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}"
          }
        },
        {
          "name": "T-D03 · 1 (second time) · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D03 · 2 read cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "const line = items.filter(i => i.product_id === pm.environment.get(\"productId\"));",
                  "pm.test(\"No duplicate line\", () => pm.expect(line.length).to.eql(1));",
                  "pm.test(\"Quantity 2\", () => pm.expect(line[0].quantity).to.eql(2));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}"
          }
        },
        {
          "name": "T-D04 · 1 quantity 5",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 5\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D04 · 2 read cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "const line = items.filter(i => i.product_id === pm.environment.get(\"productId\"));",
                  "pm.test(\"Quantity 5\", () => pm.expect(line[0].quantity).to.eql(5));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}"
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · POST 0",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 0 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · PUT 0",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 0 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · POST -1",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": -1 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · PUT -1",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": -1 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · POST 100",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 100 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · PUT 100",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 100 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · POST \"abc\"",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": \"abc\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · PUT \"abc\"",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": \"abc\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · POST 1.5",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 1.5 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Invalid quantities (POST and PUT) · PUT 1.5",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on quantity\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"quantity\"] || (j.errors && j.errors[\"quantity\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 1.5 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Valid limits · PUT 1",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 1 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D05 Valid limits · PUT 99",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\", \"quantity\": 99 }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D06 · 1 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartD06\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D06 · 2 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartD06}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D06 · 3 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartD06}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId2}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D06 · 4 remove product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartD06}}/product/{{productId}}"
          }
        },
        {
          "name": "T-D06 · 5 read cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "pm.test(\"Only the second product remains\", () => pm.expect(items.map(i => i.product_id)).to.eql([pm.environment.get(\"productId2\")]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartD06}}"
          }
        },
        {
          "name": "T-D07 Read cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "pm.test(\"cart_items is a list\", () => pm.expect(items).to.be.an(\"array\"));",
                  "pm.test(\"Each line has product, quantity and price\", () => items.forEach(i => {",
                  "  pm.expect(i).to.include.keys(\"product_id\", \"quantity\", \"product\");",
                  "  pm.expect(i.product.price).to.be.a(\"number\");",
                  "}));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}"
          }
        },
        {
          "name": "T-D09 · 1 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartTot\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D09 · 2 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartTot}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 2\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D09 · 3 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartTot}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId2}}\",\n  \"quantity\": 3\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D09 · 4 verificare total",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "// Prices come from the catalog (saved at S3b), not from the cart.",
                  "const p1 = Number(pm.environment.get(\"productPrice\")), p2 = Number(pm.environment.get(\"productPrice2\"));",
                  "const expected = Math.round((p1 * 2 + p2 * 3) * 100) / 100;",
                  "const fromCart = items.reduce((s, i) => s + (i.discounted_price ?? i.product.price) * i.quantity, 0);",
                  "console.log(\"Calculated from catalog:\", expected, \"| from cart:\", fromCart);",
                  "pm.test(\"Cart prices = catalog prices\", () => items.forEach(i => {",
                  "  const cat = i.product_id === pm.environment.get(\"productId\") ? p1 : p2;",
                  "  pm.expect(i.product.price).to.eql(cat);",
                  "}));",
                  "pm.test(\"Cart total = calculated total\", () => pm.expect(Math.abs(fromCart - expected)).to.be.below(0.005));",
                  "pm.environment.set(\"cartTotExpected\", expected);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartTot}}"
          }
        },
        {
          "name": "T-D10 · 1 non-existent product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on product_id\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"product_id\"] || (j.errors && j.errors[\"product_id\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"01AAAAAAAAAAAAAAAAAAAAAAAA\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D10 · 2 read cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "pm.test(\"Line was not added\", () => pm.expect(items.some(i => i.product_id === \"01AAAAAAAAAAAAAAAAAAAAAAAA\")).to.eql(false));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}"
          }
        },
        {
          "name": "T-D11 Non-existent cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 404\", () => pm.response.to.have.status(404));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/01AAAAAAAAAAAAAAAAAAAAAAAA",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D12 · 1 find Thor Hammer",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const p = pm.response.json().data.find(x => x.name === \"Thor Hammer\");",
                  "pm.test(\"Thor Hammer found\", () => pm.expect(p).to.exist);",
                  "pm.environment.set(\"thorHammerId\", p.id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/search?q=thor"
          }
        },
        {
          "name": "T-D12 · 2 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartThor\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D12 · 3 quantity 2 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartThor}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{thorHammerId}}\",\n  \"quantity\": 2\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D12 · 4 quantity 1 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartThor}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{thorHammerId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D12 · 5 one more · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartThor}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{thorHammerId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D12 · 6 PUT quantity 2",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartThor}}/product/quantity",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{thorHammerId}}\",\n  \"quantity\": 2\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D12 · 7 read cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const items = json.cart_items;",
                  "const line = items.filter(i => i.product_id === pm.environment.get(\"thorHammerId\"));",
                  "pm.test(\"At most one item\", () => pm.expect(line[0].quantity).to.eql(1));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartThor}}"
          }
        },
        {
          "name": "T-D08 · 1 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartDel\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-D08 · 2 delete cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartDel}}"
          }
        },
        {
          "name": "T-D08 · 3 read after delete",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 404\", () => pm.response.to.have.status(404));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartDel}}"
          }
        },
        {
          "name": "T-E04 Card valid",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Payment was successful\", () => pm.expect(pm.response.json().message).to.eql(\"Payment was successful\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"credit-card\",\n  \"payment_details\": {\n    \"credit_card_number\": \"1111-2222-3333-4444\",\n    \"expiration_date\": \"12/2030\",\n    \"cvv\": \"123\",\n    \"card_holder_name\": \"QA Tester\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E05 Invalid card · number without dashes",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Payment is not confirmed\", () => pm.expect(pm.response.text()).to.not.include(\"Payment was successful\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"credit-card\",\n  \"payment_details\": {\n    \"credit_card_number\": \"1111222233334444\",\n    \"expiration_date\": \"12/2030\",\n    \"cvv\": \"123\",\n    \"card_holder_name\": \"QA Tester\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E05 Invalid card · expired",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Payment is not confirmed\", () => pm.expect(pm.response.text()).to.not.include(\"Payment was successful\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"credit-card\",\n  \"payment_details\": {\n    \"credit_card_number\": \"1111-2222-3333-4444\",\n    \"expiration_date\": \"01/2020\",\n    \"cvv\": \"123\",\n    \"card_holder_name\": \"QA Tester\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E05 Invalid card · short cvv",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Payment is not confirmed\", () => pm.expect(pm.response.text()).to.not.include(\"Payment was successful\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"credit-card\",\n  \"payment_details\": {\n    \"credit_card_number\": \"1111-2222-3333-4444\",\n    \"expiration_date\": \"12/2030\",\n    \"cvv\": \"12\",\n    \"card_holder_name\": \"QA Tester\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E05 Invalid card · name with digits",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Payment is not confirmed\", () => pm.expect(pm.response.text()).to.not.include(\"Payment was successful\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"credit-card\",\n  \"payment_details\": {\n    \"credit_card_number\": \"1111-2222-3333-4444\",\n    \"expiration_date\": \"12/2030\",\n    \"cvv\": \"123\",\n    \"card_holder_name\": \"QA123\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E10 Unknown payment method · bitcoin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Does not confirm payment\", () => pm.expect(pm.response.text()).to.not.include(\"Payment was successful\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"bitcoin\",\n  \"payment_details\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E10 Unknown payment method · empty body",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Does not confirm payment\", () => pm.expect(pm.response.text()).to.not.include(\"Payment was successful\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E11 Valid methods · bank-transfer",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"bank-transfer\",\n  \"payment_details\": {\n    \"bank_name\": \"Test Bank\",\n    \"account_name\": \"QA Tester\",\n    \"account_number\": \"123456789\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E11 Valid methods · gift-card",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"gift-card\",\n  \"payment_details\": {\n    \"gift_card_number\": \"ABCD1234EFGH5678\",\n    \"validation_code\": \"AB12\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E11 Valid methods · buy-now-pay-later",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"buy-now-pay-later\",\n  \"payment_details\": {\n    \"monthly_installments\": 3\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E11 Invalid variants · account with letters",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"bank-transfer\",\n  \"payment_details\": {\n    \"bank_name\": \"Test Bank\",\n    \"account_name\": \"QA Tester\",\n    \"account_number\": \"12AB\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E11 Invalid variants · gift card 15 characters",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"gift-card\",\n  \"payment_details\": {\n    \"gift_card_number\": \"ABCD1234EFGH567\",\n    \"validation_code\": \"AB12\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E11 Invalid variants · no installments",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/payment/check",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"payment_method\": \"buy-now-pay-later\",\n  \"payment_details\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "07 Orders and invoices",
      "item": [
        {
          "name": "T-E01 Place order",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Snapshot the cart before checkout, for T-E06 and T-E07.",
                  "pm.sendRequest(pm.environment.get(\"baseUrl\") + \"/carts/\" + pm.environment.get(\"cartId\"), (err, res) => {",
                  "  pm.environment.set(\"cartSnapshot\", JSON.stringify(res.json().cart_items.map(i => ({ product_id: i.product_id, quantity: i.quantity, price: i.product.price }))));",
                  "});"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "const json = pm.response.json();",
                  "pm.test(\"Has id and invoice_number\", () => { pm.expect(json.id).to.exist; pm.expect(json.invoice_number).to.exist; });",
                  "pm.test(\"Assigned to user A\", () => pm.expect(json.user_id).to.eql(pm.environment.get(\"userIdA\")));",
                  "pm.environment.set(\"invoiceId\", json.id);",
                  "pm.environment.set(\"invoiceNumber\", json.invoice_number);",
                  "pm.environment.set(\"invoiceIdA\", json.id);",
                  "pm.environment.set(\"invoiceNumberA\", json.invoice_number);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E06 Order lines",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const cart = JSON.parse(pm.environment.get(\"cartSnapshot\"));",
                  "const lines = json.invoicelines;",
                  "pm.test(\"Same number of lines as the cart\", () => pm.expect(lines.length).to.eql(cart.length));",
                  "pm.test(\"Same products, quantities and prices\", () => cart.forEach(c => {",
                  "  const l = lines.find(x => x.product_id === c.product_id);",
                  "  pm.expect(l, \"linia pentru \" + c.product_id).to.exist;",
                  "  pm.expect(l.quantity).to.eql(c.quantity);",
                  "  pm.expect(l.unit_price).to.eql(c.price);",
                  "}));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}"
          }
        },
        {
          "name": "T-E07 Order total",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const fromLines = json.invoicelines.reduce((s, l) => s + (l.discounted_price ?? l.unit_price) * l.quantity, 0);",
                  "const fromCart = JSON.parse(pm.environment.get(\"cartSnapshot\")).reduce((s, c) => s + c.price * c.quantity, 0);",
                  "console.log(\"Lines:\", fromLines, \"| invoice total:\", json.total, \"| cart:\", fromCart);",
                  "pm.test(\"Total = sum of lines\", () => pm.expect(Math.abs(json.total - fromLines)).to.be.below(0.005));",
                  "pm.test(\"Total = cart total\", () => pm.expect(Math.abs(json.total - fromCart)).to.be.below(0.005));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}"
          }
        },
        {
          "name": "T-E02 · empty body",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on billing_street\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"billing_street\"] || (j.errors && j.errors[\"billing_street\"])).to.exist;",
                  "});",
                  "pm.test(\"Validation error on cart_id\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"cart_id\"] || (j.errors && j.errors[\"cart_id\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E02 · without billing_street",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on billing_street\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"billing_street\"] || (j.errors && j.errors[\"billing_street\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E02 · without billing_city",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on billing_city\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"billing_city\"] || (j.errors && j.errors[\"billing_city\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E02 · without billing_country",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on billing_country\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"billing_country\"] || (j.errors && j.errors[\"billing_country\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E02 · without cart_id",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on cart_id\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"cart_id\"] || (j.errors && j.errors[\"cart_id\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E02 · without payment_details",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on payment_details\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"payment_details\"] || (j.errors && j.errors[\"payment_details\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E03 · 1 address lookup",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"City returned\", () => pm.expect(pm.response.json().city).to.be.a(\"string\").and.not.empty);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/postcode-lookup?country=NL&postcode=1011%20AB"
          }
        },
        {
          "name": "T-E03 · 2 wrong postal code",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on billing_country\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"billing_country\"] || (j.errors && j.errors[\"billing_country\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"12345\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E03 · 3 wrong city",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on billing_country\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"billing_country\"] || (j.errors && j.errors[\"billing_country\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"Paris\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E08 · 1 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartGuest\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E08 · 2 · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartGuest}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E08 · 3 guest order",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.test(\"No user_id\", () => pm.expect(pm.response.json().user_id).to.be.null);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices/guest",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartGuest}}\",\n  \"guest_email\": \"guest+{{runId}}@test.local\",\n  \"guest_first_name\": \"Guest\",\n  \"guest_last_name\": \"QA\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E08 · 4 without guest_email",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on guest_email\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"guest_email\"] || (j.errors && j.errors[\"guest_email\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices/guest",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartGuest}}\",\n  \"guest_first_name\": \"Guest\",\n  \"guest_last_name\": \"QA\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E09 · 1 non-existent cart",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"cartInexistent\", \"01AAAAAAAAAAAAAAAAAAAAAAAA\");"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartInexistent}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E09 · 2 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartGol\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-E09 · 3 empty cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"billing_street\": \"Teststraat 1\",\n  \"billing_city\": \"{{billingCity}}\",\n  \"billing_state\": \"{{billingState}}\",\n  \"billing_country\": \"NL\",\n  \"billing_postal_code\": \"1011 AB\",\n  \"payment_method\": \"cash-on-delivery\",\n  \"payment_details\": {},\n  \"cart_id\": \"{{cartGol}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-F01 My invoices",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const rows = Array.isArray(json) ? json : json.data;",
                  "pm.test(\"All invoices belong to A\", () => rows.forEach(i => pm.expect(i.user_id).to.eql(pm.environment.get(\"userIdA\"))));",
                  "pm.test(\"B's invoice is missing\", () => pm.expect(rows.map(i => i.id)).to.not.include(pm.environment.get(\"invoiceIdB\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices"
          }
        },
        {
          "name": "T-F02 · 1 status PDF",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [200, 400]\", () => pm.expect(pm.response.code).to.be.oneOf([200, 400]));",
                  "console.log(\"PDF status:\", pm.response.json().status); // repeat until a final status"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceNumberA}}/download-pdf-status"
          }
        },
        {
          "name": "T-F02 · 2 download PDF",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Content-Type PDF\", () => pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(\"application/pdf\"));",
                  "pm.test(\"File is not empty\", () => pm.expect(pm.response.responseSize).to.be.above(0));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceNumberA}}/download-pdf"
          }
        },
        {
          "name": "T-F06 Search user B's invoice",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const rows = Array.isArray(json) ? json : json.data;",
                  "pm.test(\"No results\", () => pm.expect(rows).to.be.empty);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/search?q={{invoiceNumberB}}"
          }
        },
        {
          "name": "T-F03 · 1 admin changes status",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"success = true\", () => pm.expect(pm.response.json().success).to.eql(true));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{ \"status\": \"SHIPPED\", \"status_message\": \"Order shipped\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-F03 · 2 read invoice",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Status SHIPPED\", () => pm.expect(pm.response.json().status).to.eql(\"SHIPPED\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}"
          }
        },
        {
          "name": "T-F05 Invalid status · unknown status",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"mesaj51\", \"x\".repeat(51));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"LOST\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-F05 Invalid status · message too short",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"mesaj51\", \"x\".repeat(51));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"SHIPPED\",\n  \"status_message\": \"abc\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-F05 Invalid status · message of 51 characters",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"mesaj51\", \"x\".repeat(51));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"SHIPPED\",\n  \"status_message\": \"{{mesaj51}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-F04 · 1 user B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{ \"status\": \"COMPLETED\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-F04 · 2 owner A",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{ \"status\": \"COMPLETED\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-F04 · 3 check as admin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Status is not COMPLETED\", () => pm.expect(pm.response.json().status).to.not.eql(\"COMPLETED\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}"
          }
        }
      ]
    },
    {
      "name": "08 Favorites",
      "item": [
        {
          "name": "T-G01 · 1 add favorite",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "const json = pm.response.json();",
                  "pm.environment.set(\"favoriteId\", json.id);",
                  "pm.environment.set(\"favoriteIdA\", json.id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/favorites",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"{{productId}}\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-G01 · 2 favorites list",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const favs = pm.response.json();",
                  "pm.test(\"Contains the product\", () => pm.expect(favs.map(f => f.product_id)).to.include(pm.environment.get(\"productId\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites"
          }
        },
        {
          "name": "T-G02 · 1 favorite for B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"favoriteIdB\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/favorites",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId2}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-G02 · 2 A's list",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const favs = pm.response.json();",
                  "pm.test(\"Only A's favorites\", () => favs.forEach(f => pm.expect(f.user_id).to.eql(pm.environment.get(\"userIdA\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites"
          }
        },
        {
          "name": "T-G02 · 3 B's list",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const favs = pm.response.json();",
                  "pm.test(\"Only B's favorites\", () => favs.forEach(f => pm.expect(f.user_id).to.eql(pm.environment.get(\"userIdB\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites"
          }
        },
        {
          "name": "T-G04 · 1 duplicate favorite",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "console.log(\"Duplicate:\", pm.response.code, pm.response.text());"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/favorites",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-G04 · 2 count occurrences",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const favs = pm.response.json();",
                  "pm.test(\"Product appears once\", () => pm.expect(favs.filter(f => f.product_id === pm.environment.get(\"productId\")).length).to.eql(1));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites"
          }
        },
        {
          "name": "T-G05 Favorite non-existent product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"Validation error on product_id\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"product_id\"] || (j.errors && j.errors[\"product_id\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/favorites",
            "body": {
              "mode": "raw",
              "raw": "{ \"product_id\": \"01AAAAAAAAAAAAAAAAAAAAAAAA\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-G06 · 1 GET without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites"
          }
        },
        {
          "name": "T-G06 · 2 POST without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/favorites",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-G03 · 1 favorite to delete",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"favoriteDel\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/favorites",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId2}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-G03 · 2 delete",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/favorites/{{favoriteDel}}"
          }
        },
        {
          "name": "T-G03 · 3 list after delete",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const favs = pm.response.json();",
                  "pm.test(\"Favorite is gone\", () => pm.expect(favs.map(f => f.id)).to.not.include(pm.environment.get(\"favoriteDel\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites"
          }
        }
      ]
    },
    {
      "name": "09 Messages",
      "item": [
        {
          "name": "T-H01 · 1 visitor",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"messageId\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages",
            "body": {
              "mode": "raw",
              "raw": "{ \"name\": \"QA Tester\", \"email\": \"qa+{{runId}}@test.local\", \"subject\": \"customer-service\", \"message\": \"Test message sent from Postman to check the contact form.\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H01 · 2 logged-in user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.environment.set(\"messageIdA\", json.id);",
                  "pm.test(\"Assigned to user A\", () => pm.expect(json.user_id).to.eql(pm.environment.get(\"userIdA\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"subject\": \"customer-service\",\n  \"message\": \"Test message sent by user A from Postman.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H02 Invalid message · without subject",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"mesaj251\", \"x\".repeat(251));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"QA Tester\",\n  \"email\": \"qa@test.local\",\n  \"message\": \"Test message.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H02 Invalid message · without message",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"mesaj251\", \"x\".repeat(251));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"QA Tester\",\n  \"email\": \"qa@test.local\",\n  \"subject\": \"customer-service\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H02 Invalid message · message of 251",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"mesaj251\", \"x\".repeat(251));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"QA Tester\",\n  \"email\": \"qa@test.local\",\n  \"subject\": \"customer-service\",\n  \"message\": \"{{mesaj251}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H02 Invalid message · invalid email",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.variables.set(\"mesaj251\", \"x\".repeat(251));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"QA Tester\",\n  \"email\": \"nu-e-email\",\n  \"subject\": \"customer-service\",\n  \"message\": \"Test message.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H03 · 1 admin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const rows = Array.isArray(json) ? json : json.data;",
                  "const ids = rows.map(m => m.id);",
                  "pm.test(\"Admin sees both messages\", () => pm.expect(ids).to.include.members([pm.environment.get(\"messageId\"), pm.environment.get(\"messageIdA\")]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages"
          }
        },
        {
          "name": "T-H03 · 2 user A",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const rows = Array.isArray(json) ? json : json.data;",
                  "const ids = rows.map(m => m.id);",
                  "pm.test(\"A sees only own messages\", () => rows.forEach(m => pm.expect(m.user_id).to.eql(pm.environment.get(\"userIdA\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages"
          }
        },
        {
          "name": "T-H03 · 3 user B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const rows = Array.isArray(json) ? json : json.data;",
                  "const ids = rows.map(m => m.id);",
                  "pm.test(\"B does not see A's message\", () => pm.expect(ids).to.not.include(pm.environment.get(\"messageIdA\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages"
          }
        },
        {
          "name": "T-H04 · 1 user B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404]));",
                  "pm.test(\"No data in response\", () => pm.expect(pm.response.text().length).to.be.below(300));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}"
          }
        },
        {
          "name": "T-H04 · 2 owner A",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}"
          }
        },
        {
          "name": "T-H06 Attach file · gol.txt",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"success = true\", () => pm.expect(pm.response.json().success).to.eql(true));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageId}}/attach-file",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "gol.txt"
                }
              ]
            }
          }
        },
        {
          "name": "T-H06 Rejected files · continut.txt",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));",
                  "pm.test(\"Explicit error message\", () => pm.expect(pm.response.json().errors).to.not.be.empty);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageId}}/attach-file",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "continut.txt"
                }
              ]
            }
          }
        },
        {
          "name": "T-H06 Rejected files · gol.pdf",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));",
                  "pm.test(\"Explicit error message\", () => pm.expect(pm.response.json().errors).to.not.be.empty);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageId}}/attach-file",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "gol.pdf"
                }
              ]
            }
          }
        },
        {
          "name": "T-H06 Rejected files · no file",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 400\", () => pm.response.to.have.status(400));",
                  "pm.test(\"Explicit error message\", () => pm.expect(pm.response.json().errors).to.not.be.empty);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageId}}/attach-file",
            "body": {
              "mode": "formdata",
              "formdata": []
            }
          }
        },
        {
          "name": "T-H05 · 1 reply admin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}/reply",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"message\": \"Test reply from the admin.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H05 · 2 status after reply",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"IN_PROGRESS\", () => pm.expect(pm.response.json().status).to.eql(\"IN_PROGRESS\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}"
          }
        },
        {
          "name": "T-H05 · 3 status RESOLVED",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"RESOLVED\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H05 · 4 status final",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"RESOLVED\", () => pm.expect(pm.response.json().status).to.eql(\"RESOLVED\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}"
          }
        },
        {
          "name": "T-H07 · 1 B changes status",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([403, 404]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}/status",
            "body": {
              "mode": "raw",
              "raw": "{ \"status\": \"NEW\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-H07 · 2 check as admin",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Status stayed RESOLVED\", () => pm.expect(pm.response.json().status).to.eql(\"RESOLVED\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}"
          }
        }
      ]
    },
    {
      "name": "10 Data isolation",
      "item": [
        {
          "name": "T-J08 Private endpoints without token · GET /users/me",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No data\", () => pm.expect(pm.response.text()).to.not.include(\"\\\"id\\\"\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-J08 Private endpoints without token · GET /users",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No data\", () => pm.expect(pm.response.text()).to.not.include(\"\\\"id\\\"\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users"
          }
        },
        {
          "name": "T-J08 Private endpoints without token · GET /invoices",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No data\", () => pm.expect(pm.response.text()).to.not.include(\"\\\"id\\\"\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices"
          }
        },
        {
          "name": "T-J08 Private endpoints without token · GET /favorites",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No data\", () => pm.expect(pm.response.text()).to.not.include(\"\\\"id\\\"\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites"
          }
        },
        {
          "name": "T-J08 Private endpoints without token · GET /messages",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No data\", () => pm.expect(pm.response.text()).to.not.include(\"\\\"id\\\"\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/messages"
          }
        },
        {
          "name": "T-J08 Private endpoints without token · GET /reports/total-sales-of-years",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No data\", () => pm.expect(pm.response.text()).to.not.include(\"\\\"id\\\"\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-of-years"
          }
        },
        {
          "name": "T-J08 Private endpoints without token · POST /totp/setup",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No data\", () => pm.expect(pm.response.text()).to.not.include(\"\\\"id\\\"\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/totp/setup"
          }
        },
        {
          "name": "T-J02 · 1 B reads A's invoice",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404]));",
                  "pm.test(\"No data in response\", () => pm.expect(pm.response.text().length).to.be.below(300));",
                  "pm.test(\"No billing address\", () => pm.expect(pm.response.text()).to.not.include(\"billing_street\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}"
          }
        },
        {
          "name": "T-J02 · 2 A reads own invoice",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceIdA}}"
          }
        },
        {
          "name": "T-J03 B downloads A's PDF",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([403, 404]));",
                  "pm.test(\"Does not receive a PDF\", () => pm.expect(pm.response.headers.get(\"Content-Type\") || \"\").to.not.include(\"application/pdf\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/invoices/{{invoiceNumberA}}/download-pdf"
          }
        },
        {
          "name": "T-J04 · 1 B reads A's profile",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404]));",
                  "pm.test(\"No data in response\", () => pm.expect(pm.response.text().length).to.be.below(300));",
                  "pm.test(\"No email of A\", () => pm.expect(pm.response.text()).to.not.include(pm.environment.get(\"emailA\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdA}}"
          }
        },
        {
          "name": "T-J04 · 2 admin reads A's profile",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdA}}"
          }
        },
        {
          "name": "T-J05 · 1 PUT by B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdA}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"Hacked\",\n  \"last_name\": \"Tester\",\n  \"email\": \"atacator+{{runId}}@test.local\",\n  \"phone\": \"0612345678\",\n  \"address\": {\n    \"street\": \"Teststraat 2\",\n    \"city\": \"Amsterdam\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J05 · 2 PATCH by B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "PATCH",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdA}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"Hacked\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J05 · 3 A's profile",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Email unchanged\", () => pm.expect(json.email).to.eql(pm.environment.get(\"emailA\")));",
                  "pm.test(\"Name unchanged\", () => pm.expect(json.first_name).to.not.eql(\"Hacked\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-J06 B reads A's favorite",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404]));",
                  "pm.test(\"No data in response\", () => pm.expect(pm.response.text().length).to.be.below(300));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/favorites/{{favoriteIdA}}"
          }
        },
        {
          "name": "T-J07 B replies to A's message",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([403, 404]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/messages/{{messageIdA}}/reply",
            "body": {
              "mode": "raw",
              "raw": "{ \"message\": \"Reply from another account.\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J01 · 1 A · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartIdA\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J01 · 2 A · add product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartIdA}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J01 · 3 B reads A's cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([403, 404]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartIdA}}"
          }
        },
        {
          "name": "T-J01 · 4 visitor reads A's cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartIdA}}"
          }
        },
        {
          "name": "T-J01 · 5 B adds to A's cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenB}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartIdA}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId2}}\",\n  \"quantity\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J01 · 6 cart to delete · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartJ01del\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J01 · 7 visitor deletes cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartJ01del}}"
          }
        },
        {
          "name": "T-J09 · 1 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartJ1\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J09 · 2 · create cart",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"cartJ2\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-J09 · 3 guessed id",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// The second id with its last character changed.",
                  "const b = pm.environment.get(\"cartJ2\");",
                  "pm.variables.set(\"cartGhicit\", b.slice(0, -1) + (b.slice(-1) === \"0\" ? \"1\" : \"0\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 404\", () => pm.response.to.have.status(404));",
                  "const a = pm.environment.get(\"cartJ1\"), b = pm.environment.get(\"cartJ2\");",
                  "let common = 0; while (common < a.length && a[common] === b[common]) common++;",
                  "console.log(\"Id 1:\", a, \"| Id 2:\", b, \"| common prefix:\", common, \"of\", a.length, \"characters\");",
                  "console.log(\"Invoices:\", pm.environment.get(\"invoiceNumberA\"), pm.environment.get(\"invoiceNumberB\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartGhicit}}"
          }
        }
      ]
    },
    {
      "name": "11 Administration",
      "item": [
        {
          "name": "T-I01 Admin lists users",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const rows = Array.isArray(json) ? json : json.data;",
                  "pm.test(\"Non-empty list\", () => pm.expect(rows).to.not.be.empty);",
                  "pm.test(\"No sensitive fields\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"\\\"password\\\"\"].forEach(key => pm.expect(t).to.not.include(key));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users"
          }
        },
        {
          "name": "T-I02 · 1 regular user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users"
          }
        },
        {
          "name": "T-I02 · 2 without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users"
          }
        },
        {
          "name": "T-I04 · 1 create",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"newProductId\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Test product {{runId}}\",\n  \"description\": \"Created by the API test\",\n  \"price\": 9.99,\n  \"category_id\": \"{{categoryId}}\",\n  \"brand_id\": \"{{brandId}}\",\n  \"product_image_id\": \"{{productImageId}}\",\n  \"is_location_offer\": false,\n  \"is_rental\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I04 · 2 update",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/products/{{newProductId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Test product {{runId}}\",\n  \"description\": \"Created by the API test\",\n  \"price\": 19.99,\n  \"category_id\": \"{{categoryId}}\",\n  \"brand_id\": \"{{brandId}}\",\n  \"product_image_id\": \"{{productImageId}}\",\n  \"is_location_offer\": false,\n  \"is_rental\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I04 · 3 read",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"New price persisted\", () => pm.expect(pm.response.json().price).to.eql(19.99));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{newProductId}}"
          }
        },
        {
          "name": "T-I04 · 4 delete",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/products/{{newProductId}}"
          }
        },
        {
          "name": "T-I04 · 5 read after delete",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 404\", () => pm.response.to.have.status(404));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{newProductId}}"
          }
        },
        {
          "name": "T-I05 · 1 admin creates product",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"productI05\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Test product {{runId}}\",\n  \"description\": \"Created by the API test\",\n  \"price\": 9.99,\n  \"category_id\": \"{{categoryId}}\",\n  \"brand_id\": \"{{brandId}}\",\n  \"product_image_id\": \"{{productImageId}}\",\n  \"is_location_offer\": false,\n  \"is_rental\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I05 · 2 user tries to delete",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/products/{{productI05}}"
          }
        },
        {
          "name": "T-I05 · 3 product still exists",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{productI05}}"
          }
        },
        {
          "name": "T-I05 · 4 cleanup (admin)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/products/{{productI05}}"
          }
        },
        {
          "name": "T-I07 · 1 A tries to delete B",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/users/{{userIdB}}"
          }
        },
        {
          "name": "T-I07 · 2 B can still log in",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenB\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailB}}\",\n  \"password\": \"{{passwordB}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I06 · 1 admin creates brand",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"brandQA\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/brands",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Brand QA {{runId}}\",\n  \"slug\": \"brand-qa-{{runId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I06 · 2 admin creates category",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"categoryQA\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/categories",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"QA Category {{runId}}\",\n  \"slug\": \"qa-category-{{runId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I06 · 3 brand without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/brands",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Brand QA anon-{{runId}}\",\n  \"slug\": \"brand-qa-anon-{{runId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I06 · 4 brand as regular user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/brands",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Brand QA user-{{runId}}\",\n  \"slug\": \"brand-qa-user-{{runId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I06 · 5 duplicate slug",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 409\", () => pm.response.to.have.status(409));",
                  "pm.test(\"Validation error on slug\", () => {",
                  "  const j = pm.response.json();",
                  "  pm.expect(j[\"slug\"] || (j.errors && j.errors[\"slug\"])).to.exist;",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/brands",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Brand QA {{runId}}\",\n  \"slug\": \"brand-qa-{{runId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I06 · 6 user deletes brand",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/brands/{{brandQA}}"
          }
        },
        {
          "name": "T-I06 · 7 admin deletes brand",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/brands/{{brandQA}}"
          }
        },
        {
          "name": "T-I06 · 8 admin deletes category",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/categories/{{categoryQA}}"
          }
        },
        {
          "name": "T-C16 · 1 without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}/specs",
            "body": {
              "mode": "raw",
              "raw": "{ \"spec_name\": \"Test\", \"spec_value\": \"QA\", \"spec_unit\": \"\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-C16 · 2 regular user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403]));",
                  "if (pm.response.code === 201) pm.environment.set(\"specDeSters\", pm.response.json().id); // for cleanup"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}/specs",
            "body": {
              "mode": "raw",
              "raw": "{ \"spec_name\": \"Test\", \"spec_value\": \"QA\", \"spec_unit\": \"\" }",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I03 · 1 create product without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Test product {{runId}}\",\n  \"description\": \"Created by the API test\",\n  \"price\": 9.99,\n  \"category_id\": \"{{categoryId}}\",\n  \"brand_id\": \"{{brandId}}\",\n  \"product_image_id\": \"{{productImageId}}\",\n  \"is_location_offer\": false,\n  \"is_rental\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I03 · 2 create product as user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Test product {{runId}}\",\n  \"description\": \"Created by the API test\",\n  \"price\": 9.99,\n  \"category_id\": \"{{categoryId}}\",\n  \"brand_id\": \"{{brandId}}\",\n  \"product_image_id\": \"{{productImageId}}\",\n  \"is_location_offer\": false,\n  \"is_rental\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I03 · 3 change price without token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PATCH",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"price\": 0.01\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I03 · 4 change price as user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "PATCH",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"price\": 0.01\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-I03 · 5 price unchanged",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Original price\", () => pm.expect(pm.response.json().price).to.eql(Number(pm.environment.get(\"productPrice\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/{{productId}}"
          }
        }
      ]
    },
    {
      "name": "12 Reports",
      "item": [
        {
          "name": "T-L01 Reports (admin) · total-sales-of-years",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Response has data\", () => pm.expect(pm.response.text().length).to.be.above(2));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-of-years"
          }
        },
        {
          "name": "T-L01 Reports (admin) · total-sales-per-country",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Response is an array\", () => pm.expect(pm.response.json()).to.be.an(\"array\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-per-country"
          }
        },
        {
          "name": "T-L01 Reports (admin) · top10-purchased-products",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Response has data\", () => pm.expect(pm.response.text().length).to.be.above(2));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-purchased-products"
          }
        },
        {
          "name": "T-L01 Reports (admin) · top10-best-selling-categories",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Response has data\", () => pm.expect(pm.response.text().length).to.be.above(2));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-best-selling-categories"
          }
        },
        {
          "name": "T-L01 Reports (admin) · customers-by-country",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Response has data\", () => pm.expect(pm.response.text().length).to.be.above(2));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/customers-by-country"
          }
        },
        {
          "name": "T-L01 Reports (admin) · average-sales-per-month",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Response has data\", () => pm.expect(pm.response.text().length).to.be.above(2));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/average-sales-per-month"
          }
        },
        {
          "name": "T-L01 Reports (admin) · average-sales-per-week",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Response has data\", () => pm.expect(pm.response.text().length).to.be.above(2));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/average-sales-per-week"
          }
        },
        {
          "name": "T-L02 · 1 top 10 products",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "const rows = Array.isArray(json) ? json : Object.values(json);",
                  "pm.test(\"At most 10 items\", () => pm.expect(rows.length).to.be.at.most(10));",
                  "const key = rows.length ? Object.keys(rows[0]).find(k => typeof rows[0][k] === \"number\") : null;",
                  "pm.test(\"Sorted descending by \" + key, () => { for (let i = 1; i < rows.length; i++) pm.expect(rows[i][key]).to.be.at.most(rows[i - 1][key]); });"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-purchased-products"
          }
        },
        {
          "name": "T-L02 · 2 top 10 categories",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const rows = pm.response.json();",
                  "pm.test(\"At most 10 items\", () => pm.expect(rows.length).to.be.at.most(10));",
                  "const key = \"total_earned\";",
                  "pm.test(\"Sorted descending by \" + key, () => {",
                  "  for (let i = 1; i < rows.length; i++) {",
                  "    pm.expect(Number(rows[i][key])).to.be.at.most(Number(rows[i - 1][key]));",
                  "  }",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-best-selling-categories"
          }
        },
        {
          "name": "T-L03 Reports with user token · total-sales-of-years",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-of-years"
          }
        },
        {
          "name": "T-L03 Reports with user token · total-sales-per-country",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-per-country"
          }
        },
        {
          "name": "T-L03 Reports with user token · top10-purchased-products",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-purchased-products"
          }
        },
        {
          "name": "T-L03 Reports with user token · top10-best-selling-categories",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-best-selling-categories"
          }
        },
        {
          "name": "T-L03 Reports with user token · customers-by-country",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/customers-by-country"
          }
        },
        {
          "name": "T-L03 Reports with user token · average-sales-per-month",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/average-sales-per-month"
          }
        },
        {
          "name": "T-L03 Reports with user token · average-sales-per-week",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 403\", () => pm.response.to.have.status(403));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/average-sales-per-week"
          }
        },
        {
          "name": "T-L03 Reports without token · total-sales-of-years",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-of-years"
          }
        },
        {
          "name": "T-L03 Reports without token · total-sales-per-country",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-per-country"
          }
        },
        {
          "name": "T-L03 Reports without token · top10-purchased-products",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-purchased-products"
          }
        },
        {
          "name": "T-L03 Reports without token · top10-best-selling-categories",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/top10-best-selling-categories"
          }
        },
        {
          "name": "T-L03 Reports without token · customers-by-country",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/customers-by-country"
          }
        },
        {
          "name": "T-L03 Reports without token · average-sales-per-month",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/average-sales-per-month"
          }
        },
        {
          "name": "T-L03 Reports without token · average-sales-per-week",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/average-sales-per-week"
          }
        },
        {
          "name": "T-L04 Repeated report",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [200, 429]\", () => pm.expect(pm.response.code).to.be.oneOf([200, 429]));",
                  "if (pm.response.code === 429) console.log(\"First 429 at iteration\", pm.info.iteration + 1, \"| Retry-After:\", pm.response.headers.get(\"Retry-After\"));",
                  "console.log(\"Limit:\", pm.response.headers.get(\"X-RateLimit-Limit\"), \"| remaining:\", pm.response.headers.get(\"X-RateLimit-Remaining\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/reports/total-sales-of-years"
          }
        }
      ]
    },
    {
      "name": "13 Dedicated users",
      "item": [
        {
          "name": "T-A10 · 1 · register dedicated user",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.environment.set(\"emailPW\", \"qa+pw-\" + id + \"@test.local\");",
                  "pm.environment.set(\"passwordPW\", \"Qa-Test!2026#PW\" + id);",
                  "pm.variables.set(\"email\", pm.environment.get(\"emailPW\"));",
                  "pm.variables.set(\"password\", pm.environment.get(\"passwordPW\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"userIdPW\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A10 · 2 · login dedicated user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenPW\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailPW}}\",\n  \"password\": \"{{passwordPW}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A10 · 3 change-password",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"success = true\", () => pm.expect(pm.response.json().success).to.eql(true));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenPW}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/change-password",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"current_password\": \"{{passwordPW}}\",\n  \"new_password\": \"Nou-Pass!2026x\",\n  \"new_password_confirmation\": \"Nou-Pass!2026x\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A10 · 4 login with new password",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailPW}}\",\n  \"password\": \"Nou-Pass!2026x\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A10 · 5 login with old password",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailPW}}\",\n  \"password\": \"{{passwordPW}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A11 · 1 · register dedicated user",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.environment.set(\"emailFP\", \"qa+fp-\" + id + \"@test.local\");",
                  "pm.environment.set(\"passwordFP\", \"Qa-Test!2026#FP\" + id);",
                  "pm.variables.set(\"email\", pm.environment.get(\"emailFP\"));",
                  "pm.variables.set(\"password\", pm.environment.get(\"passwordFP\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"userIdFP\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A11 · 2 existing email",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "pm.environment.set(\"a11Status\", pm.response.code);",
                  "pm.environment.set(\"a11Body\", pm.response.text());"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/forgot-password",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailFP}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A11 · 3 unknown email",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "pm.test(\"Same status as for the existing email\", () => pm.expect(pm.response.code).to.eql(Number(pm.environment.get(\"a11Status\"))));",
                  "pm.test(\"Same message as for the existing email\", () => pm.expect(pm.response.text()).to.eql(pm.environment.get(\"a11Body\")));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/forgot-password",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"nu-exista-{{runId}}@test.local\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A12 · 1 · register dedicated user",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.environment.set(\"emailLK\", \"qa+lk-\" + id + \"@test.local\");",
                  "pm.environment.set(\"passwordLK\", \"Qa-Test!2026#LK\" + id);",
                  "pm.variables.set(\"email\", pm.environment.get(\"emailLK\"));",
                  "pm.variables.set(\"password\", pm.environment.get(\"passwordLK\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"userIdLK\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A12 · 2 wrong password, attempt 1",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailLK}}\",\n  \"password\": \"Gresita!123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A12 · 3 wrong password, attempt 2",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailLK}}\",\n  \"password\": \"Gresita!123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A12 · 4 wrong password, attempt 3",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailLK}}\",\n  \"password\": \"Gresita!123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A12 · 5 wrong password, attempt 4",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 423\", () => pm.response.to.have.status(423));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailLK}}\",\n  \"password\": \"Gresita!123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A12 · 6 correct password after lockout",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 423\", () => pm.response.to.have.status(423));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));",
                  "pm.test(\"Account locked message\", () => pm.expect(pm.response.json().error).to.include(\"locked\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailLK}}\",\n  \"password\": \"{{passwordLK}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A15 · 1 · register dedicated user",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.environment.set(\"emailTP\", \"qa+tp-\" + id + \"@test.local\");",
                  "pm.environment.set(\"passwordTP\", \"Qa-Test!2026#TP\" + id);",
                  "pm.variables.set(\"email\", pm.environment.get(\"emailTP\"));",
                  "pm.variables.set(\"password\", pm.environment.get(\"passwordTP\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 201\", () => pm.response.to.have.status(201));",
                  "pm.environment.set(\"userIdTP\", pm.response.json().id);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A15 · 2 · login dedicated user",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.environment.set(\"tokenTP\", pm.response.json().access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailTP}}\",\n  \"password\": \"{{passwordTP}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A15 · 3 setup",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"Secret received\", () => pm.expect(json.secret).to.be.a(\"string\").and.not.empty);",
                  "pm.environment.set(\"totpSecret\", json.secret);",
                  "// Mask the secret when saving the evidence."
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenTP}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/totp/setup"
          }
        },
        {
          "name": "T-A15 · 4 verify",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Compute the TOTP code (RFC 6238, 30 s, 6 digits) from the secret saved at setup.",
                  "function base32ToHex(s) {",
                  "  const alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\";",
                  "  let bits = \"\", hex = \"\";",
                  "  s = s.replace(/=+$/, \"\").toUpperCase();",
                  "  for (const c of s) bits += alphabet.indexOf(c).toString(2).padStart(5, \"0\");",
                  "  for (let i = 0; i + 8 <= bits.length; i += 8) hex += parseInt(bits.substr(i, 8), 2).toString(16).padStart(2, \"0\");",
                  "  return hex;",
                  "}",
                  "const counter = Math.floor(Date.now() / 30000).toString(16).padStart(16, \"0\");",
                  "const key = CryptoJS.enc.Hex.parse(base32ToHex(pm.environment.get(\"totpSecret\")));",
                  "const hmac = CryptoJS.HmacSHA1(CryptoJS.enc.Hex.parse(counter), key).toString();",
                  "const offset = parseInt(hmac.slice(-1), 16);",
                  "const code = (parseInt(hmac.substr(offset * 2, 8), 16) & 0x7fffffff) % 1000000;",
                  "pm.variables.set(\"totpCode\", String(code).padStart(6, \"0\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenTP}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/totp/verify",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"totp\": \"{{totpCode}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A15 · 5 login with password",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "const json = pm.response.json();",
                  "pm.test(\"requires_totp = true\", () => pm.expect(json.requires_totp).to.eql(true));",
                  "pm.environment.set(\"tokenTemp\", json.access_token);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{emailTP}}\",\n  \"password\": \"{{passwordTP}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A15 · 6 me with temporary token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403]));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenTemp}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        },
        {
          "name": "T-A15 · 7 login with correct code",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Compute the TOTP code (RFC 6238, 30 s, 6 digits) from the secret saved at setup.",
                  "function base32ToHex(s) {",
                  "  const alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\";",
                  "  let bits = \"\", hex = \"\";",
                  "  s = s.replace(/=+$/, \"\").toUpperCase();",
                  "  for (const c of s) bits += alphabet.indexOf(c).toString(2).padStart(5, \"0\");",
                  "  for (let i = 0; i + 8 <= bits.length; i += 8) hex += parseInt(bits.substr(i, 8), 2).toString(16).padStart(2, \"0\");",
                  "  return hex;",
                  "}",
                  "const counter = Math.floor(Date.now() / 30000).toString(16).padStart(16, \"0\");",
                  "const key = CryptoJS.enc.Hex.parse(base32ToHex(pm.environment.get(\"totpSecret\")));",
                  "const hmac = CryptoJS.HmacSHA1(CryptoJS.enc.Hex.parse(counter), key).toString();",
                  "const offset = parseInt(hmac.slice(-1), 16);",
                  "const code = (parseInt(hmac.substr(offset * 2, 8), 16) & 0x7fffffff) % 1000000;",
                  "pm.variables.set(\"totpCode\", String(code).padStart(6, \"0\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"Full token received\", () => pm.expect(pm.response.json().access_token).to.be.a(\"string\").and.not.empty);"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"access_token\": \"{{tokenTemp}}\",\n  \"totp\": \"{{totpCode}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A15 · 8 login with wrong code",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [400, 401]\", () => pm.expect(pm.response.code).to.be.oneOf([400, 401]));",
                  "pm.test(\"No access_token in response\", () => pm.expect(pm.response.text()).to.not.include(\"access_token\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"access_token\": \"{{tokenTemp}}\",\n  \"totp\": \"000000\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-A15 · 9 route /totp/login/totp",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Compute the TOTP code (RFC 6238, 30 s, 6 digits) from the secret saved at setup.",
                  "function base32ToHex(s) {",
                  "  const alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\";",
                  "  let bits = \"\", hex = \"\";",
                  "  s = s.replace(/=+$/, \"\").toUpperCase();",
                  "  for (const c of s) bits += alphabet.indexOf(c).toString(2).padStart(5, \"0\");",
                  "  for (let i = 0; i + 8 <= bits.length; i += 8) hex += parseInt(bits.substr(i, 8), 2).toString(16).padStart(2, \"0\");",
                  "  return hex;",
                  "}",
                  "const counter = Math.floor(Date.now() / 30000).toString(16).padStart(16, \"0\");",
                  "const key = CryptoJS.enc.Hex.parse(base32ToHex(pm.environment.get(\"totpSecret\")));",
                  "const hmac = CryptoJS.HmacSHA1(CryptoJS.enc.Hex.parse(counter), key).toString();",
                  "const offset = parseInt(hmac.slice(-1), 16);",
                  "const code = (parseInt(hmac.substr(offset * 2, 8), 16) & 0x7fffffff) % 1000000;",
                  "pm.variables.set(\"totpCode\", String(code).padStart(6, \"0\"));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenTP}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/totp/login/totp",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"access_token\": \"{{tokenTemp}}\",\n  \"totp\": \"{{totpCode}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "14 Contract",
      "item": [
        {
          "name": "T-K04 Wrong types · quantity as text",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"product_id\": \"{{productId}}\",\n  \"quantity\": \"doi\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K04 Wrong types · price as text",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Test product {{runId}}\",\n  \"description\": \"Created by the API test\",\n  \"price\": \"ieftin\",\n  \"category_id\": \"{{categoryId}}\",\n  \"brand_id\": \"{{brandId}}\",\n  \"product_image_id\": \"{{productImageId}}\",\n  \"is_location_offer\": false,\n  \"is_rental\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K04 Wrong types · is_rental as text",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenAdmin}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/products",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Test product {{runId}}\",\n  \"description\": \"Created by the API test\",\n  \"price\": 9.99,\n  \"category_id\": \"{{categoryId}}\",\n  \"brand_id\": \"{{brandId}}\",\n  \"product_image_id\": \"{{productImageId}}\",\n  \"is_location_offer\": false,\n  \"is_rental\": \"poate\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K04 Wrong types · first_name as number",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": 123,\n  \"last_name\": \"Tester\",\n  \"address\": {\n    \"street\": \"Teststraat 1\",\n    \"city\": \"Amsterdam\",\n    \"state\": \"Noord-Holland\",\n    \"country\": \"NL\",\n    \"postal_code\": \"1011 AB\"\n  },\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K04 Wrong types · address as text",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const id = Date.now();",
                  "pm.variables.set(\"email\", \"qa+\" + id + \"@test.local\");",
                  "pm.variables.set(\"password\", \"Qa-Test!2026#\" + id);"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 422\", () => pm.response.to.have.status(422));",
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"first_name\": \"QA\",\n  \"last_name\": \"Tester\",\n  \"address\": \"text\",\n  \"phone\": \"0612345678\",\n  \"dob\": \"1990-01-01\",\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K05 Broken body · login, unterminated JSON",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [400, 401, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([400, 401, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\"email\": ",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K05 Broken body · register, plain text",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [400, 401, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([400, 401, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/register",
            "body": {
              "mode": "raw",
              "raw": "nu e json",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K05 Broken body · invoices, unterminated JSON",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [400, 401, 422]\", () => pm.expect(pm.response.code).to.be.oneOf([400, 401, 422]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenA}}",
                  "type": "string"
                }
              ]
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/invoices",
            "body": {
              "mode": "raw",
              "raw": "{\"cart_id\": ",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "T-K06 Invalid query parameters · page=abc",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "console.log(pm.info.requestName, \"→\", pm.response.code); // record the behavior"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?page=abc"
          }
        },
        {
          "name": "T-K06 Invalid query parameters · page=-1",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "console.log(pm.info.requestName, \"→\", pm.response.code); // record the behavior"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?page=-1"
          }
        },
        {
          "name": "T-K06 Invalid query parameters · page=99999",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "console.log(pm.info.requestName, \"→\", pm.response.code); // record the behavior"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?page=99999"
          }
        },
        {
          "name": "T-K06 Invalid query parameters · unknown sort",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "console.log(pm.info.requestName, \"→\", pm.response.code); // record the behavior"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?sort=nu_exista,asc"
          }
        },
        {
          "name": "T-K06 Invalid query parameters · invalid between",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No server error (5xx)\", () => pm.expect(pm.response.code).to.be.below(500));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "console.log(pm.info.requestName, \"→\", pm.response.code); // record the behavior"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products?between=price,abc,10"
          }
        },
        {
          "name": "T-K07 Method not allowed · DELETE /status",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 405]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 405]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/status"
          }
        },
        {
          "name": "T-K07 Method not allowed · PUT /users/login",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [401, 403, 404, 405]\", () => pm.expect(pm.response.code).to.be.oneOf([401, 403, 404, 405]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PUT",
            "header": [],
            "url": "{{baseUrl}}/users/login"
          }
        },
        {
          "name": "T-K07 Method not allowed · PATCH /carts",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status in [404, 405]\", () => pm.expect(pm.response.code).to.be.oneOf([404, 405]));",
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "PATCH",
            "header": [],
            "url": "{{baseUrl}}/carts"
          }
        },
        {
          "name": "T-K08 · 1 OPTIONS /products",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));",
                  "const allow = pm.response.headers.get(\"Allow\") || \"\";",
                  "pm.test(\"Allow contains GET, POST, QUERY\", () => [\"GET\", \"POST\", \"QUERY\"].forEach(m => pm.expect(allow).to.include(m)));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "OPTIONS",
            "header": [],
            "url": "{{baseUrl}}/products"
          }
        },
        {
          "name": "T-K08 · 2 OPTIONS /carts/{id}",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 204\", () => pm.response.to.have.status(204));",
                  "const allow = pm.response.headers.get(\"Allow\") || \"\";",
                  "pm.test(\"Allow contains DELETE, GET, POST\", () => [\"DELETE\", \"GET\", \"POST\"].forEach(m => pm.expect(allow).to.include(m)));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "OPTIONS",
            "header": [],
            "url": "{{baseUrl}}/carts/{{cartId}}"
          }
        },
        {
          "name": "T-K09 Errors without internal details · apostrophe in id",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "pm.test(\"No trace or exception in JSON\", () => { const t = pm.response.text(); pm.expect(t).to.not.include(\"\\\"trace\\\"\"); pm.expect(t).to.not.include(\"\\\"exception\\\"\"); });"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/products/'"
          }
        },
        {
          "name": "T-K09 Errors without internal details · non-existent route",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "pm.test(\"No trace or exception in JSON\", () => { const t = pm.response.text(); pm.expect(t).to.not.include(\"\\\"trace\\\"\"); pm.expect(t).to.not.include(\"\\\"exception\\\"\"); });"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/nu-exista"
          }
        },
        {
          "name": "T-K09 Errors without internal details · broken JSON",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"No internal details in response\", () => {",
                  "  const t = pm.response.text();",
                  "  [\"SQLSTATE\", \"vendor/\", \"Stack trace\", \"/var/www\", \"Exception\"].forEach(w => pm.expect(t).to.not.include(w));",
                  "});",
                  "pm.test(\"No trace or exception in JSON\", () => { const t = pm.response.text(); pm.expect(t).to.not.include(\"\\\"trace\\\"\"); pm.expect(t).to.not.include(\"\\\"exception\\\"\"); });"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/users/login",
            "body": {
              "mode": "raw",
              "raw": "{\"email\": ",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "15 Time dependent",
      "item": [
        {
          "name": "T-J10 Expired token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 401\", () => pm.response.to.have.status(401));",
                  "pm.test(\"Token had actually expired at request time\", () => pm.expect(Date.now()).to.be.above(Number(pm.environment.get(\"tokenOldExpiresAt\"))));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{tokenOld}}",
                  "type": "string"
                }
              ]
            },
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/users/me"
          }
        }
      ]
    },
    {
      "name": "16 Database reset",
      "item": [
        {
          "name": "T-K10 · 1 refresh",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 200\", () => pm.response.to.have.status(200));",
                  "pm.test(\"refresh done\", () => pm.expect(pm.response.json().result).to.eql(\"refresh done\"));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/refresh"
          }
        },
        {
          "name": "T-K10 · 2 immediate refresh",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Status 429\", () => pm.response.to.have.status(429));"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/refresh"
          }
        }
      ]
    }
  ],
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Clienții JSON trimit Accept: application/json. Excepție: T-A14 cu Accept implicit, pentru DEF-004.",
          "if (!pm.info.requestName.startsWith(\"T-A14 · too short\")) {",
          "  pm.request.headers.upsert({ key: \"Accept\", value: \"application/json\" });",
          "}"
        ]
      }
    }
  ]
}
