{
  "info": {
    "name": "QA Orbit · API-CORE-1 local lab",
    "description": "Original educational collection. Synthetic local model, not a production API. Requires running api-lab.mjs on loopback. Reset/Create/Read/Publish/Repeat run in order. No real secrets.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "http://127.0.0.1:4177",
      "type": "string"
    },
    {
      "key": "studentToken",
      "value": "demo-anna",
      "type": "string"
    },
    {
      "key": "taskId",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "01 Reset synthetic lab",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "X-Lab-Key",
            "value": "local-only",
            "type": "text"
          }
        ],
        "url": "{{baseUrl}}/__lab/reset",
        "description": "API-CORE-1: local synthetic lab only. Run full collection in order. See README.md and contract.md."
      },
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('lab reset 200', () => pm.response.to.have.status(200));",
              "pm.test('reset confirmed', () => pm.expect(pm.response.json().reset).to.eql(true));"
            ]
          }
        }
      ]
    },
    {
      "name": "02 Create current task",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Authorization",
            "value": "Bearer {{studentToken}}",
            "type": "text"
          },
          {
            "key": "Content-Type",
            "value": "application/json",
            "type": "text"
          }
        ],
        "url": "{{baseUrl}}/tasks",
        "description": "API-CORE-1: local synthetic lab only. Run full collection in order. See README.md and contract.md.",
        "body": {
          "mode": "raw",
          "raw": "{\n  \"title\": \"Коллекция\"\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        }
      },
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.collectionVariables.unset('taskId');",
              "pm.variables.unset('taskId');"
            ]
          }
        },
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('201 on create', () => pm.response.to.have.status(201));",
              "pm.test('JSON media type', () => pm.expect(pm.response.headers.get('Content-Type')).to.match(/^application\\/json(?:;|$)/i));",
              "const data = pm.response.json();",
              "pm.test('nonempty generated ID', () => { pm.expect(data.id).to.be.a('string'); pm.expect(data.id.length).to.be.above(0); });",
              "pm.test('author and team from token', () => { pm.expect(data.authorId).to.eql('u-a'); pm.expect(data.team).to.eql('north'); });",
              "pm.test('exact title retained', () => pm.expect(data.title).to.eql('Коллекция'));",
              "pm.test('defaults and draft', () => { pm.expect(data.description).to.eql(''); pm.expect(data.priority).to.eql('normal'); pm.expect(data.state).to.eql('draft'); });",
              "pm.test('Location matches current ID', () => pm.expect(pm.response.headers.get('Location')).to.eql('/tasks/' + data.id));",
              "if (pm.response.code === 201 && typeof data.id === 'string' && data.id.length > 0) { pm.collectionVariables.set('taskId', data.id); }"
            ]
          }
        }
      ]
    },
    {
      "name": "03 Read current draft",
      "request": {
        "method": "GET",
        "header": [
          {
            "key": "Authorization",
            "value": "Bearer {{studentToken}}",
            "type": "text"
          }
        ],
        "url": "{{baseUrl}}/tasks/{{taskId}}",
        "description": "API-CORE-1: local synthetic lab only. Run full collection in order. See README.md and contract.md."
      },
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const id = pm.collectionVariables.get('taskId');",
              "if (!id) {",
              "  console.error('Run successful Create in this collection first; no current taskId. Request skipped, not passed.');",
              "  pm.execution.skipRequest();",
              "} else {",
              "  pm.variables.set('taskId', id);",
              "}"
            ]
          }
        },
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('200 read current draft', () => pm.response.to.have.status(200));",
              "const data = pm.response.json();",
              "pm.test('read same ID', () => pm.expect(data.id).to.eql(pm.collectionVariables.get('taskId')));",
              "pm.test('draft and exact title', () => { pm.expect(data.state).to.eql('draft'); pm.expect(data.title).to.eql('Коллекция'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "04 Publish current draft",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Authorization",
            "value": "Bearer {{studentToken}}",
            "type": "text"
          }
        ],
        "url": "{{baseUrl}}/tasks/{{taskId}}/publish",
        "description": "API-CORE-1: local synthetic lab only. Run full collection in order. See README.md and contract.md."
      },
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const id = pm.collectionVariables.get('taskId');",
              "if (!id) {",
              "  console.error('Run successful Create in this collection first; no current taskId. Request skipped, not passed.');",
              "  pm.execution.skipRequest();",
              "} else {",
              "  pm.variables.set('taskId', id);",
              "}"
            ]
          }
        },
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('200 publish', () => pm.response.to.have.status(200));",
              "const data = pm.response.json();",
              "pm.test('same ID published', () => { pm.expect(data.id).to.eql(pm.collectionVariables.get('taskId')); pm.expect(data.state).to.eql('published'); });",
              "pm.test('title and author preserved', () => { pm.expect(data.title).to.eql('Коллекция'); pm.expect(data.authorId).to.eql('u-a'); });"
            ]
          }
        }
      ]
    },
    {
      "name": "05 Repeat publication rejected",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Authorization",
            "value": "Bearer {{studentToken}}",
            "type": "text"
          }
        ],
        "url": "{{baseUrl}}/tasks/{{taskId}}/publish",
        "description": "API-CORE-1: local synthetic lab only. Run full collection in order. See README.md and contract.md."
      },
      "event": [
        {
          "listen": "prerequest",
          "script": {
            "type": "text/javascript",
            "exec": [
              "const id = pm.collectionVariables.get('taskId');",
              "if (!id) {",
              "  console.error('Run successful Create in this collection first; no current taskId. Request skipped, not passed.');",
              "  pm.execution.skipRequest();",
              "} else {",
              "  pm.variables.set('taskId', id);",
              "}"
            ]
          }
        },
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('409 repeat publication', () => pm.response.to.have.status(409));",
              "pm.test('explicit conflict code', () => pm.expect(pm.response.json().error.code).to.eql('ALREADY_PUBLISHED'));"
            ]
          }
        }
      ]
    }
  ]
}
