import unittest from ai_web_agent_console.query_contracts import ( append_query_contract_guidance, evidence_contract_report, matching_query_contracts, ) def test_leave_eligibility_question_selects_external_contract(): contracts = matching_query_contracts( "직원 E9999의 연차 잔여와 신청 가능 여부를 알려줘", "search_hr_data", ) assert [contract["id"] for contract in contracts] == [ "leave_balance_eligibility" ] def test_unrelated_tool_does_not_receive_leave_contract(): contracts = matching_query_contracts( "직원 E9999의 연차 잔여를 알려줘", "search_carrier_performance", ) assert contracts == () def test_incomplete_result_cannot_be_interpreted_as_zero_balance(): contracts = matching_query_contracts( "직원 E9999의 연차 사용 가능 여부를 알려줘", "search_hr_data", ) evidence = { "result": ( 'DATA_RESULT: {"items":[{"EMPLOYEE_CODE":"E9999",' '"REMAINING_DAYS":0}]}' ) } report = evidence_contract_report(contracts, evidence) assert report[0]["satisfied"] is False assert "AVAILABLE_REMAINING_DAYS" in report[0]["missing_fields"] assert "LEAVE_YEAR" in report[0]["missing_fields"] def test_complete_database_row_satisfies_contract_without_fixture_values(): contracts = matching_query_contracts( "직원 E9999의 연차 신청 가능 여부를 알려줘", "search_hr_data", ) evidence = { "result": ( 'DATA_RESULT\n{"items":[{"EMPLOYEE_CODE":"E9999",' '"LEAVE_YEAR":2031,"ANNUAL_ENTITLEMENT":17,' '"CARRYOVER_DAYS":2,"USED_DAYS":4,"PENDING_DAYS":1,' '"AVAILABLE_REMAINING_DAYS":14}]}' ) } report = evidence_contract_report(contracts, evidence) assert report[0]["satisfied"] is True assert report[0]["validated_record"]["LEAVE_YEAR"] == 2031 assert report[0]["validated_record"]["AVAILABLE_REMAINING_DAYS"] == 14 def test_conflicting_computed_value_fails_contract(): contracts = matching_query_contracts( "직원 E9999의 연차 신청 가능 여부를 알려줘", "search_hr_data", ) evidence = { "result": ( 'DATA_RESULT\n{"items":[{"EMPLOYEE_CODE":"E9999",' '"LEAVE_YEAR":2031,"ANNUAL_ENTITLEMENT":17,' '"CARRYOVER_DAYS":2,"USED_DAYS":4,"PENDING_DAYS":1,' '"AVAILABLE_REMAINING_DAYS":0}]}' ) } report = evidence_contract_report(contracts, evidence) assert report[0]["satisfied"] is False assert report[0]["computed_field_checks"][0]["expected"] == "14" assert report[0]["computed_field_checks"][0]["actual"] == "0" def test_historical_period_rejects_yes_no_decision_and_missing_counts(): contracts = matching_query_contracts( "직원 E9999의 과거 연차 신청 가능 여부를 알려줘", "search_hr_data", ) evidence = { "result": ( 'DATA_RESULT\n{"items":[{"EMPLOYEE_CODE":"E9999",' '"LEAVE_YEAR":2031,"ANNUAL_ENTITLEMENT":17,' '"CARRYOVER_DAYS":2,"USED_DAYS":4,"PENDING_DAYS":1,' '"AVAILABLE_REMAINING_DAYS":14,' '"REQUEST_PERIOD_LABEL":"HISTORICAL_REQUEST_PERIOD",' '"CAN_APPLY":"YES","INCLUSIVE_CALENDAR_DAYS":11}]}' ) } report = evidence_contract_report(contracts, evidence) assert report[0]["satisfied"] is False failed = { check["check"] for check in report[0]["temporal_contract_checks"] if not check["satisfied"] } assert failed == { "past_period_decision", "no_past_period_yes_no_decision", "date_range_counts", } def test_historical_period_contract_accepts_non_decision_status(): contracts = matching_query_contracts( "직원 E9999의 과거 연차 신청 가능 여부를 알려줘", "search_hr_data", ) evidence = { "result": ( 'DATA_RESULT\n{"items":[{"EMPLOYEE_CODE":"E9999",' '"LEAVE_YEAR":2031,"ANNUAL_ENTITLEMENT":17,' '"CARRYOVER_DAYS":2,"USED_DAYS":4,"PENDING_DAYS":1,' '"AVAILABLE_REMAINING_DAYS":14,' '"REQUEST_PERIOD_LABEL":"HISTORICAL_REQUEST_PERIOD",' '"APPLICATION_DECISION_STATUS":' '"NOT_APPLICABLE_FOR_HISTORICAL_PERIOD",' '"INCLUSIVE_CALENDAR_DAYS":11,' '"MONDAY_TO_FRIDAY_DAYS":7}]}' ) } report = evidence_contract_report(contracts, evidence) assert report[0]["satisfied"] is True def test_guidance_is_appended_from_json_contract(): guided = append_query_contract_guidance( "직원 E9999의 휴가 잔여를 조회해줘", original_question="직원 E9999의 휴가 잔여를 조회해줘", tool_name="search_hr_data", ) assert '"id":"leave_balance_eligibility"' in guided assert "AVAILABLE_REMAINING_DAYS" in guided class QueryContractTest(unittest.TestCase): test_leave_eligibility_question_selects_external_contract = staticmethod( test_leave_eligibility_question_selects_external_contract ) test_unrelated_tool_does_not_receive_leave_contract = staticmethod( test_unrelated_tool_does_not_receive_leave_contract ) test_incomplete_result_cannot_be_interpreted_as_zero_balance = staticmethod( test_incomplete_result_cannot_be_interpreted_as_zero_balance ) test_complete_database_row_satisfies_contract_without_fixture_values = ( staticmethod(test_complete_database_row_satisfies_contract_without_fixture_values) ) test_conflicting_computed_value_fails_contract = staticmethod( test_conflicting_computed_value_fails_contract ) test_historical_period_rejects_yes_no_decision_and_missing_counts = ( staticmethod(test_historical_period_rejects_yes_no_decision_and_missing_counts) ) test_historical_period_contract_accepts_non_decision_status = staticmethod( test_historical_period_contract_accepts_non_decision_status ) test_guidance_is_appended_from_json_contract = staticmethod( test_guidance_is_appended_from_json_contract )