Skip to content

Commit a8e3fd5

Browse files
authored
Merge pull request #10 from ReproNim/feat/improve-cookiecutter-qa
feat: improve cookiecutter reliability and testing infrastructure
2 parents 416471e + cca6b2e commit a8e3fd5

39 files changed

+1151
-138
lines changed

.github/pull_request_template.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
## Description
2+
Brief description of changes made in this PR.
3+
4+
## Related Issue
5+
Fixes #(issue number)
6+
7+
## Type of Change
8+
- [ ] Bug fix (non-breaking change which fixes an issue)
9+
- [ ] New feature (non-breaking change which adds functionality)
10+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
11+
- [ ] Documentation update
12+
- [ ] Performance improvement
13+
- [ ] Code refactoring
14+
15+
## Changes Made
16+
- List specific changes made
17+
- Include file paths affected
18+
- Highlight any schema version updates
19+
20+
## Testing
21+
- [ ] Unit tests pass locally
22+
- [ ] Integration tests pass
23+
- [ ] Schema validation passes
24+
- [ ] Cross-platform testing completed (if applicable)
25+
26+
## Checklist
27+
- [ ] My code follows the project's style guidelines
28+
- [ ] I have performed a self-review of my own code
29+
- [ ] I have commented my code where necessary
30+
- [ ] I have updated the documentation
31+
- [ ] My changes generate no new warnings
32+
- [ ] Schema versions are consistent (1.0.0)
33+
- [ ] No hardcoded activity references remain
34+
35+
## Screenshots (if applicable)
36+
Add screenshots for UI changes or validation results.
37+
38+
## Additional Notes
39+
Any additional information that reviewers should know.
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
name: Test Cookiecutter
2+
3+
on:
4+
push:
5+
branches: [main, dev, 'feat/*']
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
jobs:
11+
test-generation:
12+
name: Test template generation
13+
runs-on: ${{ matrix.os }}
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os: [ubuntu-latest, windows-latest, macos-latest]
18+
python-version: ['3.8', '3.9', '3.10', '3.11']
19+
num-activities: [1, 3, 5]
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Set up Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: ${{ matrix.python-version }}
28+
29+
- name: Install dependencies
30+
run: |
31+
python -m pip install --upgrade pip
32+
pip install -r requirements.txt
33+
34+
- name: Test cookiecutter generation
35+
run: |
36+
python -m cookiecutter . --no-input \
37+
protocol_name="Test Protocol ${{ matrix.num-activities }}" \
38+
protocol_description="Testing with ${{ matrix.num-activities }} activities" \
39+
github_org="test-org" \
40+
github_repo="test-repo" \
41+
protocol_slug="test_protocol_${{ matrix.num-activities }}" \
42+
author_name="Test Author" \
43+
author_email="[email protected]" \
44+
license="MIT" \
45+
number_of_activities=${{ matrix.num-activities }}
46+
47+
- name: Validate generated project structure
48+
run: |
49+
cd "Test Protocol ${{ matrix.num-activities }}"
50+
# Check essential files exist
51+
test -f README.md
52+
test -f Makefile
53+
test -f LICENSE
54+
test -f config.env
55+
test -d activities
56+
test -d ui-changes
57+
test -f test_protocol_${{ matrix.num-activities }}/test_protocol_${{ matrix.num-activities }}_schema
58+
shell: bash
59+
60+
- name: Count activities
61+
run: |
62+
cd "Test Protocol ${{ matrix.num-activities }}/activities"
63+
count=$(ls -d */ | wc -l)
64+
echo "Found $count activities"
65+
if [ $count -ne ${{ matrix.num-activities }} ]; then
66+
echo "ERROR: Expected ${{ matrix.num-activities }} activities but found $count"
67+
exit 1
68+
fi
69+
shell: bash
70+
71+
- name: Validate JSON schemas
72+
run: |
73+
cd "Test Protocol ${{ matrix.num-activities }}"
74+
python -c "
75+
import json
76+
import sys
77+
from pathlib import Path
78+
79+
errors = []
80+
81+
# Check all schema files
82+
for schema_file in Path('.').rglob('*_schema'):
83+
try:
84+
with open(schema_file) as f:
85+
json.load(f)
86+
print(f'✓ Valid JSON: {schema_file}')
87+
except json.JSONDecodeError as e:
88+
errors.append(f'✗ Invalid JSON in {schema_file}: {e}')
89+
90+
# Check all item files
91+
for item_file in Path('.').rglob('*_item'):
92+
try:
93+
with open(item_file) as f:
94+
json.load(f)
95+
print(f'✓ Valid JSON: {item_file}')
96+
except json.JSONDecodeError as e:
97+
errors.append(f'✗ Invalid JSON in {item_file}: {e}')
98+
99+
if errors:
100+
print('\nErrors found:')
101+
for error in errors:
102+
print(error)
103+
sys.exit(1)
104+
else:
105+
print('\nAll schemas are valid JSON!')
106+
"
107+
108+
- name: Check protocol schema has correct activities
109+
run: |
110+
cd "Test Protocol ${{ matrix.num-activities }}"
111+
python -c "
112+
import json
113+
import sys
114+
115+
with open('test_protocol_${{ matrix.num-activities }}/test_protocol_${{ matrix.num-activities }}_schema') as f:
116+
schema = json.load(f)
117+
118+
activities = schema['ui']['addProperties']
119+
order = schema['ui']['order']
120+
121+
print(f'Found {len(activities)} activities in addProperties')
122+
print(f'Found {len(order)} activities in order')
123+
124+
if len(activities) != ${{ matrix.num-activities }}:
125+
print(f'ERROR: Expected ${{ matrix.num-activities }} activities but found {len(activities)}')
126+
sys.exit(1)
127+
128+
if len(order) != ${{ matrix.num-activities }}:
129+
print(f'ERROR: Expected ${{ matrix.num-activities }} items in order but found {len(order)}')
130+
sys.exit(1)
131+
132+
print('✓ Protocol schema has correct number of activities')
133+
"
134+
135+
validate-schemas:
136+
name: Validate ReproSchema compliance
137+
runs-on: ubuntu-latest
138+
needs: test-generation
139+
140+
steps:
141+
- uses: actions/checkout@v4
142+
143+
- name: Set up Python
144+
uses: actions/setup-python@v5
145+
with:
146+
python-version: '3.11'
147+
148+
- name: Install dependencies
149+
run: |
150+
python -m pip install --upgrade pip
151+
pip install -r requirements.txt
152+
pip install reproschema
153+
154+
- name: Generate test protocol
155+
run: |
156+
python -m cookiecutter . --no-input \
157+
protocol_name="Validation Test" \
158+
number_of_activities=3
159+
160+
- name: Validate with reproschema
161+
run: |
162+
cd "Validation Test"
163+
# Note: This will fail until reproschema-py is updated
164+
# For now, we'll do basic schema validation
165+
python -c "
166+
import json
167+
from pathlib import Path
168+
169+
print('Checking schema versions...')
170+
for schema_file in Path('.').rglob('*_schema'):
171+
with open(schema_file) as f:
172+
schema = json.load(f)
173+
174+
context = schema.get('@context', '')
175+
if '1.0.0-rc' in str(context):
176+
print(f'WARNING: {schema_file} uses release candidate version')
177+
elif '1.0.0' in str(context):
178+
print(f'✓ {schema_file} uses stable version')
179+
else:
180+
print(f'? {schema_file} has unexpected context: {context}')
181+
"
182+
183+
test-hooks:
184+
name: Test generation hooks
185+
runs-on: ubuntu-latest
186+
187+
steps:
188+
- uses: actions/checkout@v4
189+
190+
- name: Set up Python
191+
uses: actions/setup-python@v5
192+
with:
193+
python-version: '3.11'
194+
195+
- name: Install dependencies
196+
run: |
197+
python -m pip install --upgrade pip
198+
pip install -r requirements.txt
199+
200+
- name: Test pre-generation hook
201+
run: |
202+
cd hooks
203+
python -c "
204+
# Test activity selection
205+
import sys
206+
sys.path.insert(0, '.')
207+
208+
# Mock cookiecutter context
209+
class MockCookiecutter:
210+
number_of_activities = '3'
211+
212+
# Replace the template variable
213+
with open('pre_gen_project.py', 'r') as f:
214+
code = f.read()
215+
code = code.replace('{{ cookiecutter.number_of_activities }}', '3')
216+
217+
# Execute the modified code
218+
exec(code)
219+
220+
# Check if activities were selected
221+
import json
222+
with open('../selected_activities.json') as f:
223+
selected = json.load(f)
224+
225+
print(f'Selected activities: {selected}')
226+
assert len(selected) == 3
227+
assert all(a in ['Activity1', 'Activity2', 'Activity3', 'selectActivity', 'voiceActivity'] for a in selected)
228+
print('✓ Pre-generation hook works correctly')
229+
"
230+
231+
- name: Clean up
232+
run: rm -f selected_activities.json
233+
234+
lint-and-format:
235+
name: Code quality checks
236+
runs-on: ubuntu-latest
237+
238+
steps:
239+
- uses: actions/checkout@v4
240+
241+
- name: Set up Python
242+
uses: actions/setup-python@v5
243+
with:
244+
python-version: '3.11'
245+
246+
- name: Install dependencies
247+
run: |
248+
python -m pip install --upgrade pip
249+
pip install -r requirements.txt
250+
251+
- name: Run black
252+
run: black --check hooks/ update_schema_version.py
253+
254+
- name: Run ruff
255+
run: ruff check hooks/ update_schema_version.py
256+
257+
- name: Run bandit
258+
run: bandit -r hooks/ -ll
259+
continue-on-error: true # Bandit can be overly strict
260+
261+
integration-test:
262+
name: Full integration test
263+
runs-on: ubuntu-latest
264+
needs: [test-generation, validate-schemas, test-hooks]
265+
266+
steps:
267+
- uses: actions/checkout@v4
268+
269+
- name: Set up Python
270+
uses: actions/setup-python@v5
271+
with:
272+
python-version: '3.11'
273+
274+
- name: Install dependencies
275+
run: |
276+
python -m pip install --upgrade pip
277+
pip install -r requirements.txt
278+
279+
- name: Generate and test protocol
280+
run: |
281+
# Generate a protocol
282+
python -m cookiecutter . --no-input \
283+
protocol_name="Integration Test" \
284+
number_of_activities=3
285+
286+
cd "Integration Test"
287+
288+
# Test that Makefile works
289+
make help
290+
291+
# Check config.env was created
292+
test -f config.env
293+
grep -q "REPROSCHEMA_UI_CHECKSUM" config.env
294+
295+
# Ensure no Activity4 references
296+
if grep -r "Activity4" .; then
297+
echo "ERROR: Found Activity4 references"
298+
exit 1
299+
fi
300+
301+
echo "✓ Integration test passed!"

0 commit comments

Comments
 (0)