Coverage for structured_tutorials / models / tests.py: 100%
25 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-04 13:17 +0200
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-04 13:17 +0200
1# Copyright (c) 2025 Mathias Ertl
2# Licensed under the MIT License. See LICENSE file for details.
4"""Test specifications for commands."""
6import re
7from typing import Annotated, Literal
9from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, model_validator
11from structured_tutorials.models.base import CommandBaseModel, CommandType, TestSpecificationMixin
12from structured_tutorials.models.validators import validate_regex
13from structured_tutorials.typing import COUNT_TYPE, Self
16class TestCommandModel(TestSpecificationMixin, CommandBaseModel):
17 """Test a command by running another command."""
19 model_config = ConfigDict(extra="forbid")
21 command: CommandType = Field(description="The command to run.")
24class TestPortModel(TestSpecificationMixin, BaseModel):
25 """Test a command by checking if a port is open."""
27 model_config = ConfigDict(extra="forbid")
29 host: str = Field(description="The host to connect to.")
30 port: Annotated[int, Field(ge=0, le=65535)] = Field(description="The port to connect to.")
33class TestOutputModel(BaseModel):
34 """Test a command by checking the output of a command."""
36 model_config = ConfigDict(extra="forbid")
38 stream: Literal["stdout", "stderr"] = Field(default="stdout", description="The output stream to use.")
39 regex: Annotated[re.Pattern[bytes], BeforeValidator(validate_regex)] | None = Field(
40 default=None, description="A regular expression to test."
41 )
42 line_count: COUNT_TYPE = Field(default=None, description="Test for the given line count.")
43 character_count: COUNT_TYPE = Field(default=None, description="Test for the given character count.")
44 strip: bool = Field(default=True, description="Whether to strip the output before testing.")
46 @model_validator(mode="after")
47 def validate_tests(self) -> Self:
48 if not self.regex and not self.line_count and not self.character_count:
49 raise ValueError("At least one test must be specified.")
50 return self