|
0cb4a5e…
|
anonymous
|
1 |
"""Tests for boilerworks.manifest.""" |
|
0cb4a5e…
|
anonymous
|
2 |
|
|
0cb4a5e…
|
anonymous
|
3 |
from __future__ import annotations |
|
0cb4a5e…
|
anonymous
|
4 |
|
|
0cb4a5e…
|
anonymous
|
5 |
import pytest |
|
0cb4a5e…
|
anonymous
|
6 |
from pydantic import ValidationError |
|
0cb4a5e…
|
anonymous
|
7 |
|
|
0cb4a5e…
|
anonymous
|
8 |
from boilerworks.manifest import BoilerworksManifest, DataConfig, ServicesConfig |
|
0cb4a5e…
|
anonymous
|
9 |
from boilerworks.manifest import TestingConfig as TestingCfg |
|
0cb4a5e…
|
anonymous
|
10 |
|
|
0cb4a5e…
|
anonymous
|
11 |
|
|
0cb4a5e…
|
anonymous
|
12 |
def _make_manifest(**kwargs) -> BoilerworksManifest: |
|
0cb4a5e…
|
anonymous
|
13 |
defaults = { |
|
0cb4a5e…
|
anonymous
|
14 |
"project": "my-app", |
|
0cb4a5e…
|
anonymous
|
15 |
"family": "django-nextjs", |
|
0cb4a5e…
|
anonymous
|
16 |
"size": "full", |
|
0cb4a5e…
|
anonymous
|
17 |
} |
|
0cb4a5e…
|
anonymous
|
18 |
defaults.update(kwargs) |
|
0cb4a5e…
|
anonymous
|
19 |
return BoilerworksManifest(**defaults) |
|
0cb4a5e…
|
anonymous
|
20 |
|
|
0cb4a5e…
|
anonymous
|
21 |
|
|
0cb4a5e…
|
anonymous
|
22 |
class TestValidManifest: |
|
0cb4a5e…
|
anonymous
|
23 |
def test_minimal_manifest_passes(self) -> None: |
|
0cb4a5e…
|
anonymous
|
24 |
m = _make_manifest() |
|
0cb4a5e…
|
anonymous
|
25 |
assert m.project == "my-app" |
|
0cb4a5e…
|
anonymous
|
26 |
assert m.family == "django-nextjs" |
|
0cb4a5e…
|
anonymous
|
27 |
assert m.size == "full" |
|
0cb4a5e…
|
anonymous
|
28 |
|
|
0cb4a5e…
|
anonymous
|
29 |
def test_defaults_are_correct(self) -> None: |
|
0cb4a5e…
|
anonymous
|
30 |
m = _make_manifest() |
|
0cb4a5e…
|
anonymous
|
31 |
assert m.topology == "standard" |
|
0cb4a5e…
|
anonymous
|
32 |
assert m.cloud is None |
|
0cb4a5e…
|
anonymous
|
33 |
assert m.region is None |
|
0cb4a5e…
|
anonymous
|
34 |
assert m.domain is None |
|
0cb4a5e…
|
anonymous
|
35 |
assert m.mobile is False |
|
0cb4a5e…
|
anonymous
|
36 |
assert m.web_presence is False |
|
0cb4a5e…
|
anonymous
|
37 |
assert m.compliance == [] |
|
0cb4a5e…
|
anonymous
|
38 |
assert m.template_versions == {} |
|
0cb4a5e…
|
anonymous
|
39 |
|
|
0cb4a5e…
|
anonymous
|
40 |
def test_full_manifest_passes(self, valid_manifest: BoilerworksManifest) -> None: |
|
0cb4a5e…
|
anonymous
|
41 |
assert valid_manifest.project == "my-app" |
|
0cb4a5e…
|
anonymous
|
42 |
assert valid_manifest.cloud == "aws" |
|
0cb4a5e…
|
anonymous
|
43 |
assert valid_manifest.region == "us-east-1" |
|
0cb4a5e…
|
anonymous
|
44 |
|
|
0cb4a5e…
|
anonymous
|
45 |
def test_micro_template(self) -> None: |
|
0cb4a5e…
|
anonymous
|
46 |
m = _make_manifest(family="fastapi-micro", size="micro") |
|
0cb4a5e…
|
anonymous
|
47 |
assert m.size == "micro" |
|
0cb4a5e…
|
anonymous
|
48 |
|
|
0cb4a5e…
|
anonymous
|
49 |
def test_edge_template(self) -> None: |
|
0cb4a5e…
|
anonymous
|
50 |
m = _make_manifest(family="astro-site", size="edge") |
|
0cb4a5e…
|
anonymous
|
51 |
assert m.size == "edge" |
|
0cb4a5e…
|
anonymous
|
52 |
|
|
0cb4a5e…
|
anonymous
|
53 |
|
|
0cb4a5e…
|
anonymous
|
54 |
class TestProjectSlugValidation: |
|
0cb4a5e…
|
anonymous
|
55 |
def test_valid_slugs(self) -> None: |
|
0cb4a5e…
|
anonymous
|
56 |
for slug in ["my-app", "app", "my-app-v2", "a1b2c3"]: |
|
0cb4a5e…
|
anonymous
|
57 |
m = _make_manifest(project=slug) |
|
0cb4a5e…
|
anonymous
|
58 |
assert m.project == slug |
|
0cb4a5e…
|
anonymous
|
59 |
|
|
0cb4a5e…
|
anonymous
|
60 |
def test_spaces_fail(self) -> None: |
|
0cb4a5e…
|
anonymous
|
61 |
with pytest.raises(ValidationError, match="project name"): |
|
0cb4a5e…
|
anonymous
|
62 |
_make_manifest(project="my app") |
|
0cb4a5e…
|
anonymous
|
63 |
|
|
0cb4a5e…
|
anonymous
|
64 |
def test_uppercase_fails(self) -> None: |
|
0cb4a5e…
|
anonymous
|
65 |
with pytest.raises(ValidationError, match="project name"): |
|
0cb4a5e…
|
anonymous
|
66 |
_make_manifest(project="MyApp") |
|
0cb4a5e…
|
anonymous
|
67 |
|
|
0cb4a5e…
|
anonymous
|
68 |
def test_leading_digit_fails(self) -> None: |
|
0cb4a5e…
|
anonymous
|
69 |
with pytest.raises(ValidationError, match="project name"): |
|
0cb4a5e…
|
anonymous
|
70 |
_make_manifest(project="1app") |
|
0cb4a5e…
|
anonymous
|
71 |
|
|
0cb4a5e…
|
anonymous
|
72 |
def test_underscore_fails(self) -> None: |
|
0cb4a5e…
|
anonymous
|
73 |
with pytest.raises(ValidationError, match="project name"): |
|
0cb4a5e…
|
anonymous
|
74 |
_make_manifest(project="my_app") |
|
0cb4a5e…
|
anonymous
|
75 |
|
|
0cb4a5e…
|
anonymous
|
76 |
def test_empty_fails(self) -> None: |
|
0cb4a5e…
|
anonymous
|
77 |
with pytest.raises(ValidationError): |
|
0cb4a5e…
|
anonymous
|
78 |
_make_manifest(project="") |
|
0cb4a5e…
|
anonymous
|
79 |
|
|
0cb4a5e…
|
anonymous
|
80 |
|
|
0cb4a5e…
|
anonymous
|
81 |
class TestFamilyValidation: |
|
0cb4a5e…
|
anonymous
|
82 |
def test_unknown_family_fails(self) -> None: |
|
0cb4a5e…
|
anonymous
|
83 |
with pytest.raises(ValidationError, match="unknown template family"): |
|
0cb4a5e…
|
anonymous
|
84 |
_make_manifest(family="not-a-real-template") |
|
0cb4a5e…
|
anonymous
|
85 |
|
|
0cb4a5e…
|
anonymous
|
86 |
def test_all_known_families_pass(self) -> None: |
|
0cb4a5e…
|
anonymous
|
87 |
from boilerworks.registry import Registry |
|
0cb4a5e…
|
anonymous
|
88 |
|
|
0cb4a5e…
|
anonymous
|
89 |
registry = Registry() |
|
0cb4a5e…
|
anonymous
|
90 |
for t in registry.list_all(): |
|
0cb4a5e…
|
anonymous
|
91 |
m = _make_manifest(family=t.name, size=t.size) |
|
0cb4a5e…
|
anonymous
|
92 |
assert m.family == t.name |
|
0cb4a5e…
|
anonymous
|
93 |
|
|
0cb4a5e…
|
anonymous
|
94 |
|
|
0cb4a5e…
|
anonymous
|
95 |
class TestYamlRoundtrip: |
|
0cb4a5e…
|
anonymous
|
96 |
def test_to_yaml_and_back(self, valid_manifest: BoilerworksManifest) -> None: |
|
0cb4a5e…
|
anonymous
|
97 |
yaml_str = valid_manifest.to_yaml() |
|
0cb4a5e…
|
anonymous
|
98 |
assert "my-app" in yaml_str |
|
0cb4a5e…
|
anonymous
|
99 |
assert "django-nextjs" in yaml_str |
|
0cb4a5e…
|
anonymous
|
100 |
|
|
0cb4a5e…
|
anonymous
|
101 |
restored = BoilerworksManifest.from_yaml(yaml_str) |
|
0cb4a5e…
|
anonymous
|
102 |
assert restored.project == valid_manifest.project |
|
0cb4a5e…
|
anonymous
|
103 |
assert restored.family == valid_manifest.family |
|
0cb4a5e…
|
anonymous
|
104 |
assert restored.size == valid_manifest.size |
|
0cb4a5e…
|
anonymous
|
105 |
assert restored.cloud == valid_manifest.cloud |
|
0cb4a5e…
|
anonymous
|
106 |
assert restored.region == valid_manifest.region |
|
0cb4a5e…
|
anonymous
|
107 |
|
|
0cb4a5e…
|
anonymous
|
108 |
def test_to_yaml_is_valid_yaml(self, valid_manifest: BoilerworksManifest) -> None: |
|
0cb4a5e…
|
anonymous
|
109 |
import yaml |
|
0cb4a5e…
|
anonymous
|
110 |
|
|
0cb4a5e…
|
anonymous
|
111 |
yaml_str = valid_manifest.to_yaml() |
|
0cb4a5e…
|
anonymous
|
112 |
parsed = yaml.safe_load(yaml_str) |
|
0cb4a5e…
|
anonymous
|
113 |
assert isinstance(parsed, dict) |
|
0cb4a5e…
|
anonymous
|
114 |
assert parsed["project"] == "my-app" |
|
0cb4a5e…
|
anonymous
|
115 |
|
|
0cb4a5e…
|
anonymous
|
116 |
def test_from_file(self, valid_manifest: BoilerworksManifest, tmp_path) -> None: |
|
0cb4a5e…
|
anonymous
|
117 |
yaml_file = tmp_path / "boilerworks.yaml" |
|
0cb4a5e…
|
anonymous
|
118 |
valid_manifest.to_file(yaml_file) |
|
0cb4a5e…
|
anonymous
|
119 |
|
|
0cb4a5e…
|
anonymous
|
120 |
loaded = BoilerworksManifest.from_file(yaml_file) |
|
0cb4a5e…
|
anonymous
|
121 |
assert loaded.project == valid_manifest.project |
|
0cb4a5e…
|
anonymous
|
122 |
assert loaded.family == valid_manifest.family |
|
0cb4a5e…
|
anonymous
|
123 |
|
|
0cb4a5e…
|
anonymous
|
124 |
|
|
0cb4a5e…
|
anonymous
|
125 |
class TestNestedModels: |
|
0cb4a5e…
|
anonymous
|
126 |
def test_services_config_defaults(self) -> None: |
|
0cb4a5e…
|
anonymous
|
127 |
s = ServicesConfig() |
|
0cb4a5e…
|
anonymous
|
128 |
assert s.email is None |
|
0cb4a5e…
|
anonymous
|
129 |
assert s.cache == "redis" |
|
0cb4a5e…
|
anonymous
|
130 |
|
|
0cb4a5e…
|
anonymous
|
131 |
def test_data_config_defaults(self) -> None: |
|
0cb4a5e…
|
anonymous
|
132 |
d = DataConfig() |
|
0cb4a5e…
|
anonymous
|
133 |
assert d.database == "postgres" |
|
0cb4a5e…
|
anonymous
|
134 |
assert d.migrations is True |
|
0cb4a5e…
|
anonymous
|
135 |
assert d.seed_data is True |
|
0cb4a5e…
|
anonymous
|
136 |
|
|
0cb4a5e…
|
anonymous
|
137 |
def test_testing_config_defaults(self) -> None: |
|
0cb4a5e…
|
anonymous
|
138 |
t = TestingCfg() |
|
0cb4a5e…
|
anonymous
|
139 |
assert t.e2e is None |
|
0cb4a5e…
|
anonymous
|
140 |
assert t.unit is True |
|
0cb4a5e…
|
anonymous
|
141 |
assert t.integration is True |
|
0cb4a5e…
|
anonymous
|
142 |
|
|
0cb4a5e…
|
anonymous
|
143 |
def test_services_email_options(self) -> None: |
|
0cb4a5e…
|
anonymous
|
144 |
for provider in ("ses", "sendgrid", "mailgun"): |
|
0cb4a5e…
|
anonymous
|
145 |
s = ServicesConfig(email=provider) |
|
0cb4a5e…
|
anonymous
|
146 |
assert s.email == provider |