Request Body là phần dữ liệu chính được gửi trong HTTP request, chứa payload (tải trọng) mà client muốn gửi đến server.
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 58
Authorization: Bearer token123
{
"name": "Nguyễn Văn A",
"email": "vana@example.com"
}
Phân tích:
HTTP Method | Có Body? | Mục đích |
---|---|---|
POST | ✓ Yes | Tạo mới resource |
PUT | ✓ Yes | Cập nhật toàn bộ resource |
PATCH | ✓ Yes | Cập nhật một phần resource |
DELETE | Optional | Xóa resource (có thể có metadata) |
GET | ✗ No | Lấy dữ liệu (dùng query params) |
HEAD | ✗ No | Lấy headers only |
OPTIONS | ✗ No | Kiểm tra methods được hỗ trợ |
Mặc dù HTTP spec không cấm GET có body, nhưng:
import { test, expect } from '@playwright/test';
test('POST với JSON body', async ({ request }) => {
const response = await request.post('https://api.example.com/users', {
data: {
name: 'Nguyễn Văn A',
email: 'vana@example.com',
age: 25,
city: 'Hà Nội'
}
});
expect(response.status()).toBe(201);
const user = await response.json();
expect(user).toHaveProperty('id');
expect(user.name).toBe('Nguyễn Văn A');
});
test('PUT để cập nhật user', async ({ request }) => {
const response = await request.put('https://api.example.com/users/123', {
data: {
name: 'Nguyễn Văn A - Updated',
email: 'vana_new@example.com',
age: 26,
city: 'TP.HCM'
}
});
expect(response.status()).toBe(200);
});
test('PATCH để cập nhật một phần', async ({ request }) => {
const response = await request.patch('https://api.example.com/users/123', {
data: {
city: 'Đà Nẵng' // Chỉ update city
}
});
expect(response.status()).toBe(200);
});
Đặc điểm:
POST /api/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123"
}
POST /api/orders
Content-Type: application/json
{
"customer": {
"name": "Nguyễn Văn A",
"email": "vana@example.com",
"phone": "0123456789"
},
"items": [
{
"product_id": 101,
"quantity": 2,
"price": 500000
},
{
"product_id": 202,
"quantity": 1,
"price": 1000000
}
],
"shipping_address": {
"street": "123 Main St",
"city": "Hà Nội",
"district": "Ba Đình",
"country": "Vietnam"
},
"payment_method": "credit_card",
"notes": "Giao hàng giờ hành chính"
}
import { test, expect } from '@playwright/test';
test('POST JSON - Simple', async ({ request }) => {
const response = await request.post('https://api.example.com/login', {
data: {
email: 'user@example.com',
password: 'password123'
}
// Playwright tự động set Content-Type: application/json
});
expect(response.status()).toBe(200);
const result = await response.json();
expect(result).toHaveProperty('token');
});
test('POST JSON - Nested Objects', async ({ request }) => {
const response = await request.post('https://api.example.com/orders', {
data: {
customer: {
name: 'Nguyễn Văn A',
email: 'vana@example.com',
phone: '0123456789'
},
items: [
{ product_id: 101, quantity: 2, price: 500000 },
{ product_id: 202, quantity: 1, price: 1000000 }
],
shipping_address: {
street: '123 Main St',
city: 'Hà Nội',
country: 'Vietnam'
},
payment_method: 'credit_card'
}
});
expect(response.status()).toBe(201);
const order = await response.json();
expect(order).toHaveProperty('id');
expect(order).toHaveProperty('total');
expect(order.items).toHaveLength(2);
});
<?php
// Đọc JSON từ request body
$json = file_get_contents('php://input');
$data = json_decode($json, true);
// Validate
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON']);
exit;
}
// Access data
$email = $data['email'] ?? null;
$password = $data['password'] ?? null;
if (!$email || !$password) {
http_response_code(400);
echo json_encode(['error' => 'Email and password required']);
exit;
}
// Process login...
$result = ['token' => 'abc123xyz', 'user_id' => 1];
// Send JSON response
header('Content-Type: application/json');
echo json_encode($result);
?>
{
"string": "Hello World",
"number": 123,
"float": 12.34,
"boolean": true,
"null_value": null,
"array": [1, 2, 3, "four"],
"object": {
"nested": "value"
}
}
Content-Type: application/json
Đặc điểm:
POST /api/login
Content-Type: application/x-www-form-urlencoded
email=user%40example.com&password=password123&remember=true
Giống query string nhưng ở trong body!
import { test, expect } from '@playwright/test';
test('POST form-urlencoded', async ({ request }) => {
const response = await request.post('https://api.example.com/login', {
form: { // ← Dùng 'form' thay vì 'data'
email: 'user@example.com',
password: 'password123',
remember: 'true'
}
});
expect(response.status()).toBe(200);
});
// Hoặc set header manually
test('POST form-urlencoded (manual)', async ({ request }) => {
const params = new URLSearchParams({
email: 'user@example.com',
password: 'password123',
remember: 'true'
});
const response = await request.post('https://api.example.com/login', {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString()
});
expect(response.status()).toBe(200);
});
<?php
// PHP tự động parse form-urlencoded vào $_POST
$email = $_POST['email'] ?? null;
$password = $_POST['password'] ?? null;
$remember = $_POST['remember'] ?? false;
// Hoặc đọc raw
$body = file_get_contents('php://input');
parse_str($body, $data);
$email = $data['email'] ?? null;
$password = $data['password'] ?? null;
?>
Character | Encoded |
---|---|
Space | + or %20 |
@ | %40 |
& | %26 |
= | %3D |
% | %25 |
Đặc điểm:
POST /api/upload
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="title"
My Document
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="description"
Document description here
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="document.pdf"
Content-Type: application/pdf
[binary PDF data here]
------WebKitFormBoundary7MA4YWxkTrZu0gW--
import { test, expect } from '@playwright/test';
import fs from 'fs';
import path from 'path';
test('Upload file with form data', async ({ request }) => {
// Đọc file
const filePath = path.join(__dirname, 'files', 'document.pdf');
const fileBuffer = fs.readFileSync(filePath);
const response = await request.post('https://api.example.com/upload', {
multipart: {
title: 'My Document',
description: 'Important document',
category: 'reports',
file: {
name: 'document.pdf',
mimeType: 'application/pdf',
buffer: fileBuffer
}
}
});
expect(response.status()).toBe(201);
const result = await response.json();
expect(result).toHaveProperty('file_id');
expect(result).toHaveProperty('url');
});
test('Upload multiple files', async ({ request }) => {
const file1 = fs.readFileSync('image1.jpg');
const file2 = fs.readFileSync('image2.jpg');
const response = await request.post('https://api.example.com/upload-multiple', {
multipart: {
title: 'Photo Album',
files: [
{
name: 'image1.jpg',
mimeType: 'image/jpeg',
buffer: file1
},
{
name: 'image2.jpg',
mimeType: 'image/jpeg',
buffer: file2
}
]
}
});
expect(response.status()).toBe(201);
});
test('Upload image from URL', async ({ request }) => {
// Fetch image từ URL
const imageResponse = await request.get('https://via.placeholder.com/150');
const imageBuffer = await imageResponse.body();
const uploadResponse = await request.post('https://api.example.com/upload', {
multipart: {
name: 'avatar',
file: {
name: 'avatar.png',
mimeType: 'image/png',
buffer: imageBuffer
}
}
});
expect(uploadResponse.status()).toBe(201);
});
<?php
// Check if file uploaded
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
echo json_encode(['error' => 'No file uploaded']);
exit;
}
$file = $_FILES['file'];
$title = $_POST['title'] ?? 'Untitled';
$description = $_POST['description'] ?? '';
// Validate file
$allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
if (!in_array($file['type'], $allowedTypes)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid file type']);
exit;
}
// Validate size (max 5MB)
$maxSize = 5 * 1024 * 1024;
if ($file['size'] > $maxSize) {
http_response_code(400);
echo json_encode(['error' => 'File too large']);
exit;
}
// Generate unique filename
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$newFilename = uniqid() . '.' . $ext;
$uploadPath = '/var/www/uploads/' . $newFilename;
// Move uploaded file
if (!move_uploaded_file($file['tmp_name'], $uploadPath)) {
http_response_code(500);
echo json_encode(['error' => 'Failed to save file']);
exit;
}
// Save to database
$fileId = saveToDatabase($title, $description, $newFilename, $file['size']);
// Return response
header('Content-Type: application/json');
echo json_encode([
'success' => true,
'file_id' => $fileId,
'url' => 'https://cdn.example.com/uploads/' . $newFilename,
'size' => $file['size']
]);
?>
Đặc điểm:
POST /api/users
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>Nguyễn Văn A</name>
<email>vana@example.com</email>
<age>25</age>
<address>
<city>Hà Nội</city>
<country>Vietnam</country>
</address>
</user>
import { test, expect } from '@playwright/test';
test('POST XML data', async ({ request }) => {
const xmlData = `<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>Nguyễn Văn A</name>
<email>vana@example.com</email>
<age>25</age>
</user>`;
const response = await request.post('https://api.example.com/users', {
headers: {
'Content-Type': 'application/xml'
},
data: xmlData
});
expect(response.status()).toBe(201);
// Parse XML response
const responseText = await response.text();
expect(responseText).toContain('<user>');
});
<?php
$xml = file_get_contents('php://input');
// Parse XML
$doc = simplexml_load_string($xml);
if ($doc === false) {
http_response_code(400);
echo '<error>Invalid XML</error>';
exit;
}
// Access data
$name = (string)$doc->name;
$email = (string)$doc->email;
$age = (int)$doc->age;
// Process...
// Send XML response
header('Content-Type: application/xml');
echo '<?xml version="1.0"?>';
echo '<response>';
echo ' <success>true</success>';
echo ' <user_id>123</user_id>';
echo '</response>';
?>
Feature | XML | JSON |
---|---|---|
Kích thước | Lớn hơn | Nhỏ hơn |
Dễ đọc | Trung bình | Dễ |
Parse speed | Chậm hơn | Nhanh hơn |
Schema validation | XSD | JSON Schema |
Namespaces | Yes | No |
Comments | Yes | No |
Use case | Legacy, SOAP, Enterprise | REST APIs, Web, Mobile |
POST /api/notes
Content-Type: text/plain
This is a simple text note.
Multiple lines supported.
No special formatting.
POST /api/upload
Content-Type: application/octet-stream
[raw binary data]
import { test, expect } from '@playwright/test';
// Text/plain
test('POST plain text', async ({ request }) => {
const response = await request.post('https://api.example.com/notes', {
headers: {
'Content-Type': 'text/plain'
},
data: 'This is my note content.\nMultiple lines supported.'
});
expect(response.status()).toBe(201);
});
// Binary data
test('POST binary data', async ({ request }) => {
const buffer = Buffer.from([0x89, 0x50, 0x4E, 0x47]); // PNG header
const response = await request.post('https://api.example.com/upload', {
headers: {
'Content-Type': 'application/octet-stream'
},
data: buffer
});
expect(response.status()).toBe(201);
});
Tạo mới một resource. Body chứa toàn bộ dữ liệu của resource mới.
POST /api/posts
Content-Type: application/json
{
"title": "New Blog Post",
"content": "This is the content of my new blog post...",
"status": "draft",
"tags": ["api", "testing", "playwright"],
"publish_date": "2025-02-01"
}
// Response: 201 Created
{
"id": 456,
"title": "New Blog Post",
"status": "draft",
"created_at": "2025-01-15T10:00:00Z",
"author_id": 123
}
Cập nhật toàn bộ resource. Body phải chứa TẤT CẢ fields.
PUT /api/posts/456
Content-Type: application/json
{
"title": "Updated Blog Post",
"content": "Updated content here...",
"status": "published",
"tags": ["api", "testing"],
"publish_date": "2025-01-20"
}
// ⚠️ Nếu thiếu field nào đó, field đó có thể bị xóa/reset!
Cập nhật một phần resource. Body chỉ chứa fields cần update.
PATCH /api/posts/456
Content-Type: application/json
{
"status": "published",
"publish_date": "2025-01-20"
}
// Chỉ update status và publish_date
// Các fields khác giữ nguyên
Xóa resource. Body là optional, có thể chứa metadata hoặc lý do xóa.
DELETE /api/posts/456
Content-Type: application/json
{
"reason": "spam",
"notify_author": false,
"cascade": true
}
// Hoặc không có body
DELETE /api/posts/456
import { test, expect } from '@playwright/test';
test('Full CRUD workflow', async ({ request }) => {
// CREATE - POST
const createResponse = await request.post('https://api.example.com/posts', {
data: {
title: 'Test Post',
content: 'Content here',
status: 'draft'
}
});
expect(createResponse.status()).toBe(201);
const post = await createResponse.json();
const postId = post.id;
// READ - GET (không có body)
const getResponse = await request.get(`https://api.example.com/posts/${postId}`);
expect(getResponse.status()).toBe(200);
// UPDATE FULL - PUT
const putResponse = await request.put(`https://api.example.com/posts/${postId}`, {
data: {
title: 'Updated Post',
content: 'Updated content',
status: 'published'
}
});
expect(putResponse.status()).toBe(200);
// UPDATE PARTIAL - PATCH
const patchResponse = await request.patch(`https://api.example.com/posts/${postId}`, {
data: {
status: 'archived'
}
});
expect(patchResponse.status()).toBe(200);
// DELETE
const deleteResponse = await request.delete(`https://api.example.com/posts/${postId}`, {
data: {
reason: 'test cleanup'
}
});
expect(deleteResponse.status()).toBe(204);
});
{
"username": "john_doe",
"password": "securePass123",
"email": "john@example.com"
}
{
"user": {
"name": "Nguyễn Văn A",
"email": "vana@example.com",
"profile": {
"bio": "Software developer",
"website": "https://example.com",
"social": {
"twitter": "@johndoe",
"github": "johndoe"
}
}
},
"preferences": {
"theme": "dark",
"language": "vi",
"notifications": {
"email": true,
"push": false
}
}
}
{
"name": "Shopping Cart",
"items": [
{
"product_id": 101,
"name": "Laptop",
"quantity": 1,
"price": 15000000
},
{
"product_id": 202,
"name": "Mouse",
"quantity": 2,
"price": 200000
}
],
"tags": ["electronics", "computers", "accessories"],
"total": 15400000
}
{
"operations": [
{
"action": "create",
"type": "user",
"data": {
"name": "User 1",
"email": "user1@example.com"
}
},
{
"action": "update",
"type": "user",
"id": 123,
"data": {
"status": "active"
}
},
{
"action": "delete",
"type": "user",
"id": 456
}
]
}
test('Bulk operations', async ({ request }) => {
const response = await request.post('https://api.example.com/bulk', {
data: {
operations: [
{
action: 'create',
type: 'post',
data: { title: 'Post 1', content: 'Content 1' }
},
{
action: 'create',
type: 'post',
data: { title: 'Post 2', content: 'Content 2' }
},
{
action: 'update',
type: 'post',
id: 100,
data: { status: 'published' }
}
]
}
});
expect(response.status()).toBe(200);
const result = await response.json();
expect(result.success).toBe(true);
expect(result.results).toHaveLength(3);
});
Feature | Request Body | Query Params | Path Params | Headers |
---|---|---|---|---|
Data size | Large (MB+) | Small (~2KB) | Very small | Small |
Visibility | Hidden | Visible in URL | Visible in URL | Hidden |
Use case | Create/Update data | Filter/Search | Resource ID | Metadata |
HTTP Methods | POST/PUT/PATCH | GET (mainly) | All methods | All methods |
Cacheable | No | Yes | Yes | Depends |
Bookmarkable | No | Yes | Yes | No |
Security | Better | Logged/visible | Logged/visible | Better |
Complex data | ✓ Yes | ✗ Limited | ✗ No | ✗ No |
Nested objects | ✓ Yes | Limited | ✗ No | ✗ No |
Arrays | ✓ Yes | Limited | ✗ No | ✗ No |
File upload | ✓ Yes | ✗ No | ✗ No | ✗ No |
// ✓ Path param: Resource ID
GET /api/users/123
// ✓ Query params: Simple filter
GET /api/products?category=laptop&sort=price
// ✓ Headers: Auth & metadata
GET /api/users/me
Authorization: Bearer token123
Accept: application/json
// ✓ Request Body: Create with complex data
POST /api/orders
{
"customer": {...},
"items": [...],
"shipping_address": {...}
}
// ✓ Request Body: Complex search
POST /api/products/search
{
"filters": [
{"field": "category", "op": "in", "value": ["laptop", "phone"]},
{"field": "price", "op": "between", "value": [1000000, 5000000]}
],
"sort": [{"field": "price", "order": "asc"}],
"page": 1,
"limit": 20
}
Required vs Optional Fields:
// Document clearly
{
"name": "John Doe", // required
"email": "john@example.com", // required
"phone": "0123456789", // optional
"address": { // optional
"city": "Hanoi",
"country": "Vietnam"
}
}
Data Type Validation:
// ✓ Validate types
{
"age": 25, // number, not "25" string
"active": true, // boolean, not "true" string
"email": "valid@email.com", // valid email format
"phone": "0123456789", // valid phone format
"date": "2025-01-15" // valid ISO date
}
Length & Range Limits:
// Set reasonable limits
{
"name": "...", // max 100 characters
"email": "...", // max 255 characters
"bio": "...", // max 1000 characters
"age": 25, // range: 0-150
"quantity": 5, // range: 1-999
"tags": ["..."] // max 10 items
}
// PHP validation example
$data = json_decode(file_get_contents('php://input'), true);
// Validate required fields
$errors = [];
if (empty($data['email'])) {
$errors['email'] = 'Email is required';
}
// Validate format
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Invalid email format';
}
// Validate length
if (strlen($data['name']) > 100) {
$errors['name'] = 'Name too long (max 100 chars)';
}
// Sanitize HTML
$bio = htmlspecialchars($data['bio'], ENT_QUOTES, 'UTF-8');
if (!empty($errors)) {
http_response_code(400);
echo json_encode(['errors' => $errors]);
exit;
}
// ✗ Nguy hiểm!
$name = $data['name'];
$sql = "SELECT * FROM users WHERE name = '$name'";
// Attacker gửi: ' OR '1'='1
// ✓ An toàn: Parameterized query
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
$stmt->execute([$data['name']]);
// ✗ Nguy hiểm!
$comment = $data['comment'];
echo "<div>$comment</div>";
// Attacker gửi: <script>alert('xss')</script>
// ✓ An toàn: Escape HTML
$comment = htmlspecialchars($data['comment'], ENT_QUOTES, 'UTF-8');
echo "<div>$comment</div>";
// Validate file type
$allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
if (!in_array($_FILES['file']['type'], $allowedTypes)) {
die('Invalid file type');
}
// Validate file size (max 5MB)
$maxSize = 5 * 1024 * 1024;
if ($_FILES['file']['size'] > $maxSize) {
die('File too large');
}
// Validate file extension
$allowedExts = ['jpg', 'jpeg', 'png', 'pdf'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExts)) {
die('Invalid file extension');
}
// Generate unique filename
$newName = uniqid() . '.' . $ext;
// Don't use original filename!
// Set request body size limit
// PHP: post_max_size, upload_max_filesize in php.ini
// Nginx: client_max_body_size
// Validate in code
$json = file_get_contents('php://input');
if (strlen($json) > 1024 * 1024) { // 1MB limit
http_response_code(413); // Payload Too Large
echo json_encode(['error' => 'Request too large']);
exit;
}
Clear validation errors:
// ✓ Good error response
{
"error": "Validation failed",
"errors": {
"email": "Invalid email format",
"age": "Age must be between 0 and 150",
"phone": "Phone number is required"
},
"status": 400
}
// ✗ Bad error response
{
"error": "Bad request"
}
HTTP Status Codes:
400 Bad Request
- Invalid body format, validation errors413 Payload Too Large
- Body quá lớn415 Unsupported Media Type
- Wrong Content-Type422 Unprocessable Entity
- Valid format nhưng invalid dataPOST /api/products/search
{
"filters": {
"category": "laptop",
"price_min": 10000000,
"price_max": 20000000
},
"sort": {
"field": "price",
"order": "asc"
},
"page": 2,
"limit": 20
}
POST /api/search
{
"query": "gaming laptop",
"filters": [
{
"field": "category",
"operator": "eq",
"value": "laptop"
},
{
"field": "ram",
"operator": "gte",
"value": 16
},
{
"field": "brand",
"operator": "in",
"value": ["dell", "hp", "lenovo"]
}
],
"sort": [
{"field": "relevance", "order": "desc"},
{"field": "price", "order": "asc"}
],
"page": 1,
"limit": 20
}
PATCH /api/users/123
Content-Type: application/json-patch+json
[
{"op": "replace", "path": "/email", "value": "newemail@example.com"},
{"op": "add", "path": "/tags/-", "value": "premium"},
{"op": "remove", "path": "/old_field"}
]
POST /api/documents
Content-Type: multipart/form-data
title=My Document
description=Important document
category=reports
tags=Q1,2025,financial
visibility=private
file=[binary data]
import { test, expect } from '@playwright/test';
test.describe('Request Body Testing', () => {
test('POST - Create user with validation', async ({ request }) => {
const response = await request.post('https://api.example.com/users', {
data: {
name: 'Nguyễn Văn A',
email: 'vana@example.com',
age: 25,
address: {
city: 'Hà Nội',
country: 'Vietnam'
}
}
});
expect(response.status()).toBe(201);
const user = await response.json();
expect(user).toHaveProperty('id');
expect(user.name).toBe('Nguyễn Văn A');
});
test('POST - Validation errors', async ({ request }) => {
const response = await request.post('https://api.example.com/users', {
data: {
name: '', // Empty name
email: 'invalid-email', // Invalid email
age: 200 // Invalid age
}
});
expect(response.status()).toBe(400);
const error = await response.json();
expect(error).toHaveProperty('errors');
expect(error.errors).toHaveProperty('name');
expect(error.errors).toHaveProperty('email');
expect(error.errors).toHaveProperty('age');
});
test('POST - Complex nested structure', async ({ request }) => {
const response = await request.post('https://api.example.com/orders', {
data: {
customer: {
name: 'Nguyễn Văn A',
email: 'vana@example.com',
phone: '0123456789'
},
items: [
{
product_id: 101,
quantity: 2,
price: 500000,
options: {
color: 'black',
size: 'L'
}
},
{
product_id: 202,
quantity: 1,
price: 1000000
}
],
shipping_address: {
street: '123 Main St',
city: 'Hà Nội',
district: 'Ba Đình',
country: 'Vietnam',
postal_code: '100000'
},
payment_method: 'credit_card',
notes: 'Giao giờ hành chính'
}
});
expect(response.status()).toBe(201);
const order = await response.json();
expect(order).toHaveProperty('id');
expect(order).toHaveProperty('total');
expect(order.items).toHaveLength(2);
});
test('PATCH - Partial update', async ({ request }) => {
const response = await request.patch('https://api.example.com/users/123', {
data: {
status: 'active',
last_login: new Date().toISOString()
}
});
expect(response.status()).toBe(200);
});
test('PUT - Full update', async ({ request }) => {
const response = await request.put('https://api.example.com/posts/456', {
data: {
title: 'Updated Title',
content: 'Updated content...',
status: 'published',
tags: ['api', 'testing'],
publish_date: '2025-02-01'
}
});
expect(response.status()).toBe(200);
});
test('File upload', async ({ request }) => {
const fileContent = Buffer.from('test file content');
const response = await request.post('https://api.example.com/upload', {
multipart: {
title: 'Test Document',
description: 'Test description',
category: 'test',
file: {
name: 'test.txt',
mimeType: 'text/plain',
buffer: fileContent
}
}
});
expect(response.status()).toBe(201);
const result = await response.json();
expect(result).toHaveProperty('file_id');
expect(result).toHaveProperty('url');
});
test('Bulk operations', async ({ request }) => {
const response = await request.post('https://api.example.com/bulk', {
data: {
operations: [
{
action: 'create',
type: 'post',
data: {
title: 'Post 1',
content: 'Content 1'
}
},
{
action: 'create',
type: 'post',
data: {
title: 'Post 2',
content: 'Content 2'
}
},
{
action: 'update',
type: 'post',
id: 100,
data: {
status: 'published'
}
},
{
action: 'delete',
type: 'post',
id: 99
}
]
}
});
expect(response.status()).toBe(200);
const result = await response.json();
expect(result.results).toHaveLength(4);
expect(result.results[0].success).toBe(true);
});
test('Large JSON payload', async ({ request }) => {
// Create large dataset
const items = [];
for (let i = 0; i < 1000; i++) {
items.push({
id: i,
name: `Item ${i}`,
value: Math.random() * 1000
});
}
const response = await request.post('https://api.example.com/bulk-import', {
data: {
items: items
}
});
expect(response.status()).toBe(202); // Accepted for processing
});
});