metallkart-erp/test-estimates.sh

285 lines
10 KiB
Bash

#!/bin/bash
# Integration tests for Workflow 2 — Estimate Validation
# Tests the full cycle: DRAFT→SUBMITTED→APPROVED→LOCKED
# Plus: revision flow, versioning, RBAC
BASE="http://localhost:3001/api/v1"
PASS=0
FAIL=0
TOTAL=0
# Helper: login and get token
login() {
local email=$1
local resp
resp=$(python3 -c "
import urllib.request, json
data = json.dumps({'email':'$email','password':'MetallKart2026!'}).encode()
req = urllib.request.Request('$BASE/auth/login', data, {'Content-Type':'application/json'})
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['accessToken'])
" 2>/dev/null)
echo "$resp"
}
# Helper: make API call
api() {
local method=$1
local path=$2
local token=$3
local body=$4
python3 -c "
import urllib.request, json, sys
url = '$BASE$path'
headers = {'Authorization': 'Bearer $token'}
data = None
if '$body':
data = '$body'.encode()
headers['Content-Type'] = 'application/json'
req = urllib.request.Request(url, data, headers, method='$method')
try:
resp = urllib.request.urlopen(req)
result = resp.read().decode()
print(json.dumps({'status': resp.status, 'body': json.loads(result)}))
except urllib.error.HTTPError as e:
result = e.read().decode()
try:
body = json.loads(result)
except:
body = result
print(json.dumps({'status': e.code, 'body': body}))
" 2>/dev/null
}
assert_status() {
local test_name=$1
local expected=$2
local response=$3
TOTAL=$((TOTAL+1))
local actual
actual=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['status'])" 2>/dev/null)
if [ "$actual" = "$expected" ]; then
echo "$test_name (HTTP $actual)"
PASS=$((PASS+1))
else
echo "$test_name — expected HTTP $expected, got $actual"
echo " Response: $(echo "$response" | head -c 300)"
FAIL=$((FAIL+1))
fi
}
extract() {
local response=$1
local field=$2
echo "$response" | python3 -c "
import json, sys
data = json.loads(sys.stdin.read())
parts = '$field'.split('.')
obj = data['body']
for p in parts:
if isinstance(obj, dict):
obj = obj.get(p)
else:
obj = None
break
print(obj if obj is not None else '')
" 2>/dev/null
}
echo "=== Workflow 2 — Estimate Validation Integration Tests ==="
echo ""
# Login as different users
echo "1. Authenticating users..."
TOKEN_ENGINEER=$(login "timofey@metallcart.ru")
TOKEN_DIRECTOR=$(login "maxim@metallcart.ru")
TOKEN_COMMERCIAL=$(login "anna.v@metallcart.ru")
TOKEN_ADMIN=$(login "louis@metallcart.ru")
if [ -z "$TOKEN_ENGINEER" ] || [ -z "$TOKEN_DIRECTOR" ]; then
echo " ✗ Failed to authenticate. Aborting."
exit 1
fi
echo " ✓ All users authenticated"
echo ""
# Get test order ID
echo "2. Fetching test order..."
RESP=$(api GET "/orders?page=1&limit=1" "$TOKEN_ENGINEER")
ORDER_ID=$(extract "$RESP" "items.0.id")
if [ -z "$ORDER_ID" ] || [ "$ORDER_ID" = "None" ]; then
# Use the seeded order directly
ORDER_ID=1
fi
echo " Using order ID: $ORDER_ID"
echo ""
# === Test: Create estimate (ENGINEER) ===
echo "3. Creating estimate..."
RESP=$(api POST "/estimates" "$TOKEN_ENGINEER" "{\"orderId\":$ORDER_ID,\"totalServices\":120000,\"totalMaterials\":380000,\"totalLabor\":90000,\"supplyOverhead\":25000,\"margin\":160000,\"tradeSurcharge\":10000,\"sellingPrice\":785000}")
assert_status "Engineer creates estimate" "201" "$RESP"
ESTIMATE_ID=$(extract "$RESP" "id")
EST_VERSION=$(extract "$RESP" "version")
echo " Created estimate ID=$ESTIMATE_ID version=$EST_VERSION"
echo ""
# === Test: RBAC — Commercial cannot create ===
echo "4. RBAC tests..."
RESP=$(api POST "/estimates" "$TOKEN_COMMERCIAL" "{\"orderId\":$ORDER_ID,\"totalServices\":1000,\"totalMaterials\":1000,\"totalLabor\":1000,\"supplyOverhead\":100,\"margin\":100,\"tradeSurcharge\":0,\"sellingPrice\":3200}")
assert_status "Commercial cannot create estimate (403)" "403" "$RESP"
# === Test: Commercial cannot submit ===
RESP=$(api POST "/estimates/$ESTIMATE_ID/submit" "$TOKEN_COMMERCIAL")
assert_status "Commercial cannot submit estimate (403)" "403" "$RESP"
echo ""
# === Test: Get estimate ===
echo "5. Read estimate..."
RESP=$(api GET "/estimates/$ESTIMATE_ID" "$TOKEN_ENGINEER")
assert_status "Get estimate by ID" "200" "$RESP"
EST_STATUS=$(extract "$RESP" "status")
echo " Status: $EST_STATUS"
echo ""
# === Test: Update estimate (DRAFT) ===
echo "6. Update estimate in DRAFT..."
RESP=$(api PUT "/estimates/$ESTIMATE_ID" "$TOKEN_ENGINEER" "{\"sellingPrice\":800000}")
assert_status "Engineer updates estimate" "200" "$RESP"
echo ""
# === Test: Submit estimate ===
echo "7. Submit estimate..."
RESP=$(api POST "/estimates/$ESTIMATE_ID/submit" "$TOKEN_ENGINEER")
assert_status "Engineer submits estimate" "200" "$RESP"
EST_STATUS=$(extract "$RESP" "status")
echo " Status after submit: $EST_STATUS"
echo ""
# === Test: Cannot edit after submit ===
echo "8. Cannot edit after submit..."
RESP=$(api PUT "/estimates/$ESTIMATE_ID" "$TOKEN_ENGINEER" "{\"sellingPrice\":900000}")
assert_status "Cannot edit SUBMITTED estimate (400)" "400" "$RESP"
echo ""
# === Test: Request revision (DIRECTOR) ===
echo "9. Director requests revision..."
RESP=$(api POST "/estimates/$ESTIMATE_ID/request-revision" "$TOKEN_DIRECTOR" "{\"comment\":\"Пересмотрите стоимость материалов — слишком высокая\"}")
assert_status "Director requests revision" "200" "$RESP"
EST_STATUS=$(extract "$RESP" "status")
echo " Status after revision request: $EST_STATUS"
echo ""
# === Test: Engineer edits after revision request ===
echo "10. Engineer edits after revision request..."
RESP=$(api PUT "/estimates/$ESTIMATE_ID" "$TOKEN_ENGINEER" "{\"totalMaterials\":350000,\"sellingPrice\":770000}")
assert_status "Engineer edits after revision request" "200" "$RESP"
EST_STATUS=$(extract "$RESP" "status")
echo " Status after edit (back to DRAFT): $EST_STATUS"
echo ""
# === Test: Re-submit ===
echo "11. Re-submit after revision..."
RESP=$(api POST "/estimates/$ESTIMATE_ID/submit" "$TOKEN_ENGINEER")
assert_status "Engineer re-submits" "200" "$RESP"
echo ""
# === Test: Approve and auto-lock ===
echo "12. Director approves (auto-lock)..."
RESP=$(api POST "/estimates/$ESTIMATE_ID/approve" "$TOKEN_DIRECTOR")
assert_status "Director approves estimate" "200" "$RESP"
EST_STATUS=$(extract "$RESP" "status")
IS_LOCKED=$(extract "$RESP" "isLocked")
echo " Status: $EST_STATUS, isLocked: $IS_LOCKED"
echo ""
# === Test: Cannot edit locked estimate ===
echo "13. Cannot edit locked estimate..."
RESP=$(api PUT "/estimates/$ESTIMATE_ID" "$TOKEN_ENGINEER" "{\"sellingPrice\":999999}")
assert_status "Cannot edit LOCKED estimate (400)" "400" "$RESP"
echo ""
# === Test: Cannot submit locked estimate ===
echo "14. Cannot submit locked estimate..."
RESP=$(api POST "/estimates/$ESTIMATE_ID/submit" "$TOKEN_ENGINEER")
assert_status "Cannot submit LOCKED estimate (400)" "400" "$RESP"
echo ""
# === Test: Version history ===
echo "15. Version history..."
RESP=$(api GET "/estimates/order/$ORDER_ID/versions" "$TOKEN_ENGINEER")
assert_status "Get version history" "200" "$RESP"
echo ""
# === Test: Create new version from locked ===
echo "16. Create new version..."
RESP=$(api POST "/estimates/order/$ORDER_ID/new-version" "$TOKEN_ENGINEER")
assert_status "Engineer creates new version" "201" "$RESP"
NEW_ESTIMATE_ID=$(extract "$RESP" "id")
NEW_VERSION=$(extract "$RESP" "version")
echo " New estimate ID=$NEW_ESTIMATE_ID version=$NEW_VERSION"
echo ""
# === Test: Cannot create another draft while one exists ===
echo "17. Cannot create duplicate draft..."
RESP=$(api POST "/estimates/order/$ORDER_ID/new-version" "$TOKEN_ENGINEER")
assert_status "Cannot create another draft (400)" "400" "$RESP"
echo ""
# === Test: List estimates with filters ===
echo "18. List estimates..."
RESP=$(api GET "/estimates?orderId=$ORDER_ID" "$TOKEN_ENGINEER")
assert_status "List estimates for order" "200" "$RESP"
TOTAL_ITEMS=$(extract "$RESP" "total")
echo " Total estimates for order: $TOTAL_ITEMS"
echo ""
# === Test: RBAC — Engineer cannot approve ===
echo "19. RBAC — Engineer cannot approve..."
# Submit the new version first
RESP=$(api PUT "/estimates/$NEW_ESTIMATE_ID" "$TOKEN_ENGINEER" "{\"totalServices\":130000,\"totalMaterials\":360000,\"totalLabor\":95000,\"supplyOverhead\":26000,\"margin\":165000,\"tradeSurcharge\":12000,\"sellingPrice\":788000}")
assert_status "Update new version" "200" "$RESP"
RESP=$(api POST "/estimates/$NEW_ESTIMATE_ID/submit" "$TOKEN_ENGINEER")
assert_status "Submit new version" "200" "$RESP"
RESP=$(api POST "/estimates/$NEW_ESTIMATE_ID/approve" "$TOKEN_ENGINEER")
assert_status "Engineer cannot approve (403)" "403" "$RESP"
echo ""
# === Test: Director approves new version ===
echo "20. Director approves new version..."
RESP=$(api POST "/estimates/$NEW_ESTIMATE_ID/approve" "$TOKEN_DIRECTOR")
assert_status "Director approves v2" "200" "$RESP"
EST_STATUS=$(extract "$RESP" "status")
echo " Status: $EST_STATUS"
echo ""
# === Test: Revision request requires comment ===
echo "21. Revision request requires comment..."
# Create a third version to test
RESP=$(api POST "/estimates/order/$ORDER_ID/new-version" "$TOKEN_ENGINEER")
V3_ID=$(extract "$RESP" "id")
RESP=$(api PUT "/estimates/$V3_ID" "$TOKEN_ENGINEER" "{\"totalServices\":135000,\"totalMaterials\":365000,\"totalLabor\":98000,\"supplyOverhead\":27000,\"margin\":170000,\"tradeSurcharge\":13000,\"sellingPrice\":808000}")
RESP=$(api POST "/estimates/$V3_ID/submit" "$TOKEN_ENGINEER")
RESP=$(api POST "/estimates/$V3_ID/request-revision" "$TOKEN_DIRECTOR" "{}")
assert_status "Revision without comment fails (400)" "400" "$RESP"
echo ""
# === Test: Admin can do everything ===
echo "22. Admin powers..."
RESP=$(api POST "/estimates/$V3_ID/approve" "$TOKEN_ADMIN")
assert_status "Admin can approve" "200" "$RESP"
echo ""
# === Final version history ===
echo "23. Final version history..."
RESP=$(api GET "/estimates/order/$ORDER_ID/versions" "$TOKEN_ENGINEER")
assert_status "Final version history" "200" "$RESP"
echo ""
echo "========================================="
echo "Results: $PASS passed, $FAIL failed (out of $TOTAL tests)"
echo "========================================="
if [ "$FAIL" -gt 0 ]; then
exit 1
fi