import pytest from apps.skills.models import SkillDefinition, SkillVersion from apps.skills.services import ( SkillValidationError, render_prompt, validate_skill_version, ) VALID_INPUT_SCHEMA = { "type": "object", "properties": {"problem": {"type": "string"}}, "required": ["problem"], } VALID_OUTPUT_SCHEMA = { "type": "object", "properties": {"summary": {"type": "string"}}, "required": ["summary"], "additionalProperties": False, } def make_version(**overrides): definition = SkillDefinition.objects.create(code="summarize", name="问题摘要") values = { "definition": definition, "version": 1, "prompt_template": "请总结:{{ input.problem }}", "input_schema": VALID_INPUT_SCHEMA, "output_schema": VALID_OUTPUT_SCHEMA, "example_input": {"problem": "无法登录"}, } values.update(overrides) return SkillVersion.objects.create(**values) def test_validates_and_renders_restricted_template(db): version = make_version() validate_skill_version(version) assert render_prompt(version.prompt_template, {"problem": "无法登录"}) == "请总结:无法登录" def test_rejects_expression_or_missing_variable(db): version = make_version(prompt_template="{{ steps.previous.output }}") with pytest.raises(SkillValidationError, match="不允许的模板变量"): validate_skill_version(version) with pytest.raises(SkillValidationError, match="模板变量不存在"): render_prompt("{{ input.environment }}", {"problem": "无法登录"}) def test_rejects_example_that_does_not_match_schema(db): version = make_version(example_input={"problem": 123}) with pytest.raises(SkillValidationError, match="示例输入不符合"): validate_skill_version(version) @pytest.mark.parametrize( "output_schema", [ {"type": "array", "items": {"type": "string"}}, { "type": "object", "properties": {"summary": {"type": "string"}}, "required": ["summary"], }, { "type": "object", "properties": {"summary": {"type": "string"}}, "required": [], "additionalProperties": False, }, ], ) def test_rejects_incomplete_output_contracts_for_every_enforcement(db, output_schema): version = make_version( output_schema=output_schema, output_enforcement="validated_json", ) with pytest.raises(SkillValidationError): validate_skill_version(version) def test_native_enforcement_rejects_unsupported_schema_keyword_at_publish(db): output_schema = { "type": "object", "properties": { "missing_fields": { "type": "array", "items": {"type": "string"}, "uniqueItems": True, } }, "required": ["missing_fields"], "additionalProperties": False, } version = make_version( output_schema=output_schema, output_enforcement="native_json_schema", ) with pytest.raises( SkillValidationError, match=r"\$\.properties\.missing_fields\.uniqueItems", ): validate_skill_version(version) def test_validated_json_keeps_business_only_schema_constraints(db): output_schema = { "type": "object", "properties": { "values": { "type": "array", "items": {"type": "string"}, "uniqueItems": True, } }, "required": ["values"], "additionalProperties": False, } version = make_version( output_schema=output_schema, output_enforcement="validated_json", ) validate_skill_version(version)