advanced-rag-backend / POSTMAN_TESTING_GUIDE.md
Ashutosh1975270's picture
feat: complete frontend and backend integration and security configurations
3786a3f
|
Raw
History Blame Contribute Delete
41.4 kB

Postman Testing Guide - GraphRAG Knowledge Graph AI API

Complete Step-by-Step API Testing Guide for Django REST API with JWT Authentication


Table of Contents

  1. Postman Installation & Setup
  2. Creating a Collection
  3. Setting Up Environment Variables
  4. Endpoint-by-Endpoint Testing Guide
  5. Chaining Requests with JWT Tokens
  6. Writing Post-Response Tests
  7. Common Errors & Troubleshooting
  8. Using Postman Runner for Batch Testing

1. Postman Installation & Setup

Step 1.1: Download Postman

1. Go to https://www.postman.com/downloads/
2. Choose your OS (Windows / Mac / Linux)
3. Download and run the installer
4. Sign up for a free account (optional but recommended for sync)

Step 1.2: Verify Installation

Open Postman -> You should see the main workspace with:
- Left sidebar (Collections, History)
- Center panel (Request builder)
- Right panel (Response viewer)

Step 1.3: Disable SSL Certificate Verification (for localhost)

1. Click the gear icon (Settings) in the top-right
2. Go to "General" tab
3. Turn OFF "SSL certificate verification"
4. This is needed because localhost often uses self-signed certs

2. Creating a Collection

Step 2.1: Create the Collection

1. Click "Collections" in the left sidebar
2. Click the "+" button or "Create Collection"
3. Name it: "GraphRAG Knowledge Graph AI API"
4. Click the "..." next to the collection name -> "Edit"
5. Go to "Authorization" tab
6. Set Type to: "Bearer Token"
7. In the Token field, enter: {{access_token}}
8. Click "Save"

Step 2.2: Organize with Folders

Right-click the collection and create these folders:

GraphRAG Knowledge Graph AI API/
β”œβ”€β”€ 01 - Health Check
β”œβ”€β”€ 02 - Authentication
β”œβ”€β”€ 03 - Document Management
β”œβ”€β”€ 04 - Query Endpoints
β”œβ”€β”€ 05 - Graph Endpoints
└── 06 - Evaluation

To create a folder:

1. Right-click on the collection name
2. Select "Add Folder"
3. Name the folder
4. Drag requests into the appropriate folder

3. Setting Up Environment Variables

Step 3.1: Create an Environment

1. Click the "Environments" tab in the left sidebar
2. Click "Create Environment"
3. Name it: "GraphRAG Local Dev"
4. Click "Add" to create variables

Step 3.2: Add Variables

Add each of these variables with their initial values:

Variable Initial Value Description
base_url http://localhost:8000 API base URL
access_token JWT access token (auto-filled)
refresh_token JWT refresh token (auto-filled)
test_username testuser_01 Test user username
test_email testuser01@example.com Test user email
test_password SecureP@ss1 Test user password
uploaded_doc_id Document ID (auto-filled)

Step 3.3: Activate the Environment

1. Top-right corner: click the Environment dropdown
2. Select "GraphRAG Local Dev"
3. The eye icon shows current variable values

4. Endpoint-by-Endpoint Testing Guide


ENDPOINT 1: POST /api/auth/register/

Folder: 02 - Authentication

Setup:

Name: Register User
Method: POST
URL: {{base_url}}/api/auth/register/

Headers:

Content-Type: application/json

Body (raw JSON):

{
    "username": "{{test_username}}",
    "email": "{{test_email}}",
    "password": "{{test_password}}",
    "confirm_password": "{{test_password}}"
}

Expected Response:

  • Status: 201 Created
  • Body:
{
    "message": "User registered successfully.",
    "user": {
        "id": 1,
        "username": "testuser_01",
        "email": "testuser01@example.com"
    }
}

What to Check:

  • message field confirms success
  • user object contains id, username, email
  • No password field is returned (security!)

Post-Response Script (Tests tab):

pm.test("Status code is 201", function () {
    pm.response.to.have.status(201);
});

pm.test("Response has success message", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.message).to.eql("User registered successfully.");
});

pm.test("User object exists without password", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.user).to.have.property("id");
    pm.expect(jsonData.user).to.have.property("username");
    pm.expect(jsonData.user).to.not.have.property("password");
});

ENDPOINT 2: POST /api/auth/login/

Folder: 02 - Authentication

Setup:

Name: Login (Get JWT Tokens)
Method: POST
URL: {{base_url}}/api/auth/login/

Headers:

Content-Type: application/json

Body (raw JSON):

{
    "username": "{{test_username}}",
    "password": "{{test_password}}"
}

Expected Response:

  • Status: 200 OK
  • Body:
{
    "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

What to Check:

  • access field contains a long JWT string
  • refresh field contains a long JWT string
  • Both tokens start with eyJ (base64-encoded JWT)

Post-Response Script (Tests tab) - CRITICAL - Saves tokens for all other requests:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Response contains access token", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("access");
    pm.expect(jsonData.access).to.be.a("string");
    // Save to environment variable for other requests
    pm.environment.set("access_token", jsonData.access);
});

pm.test("Response contains refresh token", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("refresh");
    pm.expect(jsonData.refresh).to.be.a("string");
    pm.environment.set("refresh_token", jsonData.refresh);
});

ENDPOINT 3: POST /api/documents/upload/

Folder: 03 - Document Management

Setup:

Name: Upload Document
Method: POST
URL: {{base_url}}/api/documents/upload/

Headers:

Authorization: Bearer {{access_token}}
Content-Type: multipart/form-data

Body (form-data):

Key: file
Type: File
Value: [Select a PDF, TXT, MD, CSV, JSON, HTML, XML, DOCX, or DOC file from your computer]

Important: In the Body tab, select "form-data", then:

  1. In the Key column, type file
  2. Hover over the Key field - a dropdown appears on the right
  3. Change from "Text" to "File"
  4. Click the "Select Files" button that appears in the Value column
  5. Choose your test file

Allowed file types: .pdf, .txt, .md, .docx, .doc, .csv, .json, .html, .xml Max file size: 10 MB

Expected Response:

  • Status: 202 Accepted
  • Body:
{
    "message": "File upload accepted. Ingestion running in background.",
    "document": {
        "id": 1,
        "user": {
            "id": 1,
            "username": "testuser_01",
            "email": "testuser01@example.com"
        },
        "name": "test-document.pdf",
        "file_url": "http://localhost:8000/media/documents/test-document.pdf",
        "status": "PENDING",
        "entity_count": 0,
        "relationship_count": 0,
        "error_message": null,
        "processing_progress": 0,
        "processing_step": null,
        "created_at": "2026-07-09T10:00:00Z",
        "updated_at": "2026-07-09T10:00:00Z"
    }
}

What to Check:

  • Status is 202 Accepted (not 200 - this is async!)
  • document.status is "PENDING" initially
  • document.id is returned (save this for later requests)
  • No password or sensitive info leaked

Post-Response Script (Tests tab):

pm.test("Status code is 202", function () {
    pm.response.to.have.status(202);
});

pm.test("Upload accepted for background processing", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.message).to.include("accepted");
});

pm.test("Document ID is returned", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.document).to.have.property("id");
    // Save document ID for other requests
    pm.environment.set("uploaded_doc_id", jsonData.document.id);
});

pm.test("Document status is PENDING", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.document.status).to.eql("PENDING");
});

ENDPOINT 4: GET /api/documents/

Folder: 03 - Document Management

Setup:

Name: List Documents
Method: GET
URL: {{base_url}}/api/documents/

Headers:

Authorization: Bearer {{access_token}}

Body: None (GET request)

Expected Response:

  • Status: 200 OK
  • Body:
[
    {
        "id": 1,
        "user": {
            "id": 1,
            "username": "testuser_01",
            "email": "testuser01@example.com"
        },
        "name": "test-document.pdf",
        "file_url": "http://localhost:8000/media/documents/test-document.pdf",
        "status": "COMPLETED",
        "entity_count": 42,
        "relationship_count": 18,
        "error_message": null,
        "processing_progress": 100,
        "processing_step": "completed",
        "created_at": "2026-07-09T10:00:00Z",
        "updated_at": "2026-07-09T10:05:00Z"
    }
]

What to Check:

  • Response is an array (even if empty: [])
  • Each document has id, name, status, file_url
  • user object matches logged-in user
  • No other user's documents are visible (multi-tenancy)

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Response is an array", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.be.an("array");
});

pm.test("Documents belong to current user only", function () {
    var jsonData = pm.response.json();
    jsonData.forEach(function(doc) {
        pm.expect(doc.user.username).to.eql(pm.environment.get("test_username"));
    });
});

ENDPOINT 5: GET /api/documents/{id}/

Folder: 03 - Document Management

Setup:

Name: Get Document Detail
Method: GET
URL: {{base_url}}/api/documents/{{uploaded_doc_id}}/

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body: Single document object (same structure as list item)

What to Check:

  • Returned document id matches {{uploaded_doc_id}}
  • All fields are present and populated
  • file_url is a valid URL

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Returned document matches requested ID", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.id).to.eql(parseInt(pm.environment.get("uploaded_doc_id")));
});

pm.test("Document has all required fields", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.all.keys(
        "id", "user", "name", "file_url", "status",
        "entity_count", "relationship_count", "error_message",
        "processing_progress", "processing_step",
        "created_at", "updated_at"
    );
});

ENDPOINT 6: DELETE /api/documents/{id}/

Folder: 03 - Document Management

Setup:

Name: Delete Document
Method: DELETE
URL: {{base_url}}/api/documents/{{uploaded_doc_id}}/

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "message": "Document and all extracted nodes/vectors deleted successfully."
}

What to Check:

  • Confirmation message is returned
  • Document is actually removed (call List Documents to verify)

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Deletion confirmation message", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.message).to.include("deleted successfully");
});

ENDPOINT 7: POST /api/query/

Folder: 04 - Query Endpoints

Setup:

Name: Hybrid Query (Default)
Method: POST
URL: {{base_url}}/api/query/

Headers:

Authorization: Bearer {{access_token}}
Content-Type: application/json

Body (raw JSON):

{
    "query": "What are the main topics discussed in the document?",
    "mode": "hybrid"
}

mode can be: "hybrid" (default), "graph", or "vector"

Expected Response:

  • Status: 200 OK
  • Body:
{
    "answer": "The document discusses several main topics including...",
    "sources": [
        {
            "content": "excerpt from document...",
            "metadata": {"page": 1, "source": "test-document.pdf"}
        }
    ],
    "strategy": "HYBRID",
    "success": true,
    "response_time": 2.345
}

What to Check:

  • answer field contains a non-empty string
  • success is true
  • strategy matches the requested mode
  • sources array contains relevant excerpts

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Query returned a valid answer", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.success).to.be.true;
    pm.expect(jsonData.answer).to.be.a("string");
    pm.expect(jsonData.answer.length).to.be.greaterThan(0);
});

pm.test("Response includes sources", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.sources).to.be.an("array");
});

ENDPOINT 8: POST /api/query/graph-only/

Folder: 04 - Query Endpoints

Setup:

Name: Graph-Only Query
Method: POST
URL: {{base_url}}/api/query/graph-only/

Headers:

Authorization: Bearer {{access_token}}
Content-Type: application/json

Body (raw JSON):

{
    "query": "What entities are connected to the main concept?"
}

Expected Response:

  • Status: 200 OK
  • Body: Similar to hybrid query but strategy will be "GRAPH"

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Graph-only strategy is used", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.strategy).to.eql("GRAPH");
});

ENDPOINT 9: POST /api/query/vector-only/

Folder: 04 - Query Endpoints

Setup:

Name: Vector-Only Query
Method: POST
URL: {{base_url}}/api/query/vector-only/

Headers:

Authorization: Bearer {{access_token}}
Content-Type: application/json

Body (raw JSON):

{
    "query": "Find similar content about machine learning"
}

Expected Response:

  • Status: 200 OK
  • Body: Similar structure but strategy will be "VECTOR"

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Vector-only strategy is used", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.strategy).to.eql("VECTOR");
});

ENDPOINT 10: POST /api/query/compare/

Folder: 04 - Query Endpoints

Setup:

Name: Compare All Query Modes
Method: POST
URL: {{base_url}}/api/query/compare/

Headers:

Authorization: Bearer {{access_token}}
Content-Type: application/json

Body (raw JSON):

{
    "query": "Explain the relationships between key concepts"
}

Expected Response:

  • Status: 200 OK
  • Body:
{
    "query": "Explain the relationships between key concepts",
    "comparisons": {
        "graph": {
            "answer": "...",
            "sources": [],
            "strategy": "GRAPH",
            "response_time": 1.234,
            "success": true
        },
        "vector": {
            "answer": "...",
            "sources": [],
            "strategy": "VECTOR",
            "response_time": 0.987,
            "success": true
        },
        "hybrid": {
            "answer": "...",
            "sources": [],
            "strategy": "HYBRID",
            "response_time": 1.567,
            "success": true
        }
    },
    "success": true
}

What to Check:

  • All 3 modes (graph, vector, hybrid) are present in comparisons
  • Each has answer, sources, strategy, response_time, success
  • success is true for the overall response

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("All three modes are compared", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.comparisons).to.have.all.keys("graph", "vector", "hybrid");
});

pm.test("Each mode has required fields", function () {
    var jsonData = pm.response.json();
    ["graph", "vector", "hybrid"].forEach(function(mode) {
        pm.expect(jsonData.comparisons[mode]).to.have.all.keys(
            "answer", "sources", "strategy", "response_time", "success"
        );
    });
});

ENDPOINT 11: GET /api/graph/

Folder: 05 - Graph Endpoints

Setup:

Name: Get Full Graph Data
Method: GET
URL: {{base_url}}/api/graph/

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "nodes": [
        {
            "id": "entity_1",
            "label": "Machine Learning",
            "type": "CONCEPT",
            "properties": {}
        }
    ],
    "edges": [
        {
            "source": "entity_1",
            "target": "entity_2",
            "relationship": "RELATED_TO",
            "properties": {}
        }
    ]
}

What to Check:

  • nodes is an array
  • edges is an array
  • Each node has id, label, type
  • Each edge has source, target, relationship

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Response contains nodes and edges", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("nodes");
    pm.expect(jsonData).to.have.property("edges");
    pm.expect(jsonData.nodes).to.be.an("array");
    pm.expect(jsonData.edges).to.be.an("array");
});

ENDPOINT 12: GET /api/graph/entity/{name}/

Folder: 05 - Graph Endpoints

Setup:

Name: Get Entity Details
Method: GET
URL: {{base_url}}/api/graph/entity/Machine Learning/

Replace Machine Learning with an actual entity name from your graph.

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "name": "Machine Learning",
    "type": "CONCEPT",
    "properties": {},
    "relationships": [
        {
            "target": "Deep Learning",
            "type": "SUBSET_OF"
        }
    ],
    "subgraph": {
        "nodes": [],
        "edges": []
    }
}

What to Check:

  • Entity name matches the requested name
  • relationships array shows connections
  • subgraph contains the local neighborhood

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Entity name matches request", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.name).to.eql("Machine Learning");
});

ENDPOINT 13: GET /api/graph/path/

Folder: 05 - Graph Endpoints

Setup:

Name: Find Path Between Entities
Method: GET
URL: {{base_url}}/api/graph/path/?entity_a=Entity1&entity_b=Entity2

Replace Entity1 and Entity2 with actual entity names.

Headers:

Authorization: Bearer {{access_token}}

Body: None (query parameters in URL)

Query Parameters (can also set in "Params" tab):

Key Value
entity_a Entity1
entity_b Entity2

Expected Response:

  • Status: 200 OK
  • Body:
{
    "entity_a": "Entity1",
    "entity_b": "Entity2",
    "path": ["Entity1", "Intermediate", "Entity2"],
    "explanation": "Entity1 is connected to Entity2 through...",
    "path_length": 2,
    "success": true
}

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Path response has required fields", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("path");
    pm.expect(jsonData.path).to.be.an("array");
    pm.expect(jsonData.path.length).to.be.greaterThan(0);
});

ENDPOINT 14: POST /api/graph/cypher/

Folder: 05 - Graph Endpoints

Setup:

Name: Execute Cypher Query
Method: POST
URL: {{base_url}}/api/graph/cypher/

Headers:

Authorization: Bearer {{access_token}}
Content-Type: application/json

Body (raw JSON):

{
    "query": "MATCH (n) RETURN labels(n) AS label, count(n) AS count ORDER BY count DESC LIMIT 10"
}

This translates natural language or raw Cypher and executes it.

Expected Response:

  • Status: 200 OK
  • Body:
{
    "cypher": "MATCH (n) RETURN labels(n) AS label, count(n) AS count ORDER BY count DESC LIMIT 10",
    "results": [
        {"label": ["CONCEPT"], "count": 42},
        {"label": ["PERSON"], "count": 15}
    ],
    "success": true
}

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Cypher query executed successfully", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.success).to.be.true;
    pm.expect(jsonData.results).to.be.an("array");
});

ENDPOINT 15: GET /api/graph/stats/

Folder: 05 - Graph Endpoints

Setup:

Name: Get Graph Statistics
Method: GET
URL: {{base_url}}/api/graph/stats/

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "total_nodes": 150,
    "total_edges": 320,
    "node_types": {
        "CONCEPT": 42,
        "PERSON": 15,
        "ORGANIZATION": 8,
        "DOCUMENT": 5
    },
    "edge_types": {
        "RELATED_TO": 120,
        "AUTHORED_BY": 30,
        "PART_OF": 45
    }
}

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Stats contain node and edge counts", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("total_nodes");
    pm.expect(jsonData).to.have.property("total_edges");
    pm.expect(jsonData.total_nodes).to.be.a("number");
    pm.expect(jsonData.total_edges).to.be.a("number");
});

ENDPOINT 16: GET /api/graph/communities/

Folder: 05 - Graph Endpoints

Setup:

Name: List Communities
Method: GET
URL: {{base_url}}/api/graph/communities/

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "communities": [
        {
            "id": 1,
            "label": "Community 1",
            "summary": "This community covers...",
            "member_count": 12,
            "members": ["Entity1", "Entity2", "Entity3"]
        }
    ],
    "count": 1
}

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Communities response structure", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("communities");
    pm.expect(jsonData).to.have.property("count");
    pm.expect(jsonData.communities).to.be.an("array");
});

ENDPOINT 17: GET /api/graph/communities/{id}/

Folder: 05 - Graph Endpoints

Setup:

Name: Get Community Detail
Method: GET
URL: {{base_url}}/api/graph/communities/1/

Replace 1 with an actual community ID from the list endpoint.

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "id": 1,
    "label": "Community 1",
    "summary": "This community covers...",
    "member_count": 12,
    "members": ["Entity1", "Entity2"],
    "member_details": [
        {
            "name": "Entity1",
            "type": "CONCEPT",
            "properties": {}
        }
    ]
}

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Community ID matches request", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.id).to.eql(1);
});

ENDPOINT 18: POST /api/graph/search/

Folder: 05 - Graph Endpoints

Setup:

Name: Search Entities
Method: POST
URL: {{base_url}}/api/graph/search/

Headers:

Authorization: Bearer {{access_token}}
Content-Type: application/json

Body (raw JSON):

{
    "query": "machine learning"
}

Expected Response:

  • Status: 200 OK
  • Body:
{
    "query": "machine learning",
    "results": [
        {
            "name": "Machine Learning",
            "type": "CONCEPT",
            "properties": {"description": "..."}
        },
        {
            "name": "Deep Learning",
            "type": "CONCEPT",
            "properties": {"description": "..."}
        }
    ],
    "count": 2
}

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Search returns results array", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("results");
    pm.expect(jsonData.results).to.be.an("array");
    pm.expect(jsonData).to.have.property("count");
});

ENDPOINT 19: GET /api/evaluation/

Folder: 06 - Evaluation

Setup:

Name: Run Evaluation
Method: GET
URL: {{base_url}}/api/evaluation/

Headers:

Authorization: Bearer {{access_token}}

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "evaluations": [
        {
            "question": "What is X?",
            "expected_answer": "X is...",
            "results": {
                "graph": {
                    "answer": "...",
                    "response_time": 1.234,
                    "success": true
                },
                "vector": {
                    "answer": "...",
                    "response_time": 0.987,
                    "success": true
                },
                "hybrid": {
                    "answer": "...",
                    "response_time": 1.567,
                    "success": true
                }
            }
        }
    ],
    "summary": {
        "total_pairs": 1,
        "avg_response_times": {
            "graph": 1.234,
            "vector": 0.987,
            "hybrid": 1.567
        }
    }
}

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Evaluation has correct structure", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("evaluations");
    pm.expect(jsonData).to.have.property("summary");
    pm.expect(jsonData.evaluations).to.be.an("array");
});

ENDPOINT 20: GET /api/health/

Folder: 01 - Health Check

Setup:

Name: Health Check
Method: GET
URL: {{base_url}}/api/health/

Headers: None needed (no auth required)

Body: None

Expected Response:

  • Status: 200 OK
  • Body:
{
    "status": "healthy",
    "services": {
        "django": "healthy",
        "neo4j": "healthy",
        "timestamp": 1688888000.0
    }
}

What to Check:

  • status is "healthy" or "degraded"
  • services.django is "healthy"
  • services.neo4j is "healthy" (if Neo4j is running)

Post-Response Script (Tests tab):

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

pm.test("Django service is healthy", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.services.django).to.eql("healthy");
});

pm.test("Health status is valid", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.status).to.be.oneOf(["healthy", "degraded"]);
});

5. Chaining Requests with JWT Tokens

How Token Chaining Works

The key mechanism is the Post-Response Script in the Login request that saves tokens to environment variables:

Login Request
    ↓ (Post-Response Script saves tokens)
    pm.environment.set("access_token", jsonData.access);
    pm.environment.set("refresh_token", jsonData.refresh);
    ↓
All other requests use: Authorization: Bearer {{access_token}}

Complete Chaining Flow

Step 1: Health Check (no auth needed)
    ↓
Step 2: Register User (no auth needed)
    ↓
Step 3: Login -> saves access_token & refresh_token to environment
    ↓
Step 4-19: All other requests use {{access_token}} in Authorization header
    ↓
Step 20 (optional): Refresh Token -> saves new access_token

Refreshing an Expired Token

When your access token expires (after 30 minutes), create a new request:

Setup:

Name: Refresh Token
Method: POST
URL: {{base_url}}/api/auth/token/refresh/

Headers:

Content-Type: application/json

Body (raw JSON):

{
    "refresh": "{{refresh_token}}"
}

Post-Response Script (Tests tab):

pm.test("Token refreshed successfully", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("access");
    pm.environment.set("access_token", jsonData.access);
    // If refresh token is also rotated
    if (jsonData.refresh) {
        pm.environment.set("refresh_token", jsonData.refresh);
    }
});

6. Writing Post-Response Tests

Test Syntax Reference

// Status code tests
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

// Response time tests
pm.test("Response time is under 2 seconds", function () {
    pm.expect(pm.response.responseTime).to.be.below(2000);
});

// Header tests
pm.test("Content-Type is JSON", function () {
    pm.response.to.have.header("Content-Type", "application/json");
});

// Body field existence
pm.test("Response has 'answer' field", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property("answer");
});

// Body field value
pm.test("Answer is not empty", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.answer.length).to.be.greaterThan(0);
});

// Array tests
pm.test("Results is an array with items", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.results).to.be.an("array");
    pm.expect(jsonData.results.length).to.be.greaterThan(0);
});

// Type checks
pm.test("ID is a number", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.id).to.be.a("number");
});

// Nested object checks
pm.test("User object has username", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.user.username).to.be.a("string");
});

// Conditional tests
pm.test("If success is true, answer exists", function () {
    var jsonData = pm.response.json();
    if (jsonData.success) {
        pm.expect(jsonData.answer).to.exist;
    }
});

// Environment variable assertions
pm.test("Token was saved", function () {
    pm.expect(pm.environment.get("access_token")).to.exist;
    pm.expect(pm.environment.get("access_token").length).to.be.greaterThan(10);
});

// Chai assertions (more expressive)
pm.test("Response matches expected schema", function () {
    var schema = {
        type: "object",
        required: ["answer", "sources", "success"],
        properties: {
            answer: { type: "string" },
            sources: { type: "array" },
            success: { type: "boolean" }
        }
    };
    var jsonData = pm.response.json();
    pm.expect(tv4.validate(jsonData, schema)).to.be.true;
});

Setting Variables in Tests

// Set environment variable
pm.environment.set("variable_name", value);

// Set collection variable (shared across environments)
pm.collectionVariables.set("variable_name", value);

// Set global variable
pm.globals.set("variable_name", value);

// Get a variable
var val = pm.environment.get("variable_name");

// Clear a variable
pm.environment.unset("variable_name");

7. Common Errors & Troubleshooting

Error 1: 401 Unauthorized

Cause: Missing or invalid JWT token
Fix:
  1. Ensure you ran the Login request first
  2. Check that access_token environment variable is set
  3. Verify the Authorization header format: "Bearer {{access_token}}"
  4. Token may have expired (30 min lifetime) - run Refresh Token request

Error 2: 400 Bad Request - "Passwords do not match"

Cause: password and confirm_password fields differ
Fix:
  1. Ensure both fields have identical values
  2. Check for trailing spaces in either field
  3. Password must contain: uppercase, lowercase, number, special char (@$!%*?&)

Error 3: 400 Bad Request - "Disposable or temporary email accounts are not permitted"

Cause: Email domain is in the blocklist
Fix: Use a legitimate email domain like:
  - @gmail.com
  - @outlook.com
  - @company.com
  Do NOT use: mailinator.com, yopmail.com, tempmail.com, etc.

Error 4: 400 Bad Request - "No file was uploaded"

Cause: Missing file in multipart form-data
Fix:
  1. Body type must be "form-data" (not raw JSON)
  2. Key must be exactly "file"
  3. Change key type from "Text" to "File" (hover over key field)
  4. Select a valid file using the file picker

Error 5: 413 Request Entity Too Large

Cause: File exceeds 10 MB limit
Fix: Compress or split your file to under 10 MB

Error 6: 400 Bad Request - "File type '.exe' is not allowed"

Cause: Uploaded file extension not in allowed list
Allowed: .pdf, .txt, .md, .docx, .doc, .csv, .json, .html, .xml
Fix: Convert file to an allowed format

Error 7: 404 Not Found - "Entity 'X' not found"

Cause: Requested entity name doesn't exist in the graph
Fix:
  1. First call GET /api/graph/ to see available entities
  2. Or call POST /api/graph/search/ to find entity names
  3. Entity names are case-sensitive

Error 8: 429 Too Many Requests

Cause: Rate limit exceeded (100 requests/minute for authenticated users)
Fix:
  1. Wait 60 seconds before retrying
  2. Reduce request frequency
  3. In Postman Runner, add a delay between iterations

Error 9: 500 Internal Server Error

Cause: Server-side error (Neo4j down, LLM API key missing, etc.)
Fix:
  1. Check Django server logs
  2. Verify Neo4j is running: GET /api/health/
  3. Check LLM API key is configured in .env
  4. Ensure ChromaDB is initialized

Error 10: Connection Refused

Cause: Django server not running
Fix:
  cd backend
  python manage.py runserver
  # Should see: Starting development server at http://localhost:8000/

8. Using Postman Runner for Batch Testing

Step 8.1: Set Up a Test Suite Order

Create a file called test-order.json or manually arrange requests in this order:

Test Execution Order:
1. GET  /api/health/                    (verify server is up)
2. POST /api/auth/register/             (create test user)
3. POST /api/auth/login/                (get JWT tokens)
4. POST /api/documents/upload/          (upload test document)
5. GET  /api/documents/                 (list documents)
6. GET  /api/documents/{id}/            (get document detail)
7. POST /api/query/                     (hybrid query)
8. POST /api/query/graph-only/          (graph query)
9. POST /api/query/vector-only/         (vector query)
10. POST /api/query/compare/            (compare all modes)
11. GET  /api/graph/                    (full graph)
12. GET  /api/graph/entity/{name}/      (entity detail)
13. GET  /api/graph/path/               (find path)
14. POST /api/graph/cypher/             (execute cypher)
15. GET  /api/graph/stats/              (graph stats)
16. GET  /api/graph/communities/        (list communities)
17. GET  /api/graph/communities/{id}/   (community detail)
18. POST /api/graph/search/             (search entities)
19. GET  /api/evaluation/               (run evaluation)
20. DELETE /api/documents/{id}/         (cleanup - delete document)

Step 8.2: Open Postman Runner

1. Click "Runner" button in the top toolbar (or press Ctrl+Shift+R)
2. A new tab opens with the Collection Runner interface

Step 8.3: Configure the Runner

1. Select Collection: "GraphRAG Knowledge Graph AI API"
2. Select Environment: "GraphRAG Local Dev"
3.Iterations: Set number of times to run (e.g., 5 for stress testing)
4. Delay: Set delay between requests in ms (e.g., 500ms)
5. Data File: (Optional) Upload a CSV/JSON with test data
6. Save responses: Toggle ON to save all responses
7. Keep variable values: Select "Save to Environment"

Step 8.4: Data-Driven Testing with CSV

Create a file called test-queries.csv:

query,mode,expected_strategy
What is machine learning?,hybrid,HYBRID
Explain neural networks,graph,GRAPH
Find similar research papers,vector,VECTOR
Compare all approaches for NLP,hybrid,HYBRID

In Runner:

1. Click "Select File" next to "Data"
2. Upload test-queries.csv
3. In your requests, use: {{query}}, {{mode}}, {{expected_strategy}}
4. Postman will iterate through each row

Step 8.5: Run and Analyze Results

1. Click "Run GraphRAG Knowledge Graph AI API"
2. Watch the execution in real-time
3. After completion, you'll see:
   - Total tests passed/failed
   - Response times for each request
   - Assertion results
   - Any errors encountered

Step 8.6: Export Results

1. After run completes, click "Export Results"
2. Save as JSON for further analysis
3. Or use "Download as JSON" for CI/CD integration

Step 8.7: Newman (Command Line Runner)

For CI/CD pipelines, use Newman:

# Install Newman
npm install -g newman

# Export collection from Postman
# (In Postman: Collection -> ... -> Export -> Collection v2.1)

# Run collection
newman run "GraphRAG_Knowledge_Graph_AI_API.postman_collection.json" \
  --environment "GraphRAG_Local_Dev.postman_environment.json" \
  --iteration-count 5 \
  --delay-request 500 \
  --reporters cli,html

# Run with data file
newman run "GraphRAG_Knowledge_Graph_AI_API.postman_collection.json" \
  --environment "GraphRAG_Local_Dev.postman_environment.json" \
  --iteration-data "test-queries.csv" \
  --reporters cli,html

Quick Reference Card

Authentication Headers

All authenticated requests need:
Authorization: Bearer {{access_token}}

Content-Type: application/json       (for JSON bodies)
Content-Type: multipart/form-data    (for file uploads - set in body tab, not header)

Request Body Formats

JSON Body:
{
    "key": "value"
}

Form-Data Body (for file uploads):
Key: file | Type: File | Value: [select file]

Query Parameters:
?entity_a=X&entity_b=Y

Expected Status Codes

Code Meaning When
200 OK Successful GET/DELETE
201 Created Successful POST (register)
202 Accepted Upload accepted (async processing)
400 Bad Request Invalid input / validation error
401 Unauthorized Missing/invalid/expired JWT token
404 Not Found Entity/document doesn't exist
413 Request Entity Too Large File exceeds 10MB limit
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Server-side failure

Password Requirements

Must contain:
  - At least 1 uppercase letter (A-Z)
  - At least 1 lowercase letter (a-z)
  - At least 1 number (0-9)
  - At least 1 special character (@$!%*?&)
  - Minimum 8 characters
  - Must match confirm_password field

Guide Version: 1.0 API Base URL: http://localhost:8000 Total Endpoints Documented: 20 Authentication: JWT (SimpleJWT - 30min access, 7-day refresh)