{
  "info": {
    "name": "LifeOS API",
    "_postman_id": "a1f1c0de-life-os-api-coll-0001",
    "description": "Auth + CRUD coverage for tasks, goals, habits, and /import/local. Set the `base_url`, `email`, `password` collection variables, then run **Auth → Login** first; the test script captures the JWT into `{{token}}` for subsequent requests. Folder order = recommended Collection Runner order.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    { "key": "base_url", "value": "https://api.example.com/api" },
    { "key": "email",    "value": "postman+test@lifeos.test" },
    { "key": "password", "value": "Postman!Pass123" },
    { "key": "token",    "value": "" },
    { "key": "goal_id",  "value": "" },
    { "key": "task_id",  "value": "" },
    { "key": "habit_id", "value": "" },
    { "key": "today",    "value": "" }
  ],
  "auth": {
    "type": "bearer",
    "bearer": [{ "key": "token", "value": "{{token}}", "type": "string" }]
  },
  "event": [
    { "listen": "prerequest", "script": { "type": "text/javascript", "exec": [
      "if (!pm.collectionVariables.get('today')) {",
      "  pm.collectionVariables.set('today', new Date().toISOString().slice(0,10));",
      "}"
    ]}}
  ],
  "item": [
    {
      "name": "Health",
      "item": [
        { "name": "GET /health", "request": { "method": "GET", "url": "{{base_url}}/health" },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200 OK', () => pm.response.to.have.status(200));"
          ]}}]
        }
      ]
    },
    {
      "name": "Auth",
      "item": [
        { "name": "Signup", "request": {
            "method": "POST", "url": "{{base_url}}/auth/signup",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\",\n  \"full_name\": \"Postman Tester\"\n}" }
          },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('signup 200/201/409', () => pm.expect([200,201,409]).to.include(pm.response.code));"
          ]}}]
        },
        { "name": "Login", "request": {
            "method": "POST", "url": "{{base_url}}/auth/login",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}" }
          },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200 OK', () => pm.response.to.have.status(200));",
            "const j = pm.response.json();",
            "pm.expect(j.token, 'token').to.be.a('string');",
            "pm.collectionVariables.set('token', j.token);"
          ]}}]
        },
        { "name": "Me", "request": { "method": "GET", "url": "{{base_url}}/auth/me" },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200 OK', () => pm.response.to.have.status(200));"
          ]}}]
        },
        { "name": "Tasks unauthenticated → 401",
          "request": { "method": "GET", "url": "{{base_url}}/tasks", "auth": { "type": "noauth" } },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('401 or 403', () => pm.expect([401,403]).to.include(pm.response.code));"
          ]}}]
        }
      ]
    },
    {
      "name": "Goals",
      "item": [
        { "name": "Create",
          "request": { "method": "POST", "url": "{{base_url}}/goals",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"title\": \"Run 5K\",\n  \"target_value\": 5,\n  \"current_value\": 0,\n  \"unit\": \"km\",\n  \"deadline\": \"2026-12-31\"\n}" }
          },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200', () => pm.response.to.have.status(200));",
            "pm.collectionVariables.set('goal_id', pm.response.json().id);"
          ]}}]
        },
        { "name": "List",   "request": { "method": "GET",  "url": "{{base_url}}/goals" },
          "event": [{ "listen": "test", "script": { "exec": ["pm.test('200', () => pm.response.to.have.status(200));"] }}] },
        { "name": "Update", "request": { "method": "PATCH", "url": "{{base_url}}/goals/{{goal_id}}",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"current_value\": 2.5\n}" } },
          "event": [{ "listen": "test", "script": { "exec": ["pm.test('200', () => pm.response.to.have.status(200));"] }}] }
      ]
    },
    {
      "name": "Tasks",
      "item": [
        { "name": "Create",
          "request": { "method": "POST", "url": "{{base_url}}/tasks",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"title\": \"Morning run\",\n  \"task_date\": \"{{today}}\",\n  \"goal_id\": \"{{goal_id}}\"\n}" } },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200', () => pm.response.to.have.status(200));",
            "pm.collectionVariables.set('task_id', pm.response.json().id);"
          ]}}]
        },
        { "name": "List by date",
          "request": { "method": "GET", "url": "{{base_url}}/tasks?date={{today}}" },
          "event": [{ "listen": "test", "script": { "exec": ["pm.test('200', () => pm.response.to.have.status(200));"] }}] },
        { "name": "Complete",
          "request": { "method": "PATCH", "url": "{{base_url}}/tasks/{{task_id}}",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"completed\": true\n}" } },
          "event": [{ "listen": "test", "script": { "exec": ["pm.test('200', () => pm.response.to.have.status(200));"] }}] },
        { "name": "Delete",
          "request": { "method": "DELETE", "url": "{{base_url}}/tasks/{{task_id}}" },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200/204', () => pm.expect([200,204]).to.include(pm.response.code));"
          ]}}] }
      ]
    },
    {
      "name": "Habits",
      "item": [
        { "name": "Create",
          "request": { "method": "POST", "url": "{{base_url}}/habits",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"name\": \"Drink water\",\n  \"target\": 8,\n  \"unit\": \"glasses\",\n  \"frequency\": \"daily\"\n}" } },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200', () => pm.response.to.have.status(200));",
            "pm.collectionVariables.set('habit_id', pm.response.json().id);"
          ]}}]
        },
        { "name": "Log today",
          "request": { "method": "POST", "url": "{{base_url}}/habits/{{habit_id}}/logs",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"log_date\": \"{{today}}\",\n  \"value\": 8\n}" } },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200/201', () => pm.expect([200,201]).to.include(pm.response.code));"
          ]}}] },
        { "name": "List + streak",
          "request": { "method": "GET", "url": "{{base_url}}/habits" },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200', () => pm.response.to.have.status(200));",
            "const arr = pm.response.json().habits || pm.response.json();",
            "const mine = arr.find(h => h.id === pm.collectionVariables.get('habit_id'));",
            "pm.test('streak >= 1', () => pm.expect(mine.current_streak).to.be.at.least(1));"
          ]}}] },
        { "name": "Delete",
          "request": { "method": "DELETE", "url": "{{base_url}}/habits/{{habit_id}}" },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200/204', () => pm.expect([200,204]).to.include(pm.response.code));"
          ]}}] }
      ]
    },
    {
      "name": "Import (localStorage migration)",
      "item": [
        { "name": "POST /import/local",
          "request": { "method": "POST", "url": "{{base_url}}/import/local",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"goals\":  [{ \"local_id\": \"g1\", \"title\": \"Read 12 books\", \"target_value\": 12, \"current_value\": 1, \"unit\": \"books\" }],\n  \"tasks\":  [{ \"title\": \"Read 10 pages\", \"task_date\": \"{{today}}\", \"goal_local_id\": \"g1\" }],\n  \"habits\": [{ \"name\": \"Meditate\", \"target\": 1, \"unit\": \"session\", \"frequency\": \"daily\",\n                \"logs\": [{ \"log_date\": \"{{today}}\", \"value\": 1 }] }]\n}" } },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200', () => pm.response.to.have.status(200));",
            "const j = pm.response.json();",
            "pm.test('goals imported',  () => pm.expect(j.goals_imported  ?? j.goals  ?? 0).to.be.at.least(1));",
            "pm.test('tasks imported',  () => pm.expect(j.tasks_imported  ?? j.tasks  ?? 0).to.be.at.least(1));",
            "pm.test('habits imported', () => pm.expect(j.habits_imported ?? j.habits ?? 0).to.be.at.least(1));"
          ]}}] },
        { "name": "POST /import/local (idempotent rerun)",
          "request": { "method": "POST", "url": "{{base_url}}/import/local",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": { "mode": "raw", "raw": "{\n  \"goals\": [{ \"local_id\": \"g1\", \"title\": \"Read 12 books\", \"target_value\": 12, \"current_value\": 1, \"unit\": \"books\" }],\n  \"tasks\": [{ \"title\": \"Read 10 pages\", \"task_date\": \"{{today}}\", \"goal_local_id\": \"g1\" }],\n  \"habits\": [{ \"name\": \"Meditate\", \"target\": 1, \"unit\": \"session\", \"frequency\": \"daily\" }]\n}" } },
          "event": [{ "listen": "test", "script": { "exec": [
            "pm.test('200 on rerun', () => pm.response.to.have.status(200));"
          ]}}] }
      ]
    }
  ]
}
