Skip to content

Commit 27379ae

Browse files
Merge pull request steam-bell-92#1694 from jyotish6699/feature/project-registry-validation-framework
feat: add registry validation framework for project registry
2 parents cc18b13 + 7b1e5ac commit 27379ae

5 files changed

Lines changed: 558 additions & 1 deletion

File tree

CONTRIBUTING.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,16 @@ git checkout -b fix/bug-description
4747

4848
### 3. Make Your Changes
4949

50-
- Write your code following our [Project Guidelines](#-project-guidelines)
50+
- Write your code following our Project Guidelines
51+
- Validate `projects_registry.json` before submitting your changes
5152
- Test your project thoroughly
5253
- Ensure it follows the repository style
5354

55+
Run the registry validator:
56+
57+
```bash
58+
python utils/registry_validator.py --strict
59+
5460
### 4. Commit Your Changes
5561

5662
```bash
@@ -466,6 +472,44 @@ To run a syntax check across all files (similar to our CI):
466472
find . -name "*.py" -exec python -m py_compile {} +
467473
```
468474
475+
## 📋 Registry Validation
476+
477+
If your contribution adds or modifies `projects_registry.json`, validate the registry before opening a pull request.
478+
479+
### Run validation
480+
481+
```bash
482+
python utils/registry_validator.py
483+
```
484+
485+
### Validate a custom registry
486+
487+
```bash
488+
python utils/registry_validator.py --file projects_registry.json
489+
```
490+
491+
### JSON output
492+
493+
```bash
494+
python utils/registry_validator.py --json
495+
```
496+
497+
### Strict mode
498+
499+
```bash
500+
python utils/registry_validator.py --strict
501+
```
502+
503+
The validator checks:
504+
505+
- Valid JSON format
506+
- Required project fields
507+
- Valid categories
508+
- Valid difficulty levels
509+
- Duplicate project names
510+
- Duplicate project paths
511+
- Missing project files
512+
469513
---
470514
471515
## 🤔 Questions?

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,46 @@ pytest tests/test_smoke.py -v
240240
- Add unit tests for your code
241241
- Test your project before submitting a PR
242242

243+
### Registry Validator
244+
245+
Before opening a pull request, validate the project registry to ensure all project metadata is correct.
246+
247+
#### Run validation
248+
249+
```bash
250+
python utils/registry_validator.py
251+
```
252+
253+
#### Validate a custom registry
254+
255+
```bash
256+
python utils/registry_validator.py --file projects_registry.json
257+
```
258+
259+
#### JSON output
260+
261+
```bash
262+
python utils/registry_validator.py --json
263+
```
264+
265+
#### Strict mode (CI-friendly)
266+
267+
Returns a non-zero exit code if validation fails.
268+
269+
```bash
270+
python utils/registry_validator.py --strict
271+
```
272+
273+
The validator checks:
274+
275+
- Valid JSON format
276+
- Required project fields
277+
- Valid categories
278+
- Valid difficulty levels
279+
- Duplicate project names
280+
- Duplicate project paths
281+
- Missing project files
282+
243283
#### Virtual Environment Reminder
244284

245285
Always activate your virtual environment before working:

projects_registry.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,8 @@
785785
"mines"
786786
],
787787
"path": "games/Minesweeper/minesweeper.py"
788+
},
789+
{
788790
"name": "Snake Game",
789791
"emoji": "🐍",
790792
"category": "games",

tests/test_registry_validator.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import json
2+
from pathlib import Path
3+
4+
from utils.registry_validator import RegistryValidator
5+
6+
def write_registry(tmp_path, data):
7+
registry = tmp_path / "projects_registry.json"
8+
9+
registry.write_text(
10+
json.dumps(data, indent=2),
11+
encoding="utf-8",
12+
)
13+
14+
return registry
15+
16+
def test_valid_registry(tmp_path):
17+
project_file = tmp_path / "demo.py"
18+
project_file.write_text("print('hello')")
19+
20+
registry = write_registry(
21+
tmp_path,
22+
[
23+
{
24+
"name": "Demo",
25+
"emoji": "🔥",
26+
"category": "utilities",
27+
"difficulty": "beginner",
28+
"description": "Demo project",
29+
"keywords": ["demo"],
30+
"path": "demo.py",
31+
}
32+
],
33+
)
34+
35+
validator = RegistryValidator(registry)
36+
37+
validator.validate()
38+
39+
assert validator.errors == []
40+
41+
def test_invalid_category(tmp_path):
42+
project_file = tmp_path / "demo.py"
43+
project_file.write_text("print('hello')")
44+
45+
registry = write_registry(
46+
tmp_path,
47+
[
48+
{
49+
"name": "Demo",
50+
"emoji": "🔥",
51+
"category": "invalid",
52+
"difficulty": "beginner",
53+
"description": "Demo",
54+
"keywords": ["demo"],
55+
"path": "demo.py",
56+
}
57+
],
58+
)
59+
60+
validator = RegistryValidator(registry)
61+
62+
validator.validate()
63+
64+
assert validator.errors
65+
66+
def test_duplicate_names(tmp_path):
67+
(tmp_path / "a.py").write_text("")
68+
(tmp_path / "b.py").write_text("")
69+
70+
registry = write_registry(
71+
tmp_path,
72+
[
73+
{
74+
"name": "Demo",
75+
"emoji": "🔥",
76+
"category": "utilities",
77+
"difficulty": "beginner",
78+
"description": "One",
79+
"keywords": ["demo"],
80+
"path": "a.py",
81+
},
82+
{
83+
"name": "Demo",
84+
"emoji": "🔥",
85+
"category": "utilities",
86+
"difficulty": "beginner",
87+
"description": "Two",
88+
"keywords": ["demo"],
89+
"path": "b.py",
90+
},
91+
],
92+
)
93+
94+
validator = RegistryValidator(registry)
95+
96+
validator.validate()
97+
98+
assert any("Duplicate project name" in e for e in validator.errors)
99+
100+
def test_invalid_difficulty(tmp_path):
101+
project_file = tmp_path / "demo.py"
102+
project_file.write_text("print('hello')")
103+
104+
registry = write_registry(
105+
tmp_path,
106+
[{
107+
"name": "Demo",
108+
"emoji": "🔥",
109+
"category": "utilities",
110+
"difficulty": "expert",
111+
"description": "Demo",
112+
"keywords": ["demo"],
113+
"path": "demo.py",
114+
}],
115+
)
116+
117+
validator = RegistryValidator(registry)
118+
validator.validate()
119+
120+
assert any("invalid difficulty" in e.lower() for e in validator.errors)
121+
122+
def test_duplicate_paths(tmp_path):
123+
(tmp_path / "demo.py").write_text("print('hello')")
124+
125+
registry = write_registry(
126+
tmp_path,
127+
[
128+
{
129+
"name": "Demo1",
130+
"emoji": "🔥",
131+
"category": "utilities",
132+
"difficulty": "beginner",
133+
"description": "Demo",
134+
"keywords": ["demo"],
135+
"path": "demo.py",
136+
},
137+
{
138+
"name": "Demo2",
139+
"emoji": "🔥",
140+
"category": "utilities",
141+
"difficulty": "beginner",
142+
"description": "Demo",
143+
"keywords": ["demo"],
144+
"path": "demo.py",
145+
},
146+
],
147+
)
148+
149+
validator = RegistryValidator(registry)
150+
validator.validate()
151+
152+
assert any("duplicate project path" in e.lower() for e in validator.errors)
153+
154+
def test_missing_required_field(tmp_path):
155+
(tmp_path / "demo.py").write_text("print('hello')")
156+
157+
registry = write_registry(
158+
tmp_path,
159+
[{
160+
"name": "Demo",
161+
"emoji": "🔥",
162+
"category": "utilities",
163+
"difficulty": "beginner",
164+
"description": "Demo",
165+
"path": "demo.py",
166+
}],
167+
)
168+
169+
validator = RegistryValidator(registry)
170+
validator.validate()
171+
172+
assert any("missing required fields" in e.lower() for e in validator.errors)
173+
174+
def test_missing_project_file(tmp_path):
175+
registry = write_registry(
176+
tmp_path,
177+
[{
178+
"name": "Demo",
179+
"emoji": "🔥",
180+
"category": "utilities",
181+
"difficulty": "beginner",
182+
"description": "Demo",
183+
"keywords": ["demo"],
184+
"path": "missing.py",
185+
}],
186+
)
187+
188+
validator = RegistryValidator(registry)
189+
validator.validate()
190+
191+
assert any("missing project file" in e.lower() for e in validator.errors)
192+
193+
def test_invalid_json(tmp_path):
194+
registry = tmp_path / "projects_registry.json"
195+
196+
registry.write_text("{ invalid json", encoding="utf-8")
197+
198+
validator = RegistryValidator(registry)
199+
validator.validate()
200+
201+
assert any("invalid json" in e.lower() for e in validator.errors)
202+

0 commit comments

Comments
 (0)