From aaf33880c4b71fbc598ffbdad2b5b95c8cd5911a Mon Sep 17 00:00:00 2001 From: rikrdo Date: Mon, 18 May 2026 00:30:39 +0200 Subject: [PATCH] test: add ARNES self-tests and docs index --- README.md | 1 + docs/README.md | 17 +++ docs/repository-layout.md | 4 +- docs/scripts-reference.md | 3 + .../__pycache__/agent_status.cpython-313.pyc | Bin 0 -> 14449 bytes .../__pycache__/new_ticket.cpython-313.pyc | Bin 0 -> 5018 bytes .../publish_ticket.cpython-313.pyc | Bin 0 -> 7606 bytes scripts/start.sh | 22 +++- scripts/verify.sh | 1 + .../test_arnes_core.cpython-313.pyc | Bin 0 -> 6572 bytes tests/test_arnes_core.py | 112 ++++++++++++++++++ 11 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 docs/README.md create mode 100644 scripts/__pycache__/agent_status.cpython-313.pyc create mode 100644 scripts/__pycache__/new_ticket.cpython-313.pyc create mode 100644 scripts/__pycache__/publish_ticket.cpython-313.pyc create mode 100644 tests/__pycache__/test_arnes_core.cpython-313.pyc create mode 100644 tests/test_arnes_core.py diff --git a/README.md b/README.md index 0bc8691..2a86944 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ El núcleo no cambia; solo el adaptador. - Guía breve: `HOWTO.md` - Starter pack: `starter-pack/README.md` - Adaptación del template: `TEMPLATE.md` +- Índice de docs: `docs/README.md` - Layout del repo: `docs/repository-layout.md` - Referencia de scripts: `docs/scripts-reference.md` - Manual Skeleton (uso + mejoras): `docs/skeleton-manual.md` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..21ff374 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,17 @@ +# Docs index + +## Core docs +- `../README.md` — framework overview +- `../HOWTO.md` — quick start +- `../HOWTO-FEATURE.md` — feature workflow +- `../TEMPLATE.md` — how to adapt ARNES + +## Reference docs +- `repository-layout.md` — repo structure and separation rules +- `scripts-reference.md` — start/verify/ticket/publish/install scripts +- `skeleton-manual.md` — default Skeleton UI notes + +## Harness source of truth +- `../harness/agents.matrix.yml` — roles and edit boundaries +- `../harness/workflow.stages.yml` — ordered workflow stages +- `../harness/policies/` — governance, security, quality, language, model routing diff --git a/docs/repository-layout.md b/docs/repository-layout.md index 3abbfd5..1a3434c 100644 --- a/docs/repository-layout.md +++ b/docs/repository-layout.md @@ -13,15 +13,17 @@ - `harness/` — workflow, roles, policies, contracts - `spec/` — product, tech, acceptance, SDD, BDD source docs - `features/` — optional executable BDD runner assets -- `scripts/` — start, verify, ticket creation, runtime status +- `scripts/` — start, verify, ticket creation, publish, install, runtime status - `platforms/` — platform adapters (pi, opencode) - `defaults/` — optional starter assets +- `tests/` — self-tests for the ARNES source repo only ## Recommended separation - Core ARNES should stay generic. - Domain checks go in `scripts/verify.local.sh`. - Domain rules go in `AGENTS.local.md`. - Real code should not be mixed into `harness/`, `work/`, `backlog/`, or `spec/`. +- Source-repo self-tests under `tests/` are not part of installed project repos. ## Default project shape ```text diff --git a/docs/scripts-reference.md b/docs/scripts-reference.md index 320d06a..2b8c023 100644 --- a/docs/scripts-reference.md +++ b/docs/scripts-reference.md @@ -14,6 +14,8 @@ Interactive bootstrap wizard. What it does: - asks project metadata - chooses default app directory (`project/` by default) +- defaults Python/Flask projects to `python3 -m unittest discover -s project/tests -v` +- seeds a minimal bootstrap smoke test under `project/tests/` for Python/Flask - writes `harness/project.config.json` - creates `scripts/verify.local.sh` - can seed one bootstrap ticket @@ -30,6 +32,7 @@ What it checks: - only one feature is `in_progress` - done features have all required artifacts, including publish evidence - runtime status JSON is valid +- source-repo self-tests run automatically if `tests/` exists - optional local overlay runs if present ## `python3 scripts/new_ticket.py` diff --git a/scripts/__pycache__/agent_status.cpython-313.pyc b/scripts/__pycache__/agent_status.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee1ed3288a6aeae79be0aa5a3f9ffe6daf36c100 GIT binary patch literal 14449 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 literal 0 HcmV?d00001 diff --git a/scripts/__pycache__/new_ticket.cpython-313.pyc b/scripts/__pycache__/new_ticket.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b460d14238e6cbfd6a71812727ea82132d499ca GIT binary patch literal 5018 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 literal 0 HcmV?d00001 diff --git a/scripts/__pycache__/publish_ticket.cpython-313.pyc b/scripts/__pycache__/publish_ticket.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88ca8dc45cf60668e8ffceacf4969992a4e7f9f8 GIT binary patch literal 7606 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 literal 0 HcmV?d00001 diff --git a/scripts/start.sh b/scripts/start.sh index d13b053..e508d20 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -38,7 +38,12 @@ else CSSFW="skeleton" fi -TEST_CMD="$(ask 'Test command' 'make test')" +DEFAULT_TEST_CMD="echo TODO-set-test-command" +if [ "$BACKEND" = "python/flask" ]; then + DEFAULT_TEST_CMD="python3 -m unittest discover -s $APP_DIR/tests -v" +fi + +TEST_CMD="$(ask 'Test command' "$DEFAULT_TEST_CMD")" LINT_CMD="$(ask 'Lint command (optional)' '')" MODEL_MODE="$(ask 'Model mode (lean/balanced/power)' 'lean')" ADD_BOOTSTRAP="$(ask 'Create bootstrap ticket F-001 now? (y/n)' 'y')" @@ -52,7 +57,19 @@ Configured by ARNES start wizard. EOF if [ "$BACKEND" = "python/flask" ]; then - mkdir -p "$APP_DIR/templates" "$APP_DIR/static/js" "$APP_DIR/static/css" "$APP_DIR/static/images" + mkdir -p "$APP_DIR/templates" "$APP_DIR/static/js" "$APP_DIR/static/css" "$APP_DIR/static/images" "$APP_DIR/tests" + [ -f "$APP_DIR/tests/test_bootstrap.py" ] || cat > "$APP_DIR/tests/test_bootstrap.py" <<'PY' +import unittest + + +class BootstrapSmokeTest(unittest.TestCase): + def test_bootstrap(self): + self.assertTrue(True) + + +if __name__ == '__main__': + unittest.main() +PY fi if [ "$CSSFW" = "skeleton" ]; then @@ -195,3 +212,4 @@ echo "- Ticket tool: python3 scripts/new_ticket.py" echo "- Publish tool: python3 scripts/publish_ticket.py --feature-id F-001" echo "- Verify: ./scripts/verify.sh" echo "- Runtime: python3 scripts/agent_status.py show" +echo "- Reminder: configure git remote before final publish if missing" diff --git a/scripts/verify.sh b/scripts/verify.sh index 4e2d87a..8586534 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -21,6 +21,7 @@ required=( "README.md" "HOWTO.md" "TEMPLATE.md" + "docs/README.md" "docs/repository-layout.md" "docs/scripts-reference.md" "harness/agents.matrix.yml" diff --git a/tests/__pycache__/test_arnes_core.cpython-313.pyc b/tests/__pycache__/test_arnes_core.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca8895af6703772fa97b56d9d4cf07ca5fa42f42 GIT binary patch literal 6572 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 JP>-dN{|9NP)^q>> literal 0 HcmV?d00001 diff --git a/tests/test_arnes_core.py b/tests/test_arnes_core.py new file mode 100644 index 0000000..a19a176 --- /dev/null +++ b/tests/test_arnes_core.py @@ -0,0 +1,112 @@ +import json +import subprocess +import tempfile +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +INSTALL = ROOT / 'scripts' / 'install_into_repo.sh' + + +class ArnesCoreTests(unittest.TestCase): + def run_cmd(self, args, cwd=None, input_text=None, check=True): + result = subprocess.run( + args, + cwd=cwd or ROOT, + input=input_text, + text=True, + capture_output=True, + ) + if check and result.returncode != 0: + raise AssertionError( + f"Command failed: {args}\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}" + ) + return result + + def test_install_refuses_source_repo(self): + result = self.run_cmd([str(INSTALL), str(ROOT)], check=False) + self.assertNotEqual(result.returncode, 0) + self.assertIn('Refusing to install ARNES into its own source repository', result.stdout) + + def test_install_start_verify_and_publish_in_external_repo(self): + with tempfile.TemporaryDirectory() as tmp: + tmp_path = Path(tmp) + target = tmp_path / 'project-repo' + remote = tmp_path / 'remote.git' + + self.run_cmd([str(INSTALL), str(target)]) + self.assertTrue((target / 'scripts' / 'start.sh').is_file()) + self.assertFalse((target / 'tests').exists(), 'source self-tests must not be installed into project repo') + + self.run_cmd(['git', 'config', 'user.name', 'ARNES Bot'], cwd=target) + self.run_cmd(['git', 'config', 'user.email', 'arnes@example.com'], cwd=target) + self.run_cmd(['git', 'init', '--bare', str(remote)]) + self.run_cmd(['git', 'remote', 'add', 'origin', str(remote)], cwd=target) + + wizard_input = '\n'.join([ + 'demo-project', + 'Demo project', + 'project', + '1', + '', + '', + '', + '', + ]) + '\n' + self.run_cmd(['./scripts/start.sh'], cwd=target, input_text=wizard_input) + + self.assertTrue((target / 'project' / 'tests').is_dir()) + cfg = json.loads((target / 'harness' / 'project.config.json').read_text(encoding='utf-8')) + self.assertEqual(cfg['app_dir'], 'project') + self.assertEqual(cfg['commands']['test'], 'python3 -m unittest discover -s project/tests -v') + + verify_result = self.run_cmd(['./scripts/verify.sh'], cwd=target) + self.assertIn('Harness verificado', verify_result.stdout) + + self.run_cmd(['python3', 'scripts/new_ticket.py'], cwd=target, input_text='\n'.join([ + 'fix', + 'Repair docs', + 'Need docs repair', + 'Make docs clear', + 'Docs flow', + 'No redesign', + 'low', + 'med', + 'Docs clear', + 'Verify green', + '', + ]) + '\n') + + with (target / 'project' / 'README.md').open('a', encoding='utf-8') as fh: + fh.write('\nchange\n') + + publish_result = self.run_cmd( + ['python3', 'scripts/publish_ticket.py', '--feature-id', 'F-001'], + cwd=target, + ) + self.assertIn('done ->', publish_result.stdout) + + publish_path = target / 'work' / 'artifacts' / 'F-001' / 'publish.json' + payload = json.loads(publish_path.read_text(encoding='utf-8')) + self.assertEqual(payload['verdict'], 'PUBLISHED') + self.assertTrue(payload['pushed']) + self.assertEqual(payload['remote'], 'origin') + + status_result = self.run_cmd(['git', 'status', '--short'], cwd=target) + self.assertEqual(status_result.stdout.strip(), '') + + remote_refs = self.run_cmd(['git', 'ls-remote', 'origin'], cwd=target) + self.assertIn('refs/heads/master', remote_refs.stdout) + + def test_agent_status_rejects_invalid_agent(self): + result = self.run_cmd( + ['python3', 'scripts/agent_status.py', 'set', '--agent', 'nope'], + check=False, + ) + self.assertNotEqual(result.returncode, 0) + combined = (result.stdout + result.stderr) + self.assertIn('Invalid agent: nope', combined) + + +if __name__ == '__main__': + unittest.main()