Prompt Details
Model
(gemini-3.1-pro)
Token size
301
Example input
[Tech Stack & Framework]: Python using PyTest and Requests library.
[Endpoint / Functionality Description]: A POST endpoint /api/v1/users/register that accepts a JSON payload with 'username', 'email', and 'password'. It should return a 201 status code on success and save to a PostgreSQL database. Passwords must be at least 8 characters. Emails must be unique.
Example output
### 1. Test Scenarios Matrix
**Positive Paths:**
1. **Standard Registration:** Submitting a payload with a valid `username`, a unique `email`, and a `password` of exactly 8 characters returns a `201 Created` status code. The backend successfully persists the record in the PostgreSQL database with a securely hashed password.
2. **Extended Valid Payload:** Submitting a payload with a valid `username`, a unique `email`, and a long `password` (e.g., 64 characters) returns a `201 Created` status code, ensuring the system can handle large valid password strings without truncation or error.
**Negative Paths:**
1. **Duplicate Email Conflict:** Submitting a payload with an `email` that already exists in the PostgreSQL database returns a `409 Conflict` (or `400 Bad Request`) status code, preventing duplicate user records.
2. **Password Length Violation:** Submitting a payload where the `password` is strictly less than 8 characters (e.g., 7 characters) returns a `400 Bad Request` status code with an appropriate validation error message.
**Edge Cases:**
1. **Missing Required Fields:** Submitting an incomplete payload (e.g., missing the `username` key entirely) returns a `400 Bad Request` status code, verifying that the endpoint enforces schema requirements before attempting database insertion.
2. **Invalid Data Types:** Submitting a payload where the expected string fields are passed as integers or booleans (e.g., `"username": 123`) returns a `400 Bad Request` status code, ensuring robust type validation.
---
### 2. Mock Data / Payloads
**Environment Variables Required:**
* `API_BASE_URL`: The root URL for the API (e.g., `http://localhost:8000/api/v1`)
* `DATABASE_URL`: PostgreSQL connection string (e.g., `postgresql://dbuser:dbpass@localhost:5432/testdb`)
**Test Payloads (Python Dictionaries):**
```python
PAYLOADS = {
"valid_standard": {
"username": "qa_auto_user",
"email": "qa_auto_standard@example.com",
"password": "Password" # Exactly 8 characters
},
"valid_long_password": {
"username": "qa_auto_long",
"email": "qa_auto_longpass@example.com",
"password": "SuperSecretPasswordThatIsVeryLong123!@#"
},
"duplicate_email": {
"username": "qa_duplicate",
"email": "qa_auto_standard@example.com", # Matches the standard payload
"password": "ValidPassword123"
},
"invalid_short_password": {
"username": "qa_short_pass",
"email": "qa_shortpass@example.com",
"password": "Short1!" # 7 characters
},
"missing_field": {
"email": "qa_missing@example.com",
"password": "ValidPassword123"
# Missing 'username'
},
"invalid_data_type": {
"username": 12345, # Invalid type
"email": "qa_invalidtype@example.com",
"password": "ValidPassword123"
}
}
```
---
### 3. The Test Script
```python
# test_user_registration.py
import os
import pytest
import requests
import psycopg2
from psycopg2.extras import RealDictCursor
# --- Configuration & Setup ---
BASE_URL = os.getenv("API_BASE_URL", "http://localhost:8000/api/v1")
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:pass@localhost:5432/testdb")
ENDPOINT = f"{BASE_URL}/users/register"
# Mock Payloads defined in Section 2
from mock_data import PAYLOADS
@pytest.fixture(scope="module")
def db_connection():
"""Fixture to handle database connection for setup, assertions, and teardown."""
conn = psycopg2.connect(DATABASE_URL)
conn.autocommit = True
yield conn
conn.close()
@pytest.fixture(autouse=True)
def clean_test_data(db_connection):
"""Teardown fixture running after every test to keep the DB clean."""
yield
with db_connection.cursor() as cursor:
cursor.execute("DELETE FROM users WHERE email LIKE '%@example.com';")
# --- Test Suite ---
class TestUserRegistration:
# ---------------- Positive Paths ----------------
def test_standard_registration_success(self, db_connection):
"""Validates successful user creation and database persistence."""
payload = PAYLOADS["valid_standard"]
response = requests.post(ENDPOINT, json=payload)
# Assert API response
assert response.status_code == 201, f"Expected 201, got {response.status_code}"
# Verify persistence in PostgreSQL
with db_connection.cursor(cursor_factory=RealDictCursor) as cursor:
cursor.execute("SELECT * FROM users WHERE email = %s;", (payload["email"],))
db_user = cursor.fetchone()
assert db_user is not None, "User was not saved to the database"
assert db_user["username"] == payload["username"]
# Security check: ensure password is not stored in plaintext
assert db_user["password"] != payload["password"], "Password stored in plaintext!"
def test_extended_valid_payload_success(self):
"""Validates that a long, complex password is handled correctly."""
payload = PAYLOADS["valid_long_password"]
response = requests.post(ENDPOINT, json=payload)
assert response.status_code == 201
assert "id" in response.json(), "Response should contain the created user's ID"
# ---------------- Negative Paths ----------------
def test_duplicate_email_conflict(self):
"""Validates that registering an existing email is rejected."""
# Setup: Create the initial user
requests.post(ENDPOINT, json=PAYLOADS["valid_standard"])
# Action: Attempt to register with the same email
response = requests.post(ENDPOINT, json=PAYLOADS["duplicate_email"])
# Assert API correctly identifies and handles the duplicate
assert response.status_code in [400, 409], f"Expected 400 or 409, got {response.status_code}"
assert "already exists" in response.text.lower() or "duplicate" in response.text.lower()
def test_password_length_violation(self):
"""Validates that passwords under 8 characters are rejected."""
payload = PAYLOADS["invalid_short_password"]
response = requests.post(ENDPOINT, json=payload)
assert response.status_code == 400
response_data = response.json()
# Assert specific validation error is returned
assert "password" in response.text.lower()
assert "8 characters" in response.text.lower() or "length" in response.text.lower()
# ---------------- Edge Cases ----------------
def test_missing_required_field(self):
"""Validates endpoint robustness when a required schema key is missing."""
payload = PAYLOADS["missing_field"]
response = requests.post(ENDPOINT, json=payload)
assert response.status_code == 400
assert "username" in response.text.lower(), "Error should indicate missing username"
def test_invalid_data_type(self):
"""Validates payload schema typing (e.g., integer instead of string)."""
payload = PAYLOADS["invalid_data_type"]
response = requests.post(ENDPOINT, json=payload)
# The system should not crash (500) but gracefully reject (400)
assert response.status_code == 400
assert "string" in response.text.lower() or "type" in response.text.lower()
```
By purchasing this prompt, you agree to our terms of service
GEMINI-3.1-PRO
Writing test scripts and thinking of every possible edge case takes hours. This prompt acts as a Senior QA Automation Engineer. Provide it with your tech stack and a description of the endpoint or function you need to test, and it will automatically generate comprehensive test scenarios (positive, negative, and edge cases) along with the actual, ready-to-run test scripts. Ideal for QA Analysts, Backend Developers, and DevOps teams looking to streamline their CI/CD pipelines.
...more
Added 4 hours ago
