specleap-framework 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/backend.md +419 -0
- package/.agents/frontend.md +577 -0
- package/.agents/producto.md +516 -0
- package/.commands/adoptar.md +323 -0
- package/.commands/ayuda.md +142 -0
- package/.commands/crear-tickets.md +55 -0
- package/.commands/documentar.md +285 -0
- package/.commands/explicar.md +234 -0
- package/.commands/implementar.md +383 -0
- package/.commands/inicio.md +824 -0
- package/.commands/nuevo/README.md +292 -0
- package/.commands/nuevo/questions-base.yaml +320 -0
- package/.commands/nuevo/responses-example.yaml +53 -0
- package/.commands/planificar.md +253 -0
- package/.commands/refinar.md +306 -0
- package/LICENSE +21 -0
- package/README.md +603 -0
- package/SETUP.md +351 -0
- package/install.sh +152 -0
- package/package.json +60 -0
- package/proyectos/_template/.gitkeep +1 -0
- package/proyectos/_template/ANEXOS.md +21 -0
- package/proyectos/_template/CONTRATO.md +26 -0
- package/proyectos/_template/context/.gitkeep +1 -0
- package/rules/development-rules.md +113 -0
- package/rules/environment-protection.md +97 -0
- package/rules/git-workflow.md +142 -0
- package/rules/session-protocol.md +121 -0
- package/scripts/README.md +129 -0
- package/scripts/analyze-project.sh +826 -0
- package/scripts/create-asana-tasks.sh +133 -0
- package/scripts/detect-project-type.sh +141 -0
- package/scripts/estimate-effort.sh +290 -0
- package/scripts/generate-asana-structure.sh +262 -0
- package/scripts/generate-contract.sh +360 -0
- package/scripts/generate-contrato.sh +555 -0
- package/scripts/install-git-hooks.sh +141 -0
- package/scripts/install-skills.sh +130 -0
- package/scripts/lib/asana-utils.sh +191 -0
- package/scripts/lib/jira-project-utils.sh +222 -0
- package/scripts/lib/questions.json +831 -0
- package/scripts/lib/render-contrato.py +195 -0
- package/scripts/lib/validate.sh +325 -0
- package/scripts/parse-contrato.sh +190 -0
- package/scripts/setup-mcp.sh +654 -0
- package/scripts/test-cuestionario.sh +428 -0
- package/setup.sh +458 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# SpecLeap — Suite de tests para el sistema de cuestionario
|
|
4
|
+
# Fase 2.5
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
GENERATE_SCRIPT="$SCRIPT_DIR/generate-contrato.sh"
|
|
10
|
+
VALIDATE_LIB="$SCRIPT_DIR/lib/validate.sh"
|
|
11
|
+
PARSE_SCRIPT="$SCRIPT_DIR/parse-contrato.sh"
|
|
12
|
+
JIRA_SCRIPT="$SCRIPT_DIR/generate-jira-structure.sh"
|
|
13
|
+
|
|
14
|
+
# Colores
|
|
15
|
+
RED='\033[0;31m'
|
|
16
|
+
GREEN='\033[0;32m'
|
|
17
|
+
YELLOW='\033[1;33m'
|
|
18
|
+
CYAN='\033[0;36m'
|
|
19
|
+
BOLD='\033[1m'
|
|
20
|
+
RESET='\033[0m'
|
|
21
|
+
|
|
22
|
+
# Contadores
|
|
23
|
+
TESTS_RUN=0
|
|
24
|
+
TESTS_PASSED=0
|
|
25
|
+
TESTS_FAILED=0
|
|
26
|
+
|
|
27
|
+
print_test_header() {
|
|
28
|
+
echo -e "\n${CYAN}${BOLD}═══ $1 ═══${RESET}\n"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
print_test() {
|
|
32
|
+
echo -e "${CYAN}→ Test: $1${RESET}"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
print_pass() {
|
|
36
|
+
echo -e "${GREEN} ✅ PASS${RESET}"
|
|
37
|
+
TESTS_PASSED=$((TESTS_PASSED + 1))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
print_fail() {
|
|
41
|
+
echo -e "${RED} ❌ FAIL: $1${RESET}"
|
|
42
|
+
TESTS_FAILED=$((TESTS_FAILED + 1))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
run_test() {
|
|
46
|
+
TESTS_RUN=$((TESTS_RUN + 1))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# ============================================================================
|
|
50
|
+
# TESTS DE VALIDACIÓN
|
|
51
|
+
# ============================================================================
|
|
52
|
+
|
|
53
|
+
test_validate_string() {
|
|
54
|
+
print_test_header "Validaciones de String"
|
|
55
|
+
|
|
56
|
+
source "$VALIDATE_LIB"
|
|
57
|
+
|
|
58
|
+
# Test 1: String con pattern slug válido
|
|
59
|
+
print_test "String slug válido"
|
|
60
|
+
run_test
|
|
61
|
+
if validate_string_advanced "mi-proyecto-123" "^[a-z0-9-]+$" 3 50 > /dev/null 2>&1; then
|
|
62
|
+
print_pass
|
|
63
|
+
else
|
|
64
|
+
print_fail "Debería aceptar slug válido"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Test 2: String slug inválido (mayúsculas)
|
|
68
|
+
print_test "String slug inválido (mayúsculas)"
|
|
69
|
+
run_test
|
|
70
|
+
if ! validate_string_advanced "Mi-Proyecto" "^[a-z0-9-]+$" 3 50 > /dev/null 2>&1; then
|
|
71
|
+
print_pass
|
|
72
|
+
else
|
|
73
|
+
print_fail "Debería rechazar mayúsculas en slug"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Test 3: String muy corto
|
|
77
|
+
print_test "String muy corto (min_length)"
|
|
78
|
+
run_test
|
|
79
|
+
if ! validate_string_advanced "ab" "^[a-z0-9-]+$" 3 50 > /dev/null 2>&1; then
|
|
80
|
+
print_pass
|
|
81
|
+
else
|
|
82
|
+
print_fail "Debería rechazar string menor a min_length"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Test 4: String muy largo
|
|
86
|
+
print_test "String muy largo (max_length)"
|
|
87
|
+
run_test
|
|
88
|
+
local long_string=$(printf 'a%.0s' {1..100})
|
|
89
|
+
if ! validate_string_advanced "$long_string" "" 0 50 > /dev/null 2>&1; then
|
|
90
|
+
print_pass
|
|
91
|
+
else
|
|
92
|
+
print_fail "Debería rechazar string mayor a max_length"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Test 5: Hexcolor válido
|
|
96
|
+
print_test "Hexcolor válido"
|
|
97
|
+
run_test
|
|
98
|
+
if validate_string_advanced "#3B82F6" "^#[0-9A-Fa-f]{6}$" 0 999 > /dev/null 2>&1; then
|
|
99
|
+
print_pass
|
|
100
|
+
else
|
|
101
|
+
print_fail "Debería aceptar hexcolor válido"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Test 6: Hexcolor inválido
|
|
105
|
+
print_test "Hexcolor inválido"
|
|
106
|
+
run_test
|
|
107
|
+
if ! validate_string_advanced "#ZZZ" "^#[0-9A-Fa-f]{6}$" 0 999 > /dev/null 2>&1; then
|
|
108
|
+
print_pass
|
|
109
|
+
else
|
|
110
|
+
print_fail "Debería rechazar hexcolor inválido"
|
|
111
|
+
fi
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
test_validate_select() {
|
|
115
|
+
print_test_header "Validaciones de Select"
|
|
116
|
+
|
|
117
|
+
source "$VALIDATE_LIB"
|
|
118
|
+
|
|
119
|
+
# Test 1: Opción válida
|
|
120
|
+
print_test "Opción válida"
|
|
121
|
+
run_test
|
|
122
|
+
if validate_select_strict "laravel" "laravel" "nodejs" "python" > /dev/null 2>&1; then
|
|
123
|
+
print_pass
|
|
124
|
+
else
|
|
125
|
+
print_fail "Debería aceptar opción válida"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Test 2: Opción inválida
|
|
129
|
+
print_test "Opción inválida"
|
|
130
|
+
run_test
|
|
131
|
+
if ! validate_select_strict "django" "laravel" "nodejs" "python" > /dev/null 2>&1; then
|
|
132
|
+
print_pass
|
|
133
|
+
else
|
|
134
|
+
print_fail "Debería rechazar opción inválida"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# Test 3: Case-insensitive matching
|
|
138
|
+
print_test "Case-insensitive matching"
|
|
139
|
+
run_test
|
|
140
|
+
if validate_select_strict "Laravel" "laravel" "nodejs" "python" > /dev/null 2>&1; then
|
|
141
|
+
print_pass
|
|
142
|
+
else
|
|
143
|
+
print_fail "Debería aceptar opciones case-insensitive"
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
test_validate_boolean() {
|
|
148
|
+
print_test_header "Validaciones de Boolean"
|
|
149
|
+
|
|
150
|
+
source "$VALIDATE_LIB"
|
|
151
|
+
|
|
152
|
+
# Test valores true
|
|
153
|
+
print_test "Boolean true (múltiples formatos)"
|
|
154
|
+
run_test
|
|
155
|
+
local all_true=true
|
|
156
|
+
for val in "true" "s" "si" "yes" "y" "1"; do
|
|
157
|
+
if ! validate_boolean_strict "$val" | grep -q "true"; then
|
|
158
|
+
all_true=false
|
|
159
|
+
fi
|
|
160
|
+
done
|
|
161
|
+
if $all_true; then
|
|
162
|
+
print_pass
|
|
163
|
+
else
|
|
164
|
+
print_fail "Debería normalizar todos los valores true"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Test valores false
|
|
168
|
+
print_test "Boolean false (múltiples formatos)"
|
|
169
|
+
run_test
|
|
170
|
+
local all_false=true
|
|
171
|
+
for val in "false" "n" "no" "0"; do
|
|
172
|
+
if ! validate_boolean_strict "$val" | grep -q "false"; then
|
|
173
|
+
all_false=false
|
|
174
|
+
fi
|
|
175
|
+
done
|
|
176
|
+
if $all_false; then
|
|
177
|
+
print_pass
|
|
178
|
+
else
|
|
179
|
+
print_fail "Debería normalizar todos los valores false"
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
# Test valor inválido
|
|
183
|
+
print_test "Boolean inválido"
|
|
184
|
+
run_test
|
|
185
|
+
if ! validate_boolean_strict "maybe" > /dev/null 2>&1; then
|
|
186
|
+
print_pass
|
|
187
|
+
else
|
|
188
|
+
print_fail "Debería rechazar valor booleano inválido"
|
|
189
|
+
fi
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
test_validate_number() {
|
|
193
|
+
print_test_header "Validaciones de Number"
|
|
194
|
+
|
|
195
|
+
source "$VALIDATE_LIB"
|
|
196
|
+
|
|
197
|
+
# Test número válido en rango
|
|
198
|
+
print_test "Número válido en rango"
|
|
199
|
+
run_test
|
|
200
|
+
if validate_number_range "50" 0 100 > /dev/null 2>&1; then
|
|
201
|
+
print_pass
|
|
202
|
+
else
|
|
203
|
+
print_fail "Debería aceptar número en rango"
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# Test número menor al mínimo
|
|
207
|
+
print_test "Número menor al mínimo"
|
|
208
|
+
run_test
|
|
209
|
+
if ! validate_number_range "5" 10 100 > /dev/null 2>&1; then
|
|
210
|
+
print_pass
|
|
211
|
+
else
|
|
212
|
+
print_fail "Debería rechazar número menor al mínimo"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# Test número mayor al máximo
|
|
216
|
+
print_test "Número mayor al máximo"
|
|
217
|
+
run_test
|
|
218
|
+
if ! validate_number_range "150" 0 100 > /dev/null 2>&1; then
|
|
219
|
+
print_pass
|
|
220
|
+
else
|
|
221
|
+
print_fail "Debería rechazar número mayor al máximo"
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# Test no es número
|
|
225
|
+
print_test "No es número"
|
|
226
|
+
run_test
|
|
227
|
+
if ! validate_number_range "abc" 0 100 > /dev/null 2>&1; then
|
|
228
|
+
print_pass
|
|
229
|
+
else
|
|
230
|
+
print_fail "Debería rechazar texto como número"
|
|
231
|
+
fi
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# ============================================================================
|
|
235
|
+
# TESTS DE PARSER
|
|
236
|
+
# ============================================================================
|
|
237
|
+
|
|
238
|
+
test_parser() {
|
|
239
|
+
print_test_header "Parser CONTRATO.md"
|
|
240
|
+
|
|
241
|
+
# Crear CONTRATO.md de prueba
|
|
242
|
+
local test_dir="/tmp/specleap-test-$$"
|
|
243
|
+
mkdir -p "$test_dir"
|
|
244
|
+
|
|
245
|
+
cat > "$test_dir/CONTRATO.md" <<'EOF'
|
|
246
|
+
---
|
|
247
|
+
project:
|
|
248
|
+
name: test-project
|
|
249
|
+
display_name: Test Project
|
|
250
|
+
identity:
|
|
251
|
+
objective: Test objective
|
|
252
|
+
problem_solved: Test problem
|
|
253
|
+
stack:
|
|
254
|
+
backend:
|
|
255
|
+
framework: laravel
|
|
256
|
+
features:
|
|
257
|
+
core:
|
|
258
|
+
- Feature 1
|
|
259
|
+
- Feature 2
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
# Test Content
|
|
263
|
+
EOF
|
|
264
|
+
|
|
265
|
+
# Test 1: Parse exitoso
|
|
266
|
+
print_test "Parse CONTRATO.md válido"
|
|
267
|
+
run_test
|
|
268
|
+
if bash "$PARSE_SCRIPT" parse "$test_dir/CONTRATO.md" "$test_dir/output.json" > /dev/null 2>&1; then
|
|
269
|
+
if [[ -f "$test_dir/output.json" ]]; then
|
|
270
|
+
print_pass
|
|
271
|
+
else
|
|
272
|
+
print_fail "No se generó output.json"
|
|
273
|
+
fi
|
|
274
|
+
else
|
|
275
|
+
print_fail "Fallo al parsear"
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
# Test 2: Validación estructura
|
|
279
|
+
print_test "Validar estructura CONTRATO.md"
|
|
280
|
+
run_test
|
|
281
|
+
if bash "$PARSE_SCRIPT" validate "$test_dir/CONTRATO.md" > /dev/null 2>&1; then
|
|
282
|
+
print_pass
|
|
283
|
+
else
|
|
284
|
+
print_fail "Validación debería pasar"
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# Test 3: Extraer nombre
|
|
288
|
+
print_test "Extraer project.name"
|
|
289
|
+
run_test
|
|
290
|
+
local extracted_name=$(bash "$PARSE_SCRIPT" get-name "$test_dir/CONTRATO.md" 2>/dev/null)
|
|
291
|
+
if [[ "$extracted_name" == "test-project" ]]; then
|
|
292
|
+
print_pass
|
|
293
|
+
else
|
|
294
|
+
print_fail "Debería extraer 'test-project', obtuvo: $extracted_name"
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# Cleanup
|
|
298
|
+
rm -rf "$test_dir"
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
# ============================================================================
|
|
302
|
+
# TESTS DE GENERACIÓN JIRA
|
|
303
|
+
# ============================================================================
|
|
304
|
+
|
|
305
|
+
test_jira_generation() {
|
|
306
|
+
print_test_header "Generación Backlog Jira"
|
|
307
|
+
|
|
308
|
+
# Crear CONTRATO.md de prueba
|
|
309
|
+
local test_dir="/tmp/specleap-test-jira-$$"
|
|
310
|
+
mkdir -p "$test_dir"
|
|
311
|
+
|
|
312
|
+
cat > "$test_dir/CONTRATO.md" <<'EOF'
|
|
313
|
+
---
|
|
314
|
+
project:
|
|
315
|
+
name: test-jira
|
|
316
|
+
display_name: Test Jira Project
|
|
317
|
+
identity:
|
|
318
|
+
objective: Test Jira integration
|
|
319
|
+
problem_solved: Generate Jira backlog
|
|
320
|
+
stack:
|
|
321
|
+
backend:
|
|
322
|
+
framework: laravel
|
|
323
|
+
features:
|
|
324
|
+
core:
|
|
325
|
+
- Authentication
|
|
326
|
+
- Dashboard
|
|
327
|
+
- Reports
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
# Test Content
|
|
331
|
+
EOF
|
|
332
|
+
|
|
333
|
+
# Test 1: Generar backlog
|
|
334
|
+
print_test "Generar backlog.json"
|
|
335
|
+
run_test
|
|
336
|
+
if bash "$JIRA_SCRIPT" "$test_dir/CONTRATO.md" > /dev/null 2>&1; then
|
|
337
|
+
if [[ -f "$test_dir/.jira/backlog.json" ]]; then
|
|
338
|
+
print_pass
|
|
339
|
+
else
|
|
340
|
+
print_fail "No se generó backlog.json"
|
|
341
|
+
fi
|
|
342
|
+
else
|
|
343
|
+
print_fail "Fallo al generar backlog"
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# Test 2: Estructura JSON válida
|
|
347
|
+
print_test "Estructura JSON válida"
|
|
348
|
+
run_test
|
|
349
|
+
if [[ -f "$test_dir/.jira/backlog.json" ]]; then
|
|
350
|
+
if jq empty "$test_dir/.jira/backlog.json" 2>/dev/null; then
|
|
351
|
+
print_pass
|
|
352
|
+
else
|
|
353
|
+
print_fail "JSON inválido"
|
|
354
|
+
fi
|
|
355
|
+
else
|
|
356
|
+
print_fail "Backlog no existe"
|
|
357
|
+
fi
|
|
358
|
+
|
|
359
|
+
# Test 3: Épicas generadas (debería ser 3, una por feature)
|
|
360
|
+
print_test "Épicas generadas (3 esperadas)"
|
|
361
|
+
run_test
|
|
362
|
+
if [[ -f "$test_dir/.jira/backlog.json" ]]; then
|
|
363
|
+
local epic_count=$(jq '.epics | length' "$test_dir/.jira/backlog.json")
|
|
364
|
+
if [[ "$epic_count" == "3" ]]; then
|
|
365
|
+
print_pass
|
|
366
|
+
else
|
|
367
|
+
print_fail "Esperado 3 épicas, obtuvo: $epic_count"
|
|
368
|
+
fi
|
|
369
|
+
else
|
|
370
|
+
print_fail "Backlog no existe"
|
|
371
|
+
fi
|
|
372
|
+
|
|
373
|
+
# Test 4: Stories por épica (debería ser 4 por épica)
|
|
374
|
+
print_test "Stories por épica (4 esperadas)"
|
|
375
|
+
run_test
|
|
376
|
+
if [[ -f "$test_dir/.jira/backlog.json" ]]; then
|
|
377
|
+
local story_count=$(jq '.epics[0].stories | length' "$test_dir/.jira/backlog.json")
|
|
378
|
+
if [[ "$story_count" == "4" ]]; then
|
|
379
|
+
print_pass
|
|
380
|
+
else
|
|
381
|
+
print_fail "Esperado 4 stories por épica, obtuvo: $story_count"
|
|
382
|
+
fi
|
|
383
|
+
else
|
|
384
|
+
print_fail "Backlog no existe"
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
# Cleanup
|
|
388
|
+
rm -rf "$test_dir"
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
# ============================================================================
|
|
392
|
+
# MAIN
|
|
393
|
+
# ============================================================================
|
|
394
|
+
|
|
395
|
+
main() {
|
|
396
|
+
echo -e "${CYAN}${BOLD}"
|
|
397
|
+
echo "════════════════════════════════════════════════════════════════"
|
|
398
|
+
echo " 🧪 SpecLeap — Test Suite"
|
|
399
|
+
echo "════════════════════════════════════════════════════════════════"
|
|
400
|
+
echo -e "${RESET}"
|
|
401
|
+
|
|
402
|
+
test_validate_string
|
|
403
|
+
test_validate_select
|
|
404
|
+
test_validate_boolean
|
|
405
|
+
test_validate_number
|
|
406
|
+
test_parser
|
|
407
|
+
test_jira_generation
|
|
408
|
+
|
|
409
|
+
# Resumen
|
|
410
|
+
echo ""
|
|
411
|
+
echo -e "${CYAN}${BOLD}════════════════════════════════════════════════════════════════${RESET}"
|
|
412
|
+
echo -e "${BOLD}RESUMEN${RESET}"
|
|
413
|
+
echo -e "${CYAN} Tests ejecutados: ${BOLD}$TESTS_RUN${RESET}"
|
|
414
|
+
echo -e "${GREEN} Tests pasados: ${BOLD}$TESTS_PASSED${RESET}"
|
|
415
|
+
echo -e "${RED} Tests fallidos: ${BOLD}$TESTS_FAILED${RESET}"
|
|
416
|
+
echo -e "${CYAN}${BOLD}════════════════════════════════════════════════════════════════${RESET}"
|
|
417
|
+
|
|
418
|
+
if [[ $TESTS_FAILED -eq 0 ]]; then
|
|
419
|
+
echo -e "\n${GREEN}${BOLD}✅ Todos los tests pasaron!${RESET}\n"
|
|
420
|
+
exit 0
|
|
421
|
+
else
|
|
422
|
+
echo -e "\n${RED}${BOLD}❌ Algunos tests fallaron${RESET}\n"
|
|
423
|
+
exit 1
|
|
424
|
+
fi
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
# Ejecutar
|
|
428
|
+
main "$@"
|