Coverage for structured_tutorials / models / tests.py: 100%

24 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-23 20:24 +0100

1# Copyright (c) 2025 Mathias Ertl 

2# Licensed under the MIT License. See LICENSE file for details. 

3 

4"""Test specifications for commands.""" 

5 

6import re 

7from typing import Annotated, Literal 

8 

9from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, model_validator 

10 

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 

14 

15 

16class TestCommandModel(TestSpecificationMixin, CommandBaseModel): 

17 """Test a command by running another command.""" 

18 

19 model_config = ConfigDict(extra="forbid") 

20 

21 command: CommandType = Field(description="The command to run.") 

22 

23 

24class TestPortModel(TestSpecificationMixin, BaseModel): 

25 """Test a command by checking if a port is open.""" 

26 

27 model_config = ConfigDict(extra="forbid") 

28 

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.") 

31 

32 

33class TestOutputModel(BaseModel): 

34 """Test a command by checking the output of a command.""" 

35 

36 model_config = ConfigDict(extra="forbid") 

37 

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 

45 @model_validator(mode="after") 

46 def validate_tests(self) -> Self: 

47 if not self.regex and not self.line_count and not self.character_count: 

48 raise ValueError("At least one test must be specified.") 

49 return self