Coverage for tests/factories.py: 100%

36 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2026-08-22 19:08 +0000

1"""Polyfactory factories for the public data model. 

2 

3They keep property style tests honest: instead of hand writing a handful of settings objects we let 

4polyfactory build many of them, with the ranges narrowed to values that make sense for a parser. 

5""" 

6 

7import random 

8import typing 

9 

10from polyfactory.factories import dataclass_factory 

11 

12from meta_tags_parser import structs 

13 

14 

15FACTORY_RANDOM_SEED: typing.Final = 20260818 

16MAX_GENERATED_LIMIT_CHARS: typing.Final = 8192 

17MIN_GENERATED_LIMIT_CHARS: typing.Final = 16 

18SHARED_RANDOM_SOURCE: typing.Final = random.Random(FACTORY_RANDOM_SEED) 

19KNOWN_BOUNDARY_TAG_PAIRS: typing.Final[tuple[tuple[str, str], ...]] = ( 

20 ("</head>", "<body"), 

21 ("</HEAD>", "<BODY"), 

22 ("</head>", "<article"), 

23 ("</title>", "<body"), 

24) 

25 

26 

27@typing.final 

28class OneMetaTagFactory(dataclass_factory.DataclassFactory[structs.OneMetaTag]): 

29 """Build arbitrary meta tags with names that look like the ones sites really ship.""" 

30 

31 __model__ = structs.OneMetaTag 

32 __random__ = SHARED_RANDOM_SOURCE 

33 

34 @classmethod 

35 def name(cls) -> str: # noqa: COP009, COP007 

36 return cls.__random__.choice(("title", "description", "image", "image:width", "image:height", "url", "audio")) 

37 

38 

39@typing.final 

40class SettingsFromUserFactory(dataclass_factory.DataclassFactory[structs.SettingsFromUser]): 

41 """Build settings objects covering every combination of what_to_parse and slicing limits.""" 

42 

43 __model__ = structs.SettingsFromUser 

44 __random__ = SHARED_RANDOM_SOURCE 

45 

46 @classmethod 

47 def what_to_parse(cls) -> tuple[structs.WhatToParse, ...]: # noqa: COP009, COP007 

48 possible_parts: typing.Final[list[structs.WhatToParse]] = list(structs.WhatToParse) 

49 return tuple(cls.__random__.sample(possible_parts, k=cls.__random__.randint(1, len(possible_parts)))) 

50 

51 @classmethod 

52 def boundary_tags(cls) -> tuple[str, str]: # noqa: COP009, COP007 

53 return cls.__random__.choice(KNOWN_BOUNDARY_TAG_PAIRS) 

54 

55 @classmethod 

56 def fallback_limit_chars(cls) -> int: # noqa: COP009, COP007 

57 return cls.__random__.randint(MIN_GENERATED_LIMIT_CHARS, MAX_GENERATED_LIMIT_CHARS) 

58 

59 @classmethod 

60 def max_scan_chars(cls) -> int: # noqa: COP009, COP007 

61 return cls.__random__.randint(MIN_GENERATED_LIMIT_CHARS, MAX_GENERATED_LIMIT_CHARS) 

62 

63 @classmethod 

64 def hard_limit_chars(cls) -> int | None: # noqa: COP009, COP007 

65 return cls.__random__.choice( 

66 (None, cls.__random__.randint(MIN_GENERATED_LIMIT_CHARS, MAX_GENERATED_LIMIT_CHARS)) 

67 )