v0-json-schema
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""Pydantic models for application configuration."""
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Dict, Literal
|
||||
|
||||
# These models were previously in config/manager.py, moved here for better organization.
|
||||
|
||||
class APICallerConfig(BaseModel):
|
||||
default_timeout: int = 30
|
||||
default_headers: Optional[Dict[str, str]] = None
|
||||
# Add other API caller specific configs: e.g., retry_attempts, backoff_factor
|
||||
|
||||
class JSONSchemaValidatorConfig(BaseModel):
|
||||
default_draft_version: Optional[str] = None # e.g., "draft7", "draft2020-12"
|
||||
# Add other schema validator specific configs
|
||||
|
||||
class RuleStorageConfig(BaseModel):
|
||||
type: Literal["filesystem", "database", "in_memory"] = "filesystem"
|
||||
path: Optional[str] = "./rules" # For filesystem adapter: path to rules directory
|
||||
connection_string: Optional[str] = None # For database adapter
|
||||
# Add other storage specific configs, e.g., for filesystem: file_pattern = "*.json"
|
||||
|
||||
class RuleRepositoryConfig(BaseModel):
|
||||
storage: RuleStorageConfig = RuleStorageConfig()
|
||||
default_version_strategy: Literal["latest_enabled", "latest_stable", "exact"] = "latest_enabled"
|
||||
preload_rules: bool = False
|
||||
# Add other rule repository specific configs
|
||||
|
||||
class LoggingConfig(BaseModel):
|
||||
level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"
|
||||
format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
# file_path: Optional[str] = None # Optional: if logging to a file
|
||||
|
||||
class TestExecutorConfig(BaseModel):
|
||||
# Add configurations specific to the Test Executor
|
||||
# For example, default behavior for failing a test step, etc.
|
||||
stop_on_first_failure: bool = False
|
||||
|
||||
class ReportGeneratorConfig(BaseModel):
|
||||
# Add configurations specific to the Report Generator
|
||||
output_format: Literal["json", "html", "xml"] = "json"
|
||||
output_path: str = "./reports"
|
||||
|
||||
class AppConfig(BaseModel):
|
||||
app_name: str = "DDMS Compliance Suite"
|
||||
logging: LoggingConfig = LoggingConfig()
|
||||
api_caller: APICallerConfig = APICallerConfig()
|
||||
json_schema_validator: JSONSchemaValidatorConfig = JSONSchemaValidatorConfig()
|
||||
rule_repository: RuleRepositoryConfig = RuleRepositoryConfig()
|
||||
test_executor: TestExecutorConfig = TestExecutorConfig()
|
||||
report_generator: ReportGeneratorConfig = ReportGeneratorConfig()
|
||||
# input_parser: ... # Config for input parser if needed
|
||||
# assertion_engine: ... # Config for assertion engine if needed
|
||||
@@ -0,0 +1,190 @@
|
||||
"""Base Pydantic models for the application."""
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List, Dict, Any, Union, Literal
|
||||
from enum import Enum
|
||||
|
||||
class SeverityLevel(str, Enum):
|
||||
ERROR = "error"
|
||||
WARNING = "warning"
|
||||
INFO = "info"
|
||||
|
||||
class RuleCategory(str, Enum):
|
||||
JSON_SCHEMA = "JSONSchema"
|
||||
API_LINTING = "APILinting"
|
||||
BUSINESS_LOGIC = "BusinessLogic"
|
||||
DATA_QUALITY = "DataQuality"
|
||||
SECURITY = "Security"
|
||||
PERFORMANCE = "Performance"
|
||||
MAPPING = "Mapping"
|
||||
PYTHON_CODE = "PythonCode" # Python代码规则类别
|
||||
API_DESIGN = "APIDesign" # API设计规则类别
|
||||
ERROR_HANDLING = "ErrorHandling" # 错误处理规则类别
|
||||
COMPATIBILITY = "Compatibility" # 兼容性规则类别
|
||||
GENERIC = "Generic"
|
||||
|
||||
class TargetType(str, Enum):
|
||||
API_REQUEST = "APIRequest"
|
||||
API_RESPONSE = "APIResponse"
|
||||
DATA_OBJECT = "DataObject"
|
||||
OPENAPI_SPECIFICATION = "OpenAPISpecification"
|
||||
GENERIC_TARGET = "GenericTarget"
|
||||
|
||||
class RuleLifecycle(str, Enum):
|
||||
"""规则生命周期枚举,定义规则在测试流程中的适用阶段"""
|
||||
REQUEST_PREPARATION = "请求准备阶段"
|
||||
RESPONSE_VALIDATION = "响应验证阶段"
|
||||
POST_VALIDATION = "后处理阶段"
|
||||
API_SPECIFICATION_VALIDATION = "API规范验证阶段" # 新增的生命周期
|
||||
ANY_STAGE = "任意阶段"
|
||||
|
||||
class RuleScope(str, Enum):
|
||||
"""规则作用域枚举,定义规则针对的具体对象"""
|
||||
REQUEST_URL = "请求URL"
|
||||
REQUEST_HEADERS = "请求头"
|
||||
REQUEST_PARAMS = "请求参数"
|
||||
REQUEST_BODY = "请求体"
|
||||
RESPONSE_STATUS = "响应状态码"
|
||||
RESPONSE_HEADERS = "响应头"
|
||||
RESPONSE_BODY = "响应体"
|
||||
RESPONSE_TIME = "响应时间"
|
||||
SECURITY = "安全性"
|
||||
PERFORMANCE = "性能"
|
||||
ANY_SCOPE = "任意作用域"
|
||||
|
||||
class BaseRule(BaseModel):
|
||||
"""通用规则属性 (Pydantic基类 BaseRule)"""
|
||||
id: str # 规则唯一ID
|
||||
name: str # 规则名称
|
||||
description: Optional[str] = None # 规则详细描述
|
||||
category: RuleCategory # 规则类别
|
||||
version: str = "1.0.0" # 版本号
|
||||
severity: SeverityLevel = SeverityLevel.INFO # 严重性
|
||||
source: Optional[str] = None # 规则来源 (例如 "PlatformStandard-XYZ", "OWASP-ASVS")
|
||||
is_enabled: bool = True # 是否启用
|
||||
tags: Optional[List[str]] = None # 规则标签 (如 "critical", "wellbore")
|
||||
target_type: Optional[TargetType] = None # 规则适用目标类型 (描述性元数据)
|
||||
target_identifier: Optional[str] = None # 规则适用目标的具体标识 (描述性元数据)
|
||||
author: Optional[str] = None # 规则作者
|
||||
# 新增字段
|
||||
lifecycle: Optional[RuleLifecycle] = Field(
|
||||
default=RuleLifecycle.ANY_STAGE,
|
||||
description="规则的推荐适用阶段或类型。主要用于识别 API_SPECIFICATION_VALIDATION (静态分析) 规则。"
|
||||
"对于其他动态规则,此字段为元数据提示,实际执行阶段由 TestStep 定义。"
|
||||
)
|
||||
scope: Optional[RuleScope] = Field(
|
||||
default=RuleScope.ANY_SCOPE,
|
||||
description="描述规则逻辑主要关注的API交互部分 (例如,请求头、响应体)。"
|
||||
"此字段为描述性元数据,用于规则分类和理解,不作为执行时的强制约束。"
|
||||
)
|
||||
code: Optional[str] = None # 规则验证代码
|
||||
|
||||
class JSONSchemaDefinition(BaseRule):
|
||||
"""JSON Schema 定义 (用于API请求/响应体结构验证)"""
|
||||
category: Literal[RuleCategory.JSON_SCHEMA] = RuleCategory.JSON_SCHEMA
|
||||
schema_content: Dict[str, Any] # 实际的JSON Schema
|
||||
|
||||
class APILintingRuleset(BaseRule):
|
||||
"""API 设计规范/Linting规则 (例如,基于Spectral的OpenAPI规范校验规则集)"""
|
||||
category: Literal[RuleCategory.API_LINTING] = RuleCategory.API_LINTING
|
||||
ruleset_format: str = "spectral" # 例如 "spectral", "custom_json_rules"
|
||||
ruleset_content: Union[str, Dict[str, Any]] # 规则集内容或其引用(例如,文件路径或URL,或直接嵌入的规则字典)
|
||||
|
||||
class BusinessAssertionTemplate(BaseRule):
|
||||
"""业务逻辑断言模板或具体规则"""
|
||||
category: Literal[RuleCategory.BUSINESS_LOGIC] = RuleCategory.BUSINESS_LOGIC
|
||||
template_language: str = "python_expression" # 如 "python_expression", "jsonpath_assert"
|
||||
template_expression: str
|
||||
expected_parameters: Optional[List[str]] = None # 执行断言模板时需要输入的参数列表
|
||||
|
||||
class DataQualityRule(BaseRule):
|
||||
"""数据质量校验规则"""
|
||||
category: Literal[RuleCategory.DATA_QUALITY] = RuleCategory.DATA_QUALITY
|
||||
# Specific fields for data quality rules, e.g.:
|
||||
field_name: Optional[str] = None
|
||||
validation_type: str # e.g., "regex", "range", "custom_function"
|
||||
validation_expression: str # regex pattern, range boundaries, function name/path
|
||||
|
||||
class PythonCodeRule(BaseRule):
|
||||
"""使用 Python 代码定义的规则
|
||||
|
||||
此类规则允许以 Python 代码片段形式定义复杂的验证逻辑,提供了最大的灵活性。
|
||||
代码将在受控环境中执行,以确保安全性。
|
||||
"""
|
||||
category: Literal[RuleCategory.PYTHON_CODE] = RuleCategory.PYTHON_CODE
|
||||
|
||||
# Python 代码内容或文件路径
|
||||
code_file: Optional[str] = None
|
||||
|
||||
# 脚本入口函数名,默认为 validate
|
||||
entry_function: str = "validate"
|
||||
|
||||
# 预期的入口函数参数
|
||||
expected_parameters: Optional[List[str]] = None
|
||||
|
||||
# 是否允许导入外部模块 (出于安全考虑,默认为 False)
|
||||
allow_imports: bool = False
|
||||
|
||||
# 允许导入的模块列表 (如果 allow_imports 为 True)
|
||||
allowed_modules: Optional[List[str]] = None
|
||||
|
||||
# 超时设置(秒),防止无限循环等恶意代码
|
||||
timeout: int = 5
|
||||
|
||||
# 代码依赖的其他规则ID
|
||||
depends_on: Optional[List[str]] = None
|
||||
|
||||
# 新增规则类型
|
||||
class PerformanceRule(BaseRule):
|
||||
"""性能相关规则,如响应时间、吞吐量等"""
|
||||
category: Literal[RuleCategory.PERFORMANCE] = RuleCategory.PERFORMANCE
|
||||
lifecycle: Literal[RuleLifecycle.RESPONSE_VALIDATION] = RuleLifecycle.RESPONSE_VALIDATION
|
||||
scope: Literal[RuleScope.RESPONSE_TIME] = RuleScope.RESPONSE_TIME
|
||||
threshold: Union[float, int] # 性能阈值
|
||||
metric: str # 性能指标名称
|
||||
unit: str = "ms" # 默认单位:毫秒
|
||||
|
||||
class SecurityRule(BaseRule):
|
||||
"""安全相关规则,如身份验证、授权、加密等"""
|
||||
category: Literal[RuleCategory.SECURITY] = RuleCategory.SECURITY
|
||||
check_type: str # 安全检查类型(如认证、授权、加密等)
|
||||
expected_value: Optional[str] = None # 预期值
|
||||
|
||||
class RESTfulDesignRule(BaseRule):
|
||||
"""RESTful API设计规则,如HTTP方法使用、URL设计等"""
|
||||
category: Literal[RuleCategory.API_DESIGN] = RuleCategory.API_DESIGN
|
||||
design_aspect: str # 设计方面(如URL、HTTP方法、参数等)
|
||||
pattern: Optional[str] = None # 匹配模式(如正则表达式)
|
||||
|
||||
class ErrorHandlingRule(BaseRule):
|
||||
"""错误处理规则,如错误码、错误消息等"""
|
||||
category: Literal[RuleCategory.ERROR_HANDLING] = RuleCategory.ERROR_HANDLING
|
||||
error_code: str # 错误码
|
||||
expected_status: int # 预期HTTP状态码
|
||||
expected_message: Optional[str] = None # 预期错误消息
|
||||
|
||||
# 更新联合类型以包含新规则类型
|
||||
AnyRule = Union[
|
||||
JSONSchemaDefinition,
|
||||
APILintingRuleset,
|
||||
BusinessAssertionTemplate,
|
||||
DataQualityRule,
|
||||
PythonCodeRule,
|
||||
PerformanceRule,
|
||||
SecurityRule,
|
||||
RESTfulDesignRule,
|
||||
ErrorHandlingRule,
|
||||
BaseRule # 通用或未明确分类的规则
|
||||
]
|
||||
|
||||
class RuleQuery(BaseModel):
|
||||
"""规则查询条件对象 (Pydantic模型)"""
|
||||
rule_id: Optional[str] = None
|
||||
category: Optional[RuleCategory] = None
|
||||
target_type: Optional[TargetType] = None
|
||||
target_identifier: Optional[str] = None
|
||||
version: Optional[str] = "latest" # 或特殊标识 ("latest", "stable", or specific version)
|
||||
tags: Optional[List[str]] = None
|
||||
is_enabled: Optional[bool] = True # 默认查询已启用的规则
|
||||
# 新增字段
|
||||
lifecycle: Optional[RuleLifecycle] = None
|
||||
scope: Optional[RuleScope] = None
|
||||
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
Pydantic models for Test Cases, Test Steps, and Test Suites.
|
||||
"""
|
||||
from typing import Optional, List, Dict, Any, Union, Literal
|
||||
from pydantic import BaseModel, Field, HttpUrl
|
||||
from enum import Enum
|
||||
|
||||
from .rule_models import SeverityLevel # Assuming rule_models.py is in the same directory
|
||||
from ..api_caller.caller import APIRequest # Adjust path as necessary
|
||||
|
||||
class TestDataGenerationStrategy(str, Enum):
|
||||
"""Defines how test data for an API request should be generated."""
|
||||
STATIC = "static" # Data is explicitly provided
|
||||
FROM_SCHEMA = "from_schema" # Data is generated based on OpenAPI/JSON schema
|
||||
FROM_PREVIOUS_STEP = "from_previous_step" # Data is extracted from a previous test step's output
|
||||
AI_GENERATED = "ai_generated" # Data is generated by an AI model
|
||||
|
||||
class TestDataGenerationConfig(BaseModel):
|
||||
"""Configuration for generating test data for an API request."""
|
||||
strategy: TestDataGenerationStrategy = TestDataGenerationStrategy.STATIC
|
||||
# For STATIC strategy:
|
||||
static_data: Optional[Dict[str, Any]] = Field(default=None, description="Explicit data for the request (e.g., body, params).")
|
||||
# For FROM_SCHEMA strategy:
|
||||
schema_source: Optional[str] = Field(default=None, description="Identifier for the schema to be used (e.g., operationId, or path to schema file).")
|
||||
# For FROM_PREVIOUS_STEP strategy:
|
||||
source_step_id: Optional[str] = Field(default=None, description="The ID of the previous TestStep whose output will be used.")
|
||||
extraction_rules: Optional[Dict[str, str]] = Field(default_None, description="Rules to extract data (e.g., JSONPath expressions like {'user_id': '$.body.id'}).")
|
||||
# For AI_GENERATED strategy:
|
||||
ai_prompt: Optional[str] = Field(default=None, description="Prompt to be used by an AI model to generate data.")
|
||||
ai_model_config: Optional[Dict[str, Any]] = Field(default_None, description="Configuration for the AI model (e.g., temperature, max_tokens).")
|
||||
|
||||
class APIRequestContext(BaseModel):
|
||||
"""
|
||||
Defines how to construct the APIRequest for a TestStep, potentially using
|
||||
data from previous steps or other dynamic sources.
|
||||
"""
|
||||
method: str = Field(description="HTTP method (GET, POST, PUT, DELETE, etc.).")
|
||||
url_template: str = Field(description="URL template, can contain placeholders like {base_url} or {resource_id}.")
|
||||
path_params_source: Optional[Dict[str, TestDataGenerationConfig]] = Field(default_None, description="How to generate/get path parameters.")
|
||||
query_params_source: Optional[Dict[str, TestDataGenerationConfig]] = Field(default_None, description="How to generate/get query parameters.")
|
||||
headers_source: Optional[Dict[str, TestDataGenerationConfig]] = Field(default_None, description="How to generate/get request headers. Default headers can be added by the orchestrator.")
|
||||
body_source: Optional[TestDataGenerationConfig] = Field(default_None, description="How to generate/get the request body.")
|
||||
|
||||
# Stores the actual APIRequest object after context resolution
|
||||
# This field will be populated by the TestOrchestrator before execution.
|
||||
resolved_request: Optional[APIRequest] = Field(default=None, exclude=True)
|
||||
|
||||
|
||||
class TestStep(BaseModel):
|
||||
"""
|
||||
Represents a single step within a TestCase, typically involving one API call
|
||||
and a set of rules to be validated against its request/response.
|
||||
"""
|
||||
step_id: str = Field(description="Unique identifier for this test step within the TestCase.")
|
||||
description: Optional[str] = Field(default=None, description="Description of what this test step does.")
|
||||
api_request_context: APIRequestContext = Field(description="Context to build the API request for this step.")
|
||||
|
||||
# Rules to be applied at different phases of this step
|
||||
# Applied after the request is prepared but before it's sent.
|
||||
# Useful for validating the constructed request itself.
|
||||
request_preparation_rule_ids: List[str] = Field(default_factory=list, description="Rules to apply to the APIRequest before sending.")
|
||||
|
||||
# Applied after the API response is received. This is the most common place for validation rules.
|
||||
response_validation_rule_ids: List[str] = Field(default_factory=list, description="Rules to apply to the APIResponse.")
|
||||
|
||||
# Applied after response validation, يمكن استخدامه لتنظيف البيانات أو إجراءات ما بعد الاختبار
|
||||
post_validation_rule_ids: List[str] = Field(default_factory=list, description="Rules to apply after response validation (e.g., cleanup).")
|
||||
|
||||
# Configuration for expected outcomes, more specific than just rule pass/fail
|
||||
expected_status_code: Optional[int] = Field(default=None, description="Expected HTTP status code.")
|
||||
# Allows defining assertions on the response body using something like JSONPath
|
||||
# e.g., {"$.data.status": "completed", "$.errors": null}
|
||||
response_body_assertions: Optional[Dict[str, Any]] = Field(default_None, description="Assertions to make on the response body (e.g., using JSONPath).")
|
||||
|
||||
# Execution control
|
||||
skip: bool = Field(default=False, description="If True, this test step will be skipped.")
|
||||
# Allows storing output from this step to be used by subsequent steps
|
||||
# e.g., {"created_user_id": "$.response.body.id"}
|
||||
outputs_to_extract: Optional[Dict[str, str]] = Field(default_None, description="JSONPath expressions to extract data from response to be used in later steps.")
|
||||
|
||||
|
||||
class TestCaseExecutionMode(str, Enum):
|
||||
SEQUENTIAL = "sequential" # Steps are executed one after another
|
||||
PARALLEL = "parallel" # Steps (if independent) can be executed in parallel
|
||||
|
||||
class TestCase(BaseModel):
|
||||
"""
|
||||
Defines a test case, which consists of one or more test steps,
|
||||
metadata, and configurations for execution.
|
||||
"""
|
||||
id: str = Field(description="Unique identifier for the test case.")
|
||||
name: str = Field(description="Name of the test case.")
|
||||
description: Optional[str] = Field(default=None, description="Detailed description of the test case.")
|
||||
tags: List[str] = Field(default_factory=list, description="Tags for categorizing and filtering test cases.")
|
||||
severity: SeverityLevel = Field(default=SeverityLevel.INFO, description="Severity of the test case if it fails.")
|
||||
author: Optional[str] = Field(default=None, description="Author of the test case.")
|
||||
creation_date: Optional[str] = Field(default=None, description="Date when the test case was created (ISO format).") # Consider using datetime
|
||||
version: str = Field(default="1.0.0", description="Version of the test case.")
|
||||
|
||||
# Test Steps
|
||||
steps: List[TestStep] = Field(description="A list of test steps to be executed for this test case.")
|
||||
execution_mode: TestCaseExecutionMode = Field(default=TestCaseExecutionMode.SEQUENTIAL, description="How the steps in this test case should be executed.")
|
||||
|
||||
# Data that can be shared across steps within this test case.
|
||||
# Steps can read from and write to this context.
|
||||
shared_context: Dict[str, Any] = Field(default_factory=dict, description="Data context televisão across steps.")
|
||||
|
||||
# Overall expected outcome for the test case (can be high-level)
|
||||
expected_overall_result: Optional[str] = Field(default=None, description="A high-level description of the expected outcome for the entire test case.")
|
||||
|
||||
# For iterative/load testing
|
||||
execution_count: int = Field(default=1, ge=1, description="Number of times this test case should be executed.")
|
||||
# If > 1, defines delay between iterations in seconds.
|
||||
delay_between_iterations_sec: float = Field(default=0, ge=0)
|
||||
|
||||
|
||||
class TestSuite(BaseModel):
|
||||
"""
|
||||
A collection of TestCases, possibly with a shared configuration or setup/teardown logic.
|
||||
"""
|
||||
id: str = Field(description="Unique identifier for the test suite.")
|
||||
name: str = Field(description="Name of the test suite.")
|
||||
description: Optional[str] = Field(default=None, description="Description of the test suite.")
|
||||
tags: List[str] = Field(default_factory=list, description="Tags for categorizing and filtering test suites.")
|
||||
|
||||
# List of TestCase IDs or full TestCase objects. Using IDs allows for referencing.
|
||||
# For simplicity in this initial design, let's assume full TestCase objects.
|
||||
# Later, we could support referencing TestCase definitions stored elsewhere.
|
||||
test_cases: List[TestCase] = Field(default_factory=list, description="List of test cases included in this suite.")
|
||||
|
||||
# Global parameters or configurations that can apply to all TestCases in the suite
|
||||
# These could override individual TestCase settings or provide defaults.
|
||||
global_parameters: Optional[Dict[str, Any]] = Field(default_None, description="Parameters applicable to all test cases in the suite.")
|
||||
|
||||
# Setup: API calls or actions to perform before running any TestCase in the suite.
|
||||
# Similar structure to TestStep but without specific rule validations, more for setup.
|
||||
setup_steps: Optional[List[TestStep]] = Field(default_None, description="Steps to execute before running test cases in the suite.")
|
||||
|
||||
# Teardown: API calls or actions to perform after all TestCases in the suite have run.
|
||||
teardown_steps: Optional[List[TestStep]] = Field(default_None, description="Steps to execute after running test cases in the suite.")
|
||||
|
||||
# Execution control for the suite
|
||||
# e.g., run all, run tagged, run failed from previous
|
||||
execution_strategy: Optional[str] = Field(default=None, description="Strategy for executing test cases within the suite.")
|
||||
Reference in New Issue
Block a user