#!/usr/bin/env python3
"""Phase 3 server-side tests - form logic validation."""
import sys
import os
import json
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.services.form_logic import (
evaluate_all_conditions,
validate_submission,
compute_all_fields,
evaluate_condition,
)
# Sample field config embedded to avoid DB dependency
SAMPLE_FIELD_CONFIG = {
"fields": [
{
"key": "service",
"name": "service",
"type": "select",
"label": "Service Type",
"required": True,
"options": ["Web Development", "SEO Audit", "Logo Design", "Full Package"],
"validation": None,
"condition": None,
"calculation": None,
"step": 1,
},
{
"key": "budget",
"name": "budget",
"type": "number",
"label": "Budget Range",
"required": False,
"condition": {"field": "service", "operator": "eq", "value": "Web Development"},
"validation": None,
"calculation": None,
"step": 1,
},
{
"key": "timeline",
"name": "timeline",
"type": "select",
"label": "Timeline",
"required": True,
"condition": {
"any": [
{"field": "service", "operator": "eq", "value": "Web Development"},
{"field": "service", "operator": "eq", "value": "SEO Audit"},
]
},
"options": ["ASAP", "1-2 weeks", "1 month", "Flexible"],
"validation": None,
"calculation": None,
"step": 1,
},
{
"key": "logo_style",
"name": "logo_style",
"type": "select",
"label": "Logo Style Preference",
"required": True,
"condition": {"field": "service", "operator": "eq", "value": "Logo Design"},
"options": ["Minimalist", "Vintage", "Modern", "Abstract"],
"validation": None,
"calculation": None,
"step": 1,
},
{
"key": "priority",
"name": "priority",
"type": "select",
"label": "Priority Level",
"required": True,
"conditional_required": {
"field": "service",
"operator": "eq",
"value": "Full Package",
"message": "Priority is required for Full Package",
},
"options": ["Low", "Medium", "High", "Critical"],
"validation": None,
"condition": None,
"calculation": None,
"step": 1,
},
{
"key": "discount",
"name": "discount",
"type": "computed",
"label": "Discount %",
"computed_from": "priority",
"computed_method": "map",
"computed_map": {"Low": "0", "Medium": "5", "High": "10", "Critical": "15"},
"validation": None,
"condition": None,
"calculation": None,
"step": 1,
"required": False,
},
{
"key": "rush_fee",
"name": "rush_fee",
"type": "computed",
"label": "Rush Fee",
"computed_from": "timeline",
"computed_method": "map",
"computed_map": {
"ASAP": "500",
"1-2 weeks": "100",
"1 month": "0",
"Flexible": "0",
},
"validation": None,
"condition": None,
"calculation": None,
"step": 1,
"required": False,
},
{
"key": "estimated_total",
"name": "estimated_total",
"type": "computed",
"label": "Estimated Total",
"computed_method": "expression",
"computed_expression": "{{budget}} + {{rush_fee}} - ({{budget}} * {{discount}} / 100)",
"validation": None,
"condition": None,
"calculation": None,
"step": 1,
"required": False,
},
{
"key": "notes",
"name": "notes",
"type": "textarea",
"label": "Additional Notes",
"required": False,
"conditional_default": {
"condition": {
"field": "service",
"operator": "eq",
"value": "Full Package",
},
"value": "Full package inquiry - please provide detailed requirements.",
},
"validation": None,
"condition": None,
"calculation": None,
"step": 1,
},
{
"key": "contact",
"name": "contact",
"type": "select",
"label": "Preferred Contact Method",
"required": False,
"options": ["Email", "Phone", "Video Call"],
"conditional_required": {
"field": "service",
"operator": "eq",
"value": "Web Development",
},
"validation": None,
"condition": None,
"calculation": None,
"step": 1,
},
]
}
class TestFormLogic:
"""Test form logic evaluation."""
def test_simple_condition(self):
"""Test 1: Simple condition (budget visible for Web Dev)."""
result = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "Web Development"})
assert result["budget"]["visible"] is True
def test_any_group_condition(self):
"""Test 2: ANY group (timeline visible for SEO, budget hidden)."""
result = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "SEO Audit"})
assert result["timeline"]["visible"] is True
assert result["budget"]["visible"] is False
def test_all_group_condition(self):
"""Test 3: ALL group via manual construct."""
test_field_all = {
"key": "test",
"condition": {
"all": [
{"field": "service", "operator": "eq", "value": "Web Development"},
{"field": "timeline", "operator": "eq", "value": "1-2 weeks"},
]
},
}
assert (
evaluate_condition(
test_field_all, {"service": "Web Development", "timeline": "1-2 weeks"}
)
is True
)
assert (
evaluate_condition(
test_field_all, {"service": "Web Development", "timeline": "ASAP"}
)
is False
)
assert (
evaluate_condition(
test_field_all, {"service": "SEO Audit", "timeline": "1-2 weeks"}
)
is False
)
def test_conditional_required(self):
"""Test 4: Conditional required."""
result = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "Full Package"})
assert result["priority"]["required"] is True
result3 = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "Web Development"})
assert result3["contact"]["required"] is True
result4 = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "SEO Audit"})
assert result4["contact"]["required"] is False
def test_computed_field_map(self):
"""Test 5: Computed field - map (discount from priority)."""
fields = SAMPLE_FIELD_CONFIG["fields"]
result = compute_all_fields(fields, {"priority": "High"})
assert result["discount"] == 10
def test_computed_field_expression(self):
"""Test 6: Computed field - expression."""
fields = SAMPLE_FIELD_CONFIG["fields"]
result = compute_all_fields(fields, {"priority": "High", "timeline": "ASAP", "budget": "10000"})
assert result["rush_fee"] == 500
assert result["estimated_total"] == 10000 + 500 - (10000 * 10 / 100) # 9500
def test_conditional_default(self):
"""Test 7: Conditional default (notes for Full Package)."""
result = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "Full Package"})
assert "Full package" in result["notes"]["default"]
result2 = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "Web Development"})
assert result2["notes"]["default"] == ""
def test_conditional_validation(self):
"""Test 8: Conditional validation (contact required for Web Dev)."""
errors = validate_submission(
SAMPLE_FIELD_CONFIG, {"service": "Web Development", "contact": "", "priority": "Medium"}
)
assert len(errors) > 0
assert any(e["field"] == "contact" for e in errors)
errors2 = validate_submission(
SAMPLE_FIELD_CONFIG,
{"service": "Logo Design", "contact": "", "priority": "Medium", "logo_style": "Minimalist"},
)
assert len(errors2) == 0
def test_multi_field_computation(self):
"""Test 9: Multi-field computation."""
fields = SAMPLE_FIELD_CONFIG["fields"]
computed = compute_all_fields(
fields, {"priority": "High", "timeline": "ASAP", "budget": "10000"}
)
assert computed["rush_fee"] == 500
assert computed["discount"] == 10
def test_backward_compat_formula(self):
"""Test 10: Backward compatibility (legacy calculation.formula)."""
legacy_field = {
"key": "test_total",
"type": "computed",
"label": "Total",
"calculation": {"formula": "{{price}} x {{qty}}"},
}
legacy_result = compute_all_fields([legacy_field], {"price": "100", "qty": "5"})
assert legacy_result["test_total"] == "100 x 5"
def test_cascade_independent_visibility(self):
"""Test 11: Cascade - hidden dependency."""
result = evaluate_all_conditions(SAMPLE_FIELD_CONFIG, {"service": "Logo Design"})
assert result["timeline"]["visible"] is False
assert result["logo_style"]["visible"] is True
def test_cascade_hidden_dependency_chains(self):
"""Test 12: Cascade - field that DOES depend on hidden field."""
all_result = evaluate_all_conditions(
SAMPLE_FIELD_CONFIG, {"service": "Logo Design", "budget": "5000"}
)
assert all_result["budget"]["visible"] is False