mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 20:51:49 +00:00
init
This commit is contained in:
281
negot8/test/test_pdf_generator.py
Normal file
281
negot8/test/test_pdf_generator.py
Normal file
@@ -0,0 +1,281 @@
|
||||
"""
|
||||
test_pdf_generator.py — Tests for negoT8 Deal Agreement PDF generation
|
||||
|
||||
Run from the project root:
|
||||
cd /path/to/negot8
|
||||
python test/test_pdf_generator.py
|
||||
|
||||
What this tests:
|
||||
1. Freelance deal PDF — rich details (budget, scope, timeline, payment)
|
||||
2. Marketplace (buy/sell) PDF — item, price, delivery
|
||||
3. Minimal data — all optional fields absent; should still produce a valid PDF
|
||||
4. Blockchain proof attached — real-looking proof dict
|
||||
5. Mock blockchain proof — mock=True path
|
||||
6. File is actually written to /tmp and is non-empty
|
||||
7. Temp-file cleanup helper works
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
# ── Make sure backend/ is on the path ────────────────────────────────────────
|
||||
BACKEND = os.path.join(os.path.dirname(__file__), "..", "backend")
|
||||
sys.path.insert(0, os.path.abspath(BACKEND))
|
||||
|
||||
from tools.pdf_generator import generate_deal_pdf
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Shared fixtures
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
USER_A = {"id": 111111111, "name": "Alice Sharma", "username": "alice_s"}
|
||||
USER_B = {"id": 222222222, "name": "Bob Chatterjee", "username": "bobchat"}
|
||||
|
||||
REAL_PROOF = {
|
||||
"success": True,
|
||||
"mock": False,
|
||||
"tx_hash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
|
||||
"block_number": 87654321,
|
||||
"agreement_hash": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
|
||||
"explorer_url": "https://amoy.polygonscan.com/tx/0xabcdef1234567890",
|
||||
"gas_used": 42000,
|
||||
}
|
||||
|
||||
MOCK_PROOF = {
|
||||
"success": True,
|
||||
"mock": True,
|
||||
"tx_hash": "0xMOCKTX1234567890",
|
||||
"block_number": 0,
|
||||
"agreement_hash": "0xMOCKHASH1234567890",
|
||||
"explorer_url": "",
|
||||
"gas_used": 0,
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Test helpers
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _check_pdf(path: str, label: str):
|
||||
"""Assert the file exists, is non-empty, and starts with the PDF magic bytes."""
|
||||
assert os.path.exists(path), f"[{label}] PDF file not found at {path}"
|
||||
size = os.path.getsize(path)
|
||||
assert size > 500, f"[{label}] PDF suspiciously small: {size} bytes"
|
||||
with open(path, "rb") as f:
|
||||
magic = f.read(4)
|
||||
assert magic == b"%PDF", f"[{label}] File does not start with PDF magic bytes: {magic}"
|
||||
print(f" ✅ [{label}] PDF OK — {size:,} bytes → {path}")
|
||||
|
||||
|
||||
def _cleanup(path: str):
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Individual tests
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
async def test_freelance_full():
|
||||
"""Freelance deal with full details + real blockchain proof."""
|
||||
final_proposal = {
|
||||
"summary": "Alice will build the dashboard in 3 weeks for Rs. 45,000 with 50% upfront.",
|
||||
"details": {
|
||||
"budget": "45000",
|
||||
"timeline": "3 weeks",
|
||||
"scope": ["Admin dashboard", "REST API integration", "Mobile responsive UI"],
|
||||
"payment_schedule": "50% upfront (Rs. 22,500) + 50% on delivery",
|
||||
"upfront": "22500",
|
||||
"ip_ownership": "Full transfer to client on final payment",
|
||||
},
|
||||
}
|
||||
preferences_a = {
|
||||
"goal": "Build a dashboard",
|
||||
"raw_details": {
|
||||
"role": "freelancer", "skill": "React + FastAPI",
|
||||
"rate": "1500", "hours": "30", "upfront_minimum": "50",
|
||||
},
|
||||
}
|
||||
preferences_b = {
|
||||
"goal": "Hire a developer",
|
||||
"raw_details": {
|
||||
"role": "client", "budget": "45000",
|
||||
"required_features": ["Admin dashboard", "API", "Mobile UI"],
|
||||
},
|
||||
}
|
||||
|
||||
path = await generate_deal_pdf(
|
||||
negotiation_id = "freelance_test_001",
|
||||
feature_type = "freelance",
|
||||
final_proposal = final_proposal,
|
||||
user_a = USER_A,
|
||||
user_b = USER_B,
|
||||
rounds_taken = 4,
|
||||
sat_a = 85.0,
|
||||
sat_b = 78.0,
|
||||
preferences_a = preferences_a,
|
||||
preferences_b = preferences_b,
|
||||
blockchain_proof = REAL_PROOF,
|
||||
)
|
||||
_check_pdf(path, "freelance_full")
|
||||
_cleanup(path)
|
||||
|
||||
|
||||
async def test_marketplace_full():
|
||||
"""Buy/sell deal with full details + mock blockchain proof."""
|
||||
final_proposal = {
|
||||
"summary": "iPhone 14 sold for Rs. 52,000. Pickup at Andheri station on Saturday.",
|
||||
"details": {
|
||||
"agreed_price": "52000",
|
||||
"delivery": "Pickup — Andheri West Metro station, Saturday 4 PM",
|
||||
"condition": "Used — 6 months old, no scratches",
|
||||
"market_price": "55000",
|
||||
},
|
||||
}
|
||||
preferences_a = {
|
||||
"goal": "Sell iPhone 14",
|
||||
"raw_details": {
|
||||
"role": "seller", "item": "iPhone 14 128GB Black",
|
||||
"asking_price": "55000", "minimum_price": "50000",
|
||||
},
|
||||
}
|
||||
preferences_b = {
|
||||
"goal": "Buy iPhone 14",
|
||||
"raw_details": {
|
||||
"role": "buyer", "item": "iPhone 14",
|
||||
"max_budget": "54000", "offer_price": "49000",
|
||||
},
|
||||
}
|
||||
|
||||
path = await generate_deal_pdf(
|
||||
negotiation_id = "marketplace_test_001",
|
||||
feature_type = "marketplace",
|
||||
final_proposal = final_proposal,
|
||||
user_a = USER_A,
|
||||
user_b = USER_B,
|
||||
rounds_taken = 3,
|
||||
sat_a = 72.0,
|
||||
sat_b = 88.0,
|
||||
preferences_a = preferences_a,
|
||||
preferences_b = preferences_b,
|
||||
blockchain_proof = MOCK_PROOF,
|
||||
)
|
||||
_check_pdf(path, "marketplace_full")
|
||||
_cleanup(path)
|
||||
|
||||
|
||||
async def test_minimal_data():
|
||||
"""Both feature_type is unknown and all optional fields are empty — should not crash."""
|
||||
path = await generate_deal_pdf(
|
||||
negotiation_id = "minimal_test_001",
|
||||
feature_type = "generic",
|
||||
final_proposal = {"summary": "Parties agreed to share expenses equally."},
|
||||
user_a = {"id": 1, "name": "", "username": "userA"},
|
||||
user_b = {"id": 2, "name": "", "username": "userB"},
|
||||
rounds_taken = 1,
|
||||
sat_a = 60.0,
|
||||
sat_b = 60.0,
|
||||
blockchain_proof = None,
|
||||
)
|
||||
_check_pdf(path, "minimal_data")
|
||||
_cleanup(path)
|
||||
|
||||
|
||||
async def test_no_blockchain_proof():
|
||||
"""Freelance deal where blockchain proof hasn't been registered yet."""
|
||||
final_proposal = {
|
||||
"summary": "React Native app, Rs. 80,000, 6 weeks.",
|
||||
"details": {
|
||||
"budget": "80000",
|
||||
"timeline": "6 weeks",
|
||||
"scope": ["React Native app", "Backend API"],
|
||||
},
|
||||
}
|
||||
path = await generate_deal_pdf(
|
||||
negotiation_id = "noproof_test_001",
|
||||
feature_type = "freelance",
|
||||
final_proposal = final_proposal,
|
||||
user_a = USER_A,
|
||||
user_b = USER_B,
|
||||
rounds_taken = 5,
|
||||
sat_a = 90.0,
|
||||
sat_b = 70.0,
|
||||
blockchain_proof = None,
|
||||
)
|
||||
_check_pdf(path, "no_blockchain_proof")
|
||||
_cleanup(path)
|
||||
|
||||
|
||||
async def test_unicode_safe():
|
||||
"""
|
||||
Ensure the PDF builder doesn't crash on characters outside Latin-1
|
||||
(Rs. symbol ₹, em-dashes, etc.).
|
||||
"""
|
||||
final_proposal = {
|
||||
"summary": "₹45,000 deal — React dashboard — agreed ✓",
|
||||
"details": {
|
||||
"budget": "₹45,000",
|
||||
"timeline": "3 weeks – confirmed",
|
||||
"scope": ["Dashboard • Admin panel", "API • REST"],
|
||||
},
|
||||
}
|
||||
path = await generate_deal_pdf(
|
||||
negotiation_id = "unicode_test_001",
|
||||
feature_type = "freelance",
|
||||
final_proposal = final_proposal,
|
||||
user_a = {"id": 1, "name": "Anirbán Bāsak", "username": "anirban"},
|
||||
user_b = {"id": 2, "name": "Rāhul Gupta", "username": "rahul"},
|
||||
rounds_taken = 2,
|
||||
sat_a = 88.0,
|
||||
sat_b = 82.0,
|
||||
blockchain_proof = MOCK_PROOF,
|
||||
)
|
||||
_check_pdf(path, "unicode_safe")
|
||||
_cleanup(path)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Runner
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
TESTS = [
|
||||
("Freelance full details + real blockchain proof", test_freelance_full),
|
||||
("Marketplace (buy/sell) + mock proof", test_marketplace_full),
|
||||
("Minimal / sparse data — no crash", test_minimal_data),
|
||||
("No blockchain proof yet", test_no_blockchain_proof),
|
||||
("Unicode / special chars — Latin-1 safety", test_unicode_safe),
|
||||
]
|
||||
|
||||
|
||||
async def main():
|
||||
print("\n🧪 negoT8 — PDF Generator Tests")
|
||||
print("=" * 52)
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
for name, fn in TESTS:
|
||||
print(f"\n▶ {name}")
|
||||
try:
|
||||
await fn()
|
||||
passed += 1
|
||||
except Exception as exc:
|
||||
import traceback
|
||||
print(f" ❌ FAILED: {exc}")
|
||||
traceback.print_exc()
|
||||
failed += 1
|
||||
|
||||
print("\n" + "=" * 52)
|
||||
print(f"Results: {passed} passed | {failed} failed | {len(TESTS)} total")
|
||||
|
||||
if failed == 0:
|
||||
print("✅ All PDF tests passed!\n")
|
||||
else:
|
||||
print("❌ Some tests failed — see output above.\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user