74 lines
4.3 KiB
Python
74 lines
4.3 KiB
Python
from pathlib import Path
|
|
import re
|
|
import sys
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
DESIGN = ROOT / 'docs/superpowers/specs/2026-08-11-mongodb-document-persistence-platform-design.md'
|
|
PLAN = ROOT / 'docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md'
|
|
ADV = ROOT / 'docs/superpowers/plans/2026-08-11-mongodb-advanced-capabilities-expansion-plan.md'
|
|
|
|
checks = []
|
|
|
|
def check(name, condition, detail=''):
|
|
checks.append((name, bool(condition), detail))
|
|
|
|
texts = {p.name: p.read_text(encoding='utf-8') for p in (DESIGN, PLAN, ADV)}
|
|
design = texts[DESIGN.name]
|
|
plan = texts[PLAN.name]
|
|
adv = texts[ADV.name]
|
|
|
|
check('design exists', DESIGN.exists())
|
|
check('stable plan exists', PLAN.exists())
|
|
check('advanced plan exists', ADV.exists())
|
|
check('design purpose', 'MongoDB 문서 영속성 플랫폼 설계서' in design)
|
|
check('domain ownership', '도메인이 `@Document`, Repository' in design)
|
|
check('no generic repository design', '범용 `CommonMongoRepository<T, ID>`를 만들지 않는다' in design)
|
|
check('stable api strict', 'Stable API V1' in design and 'apiStrict=true' in design)
|
|
check('local replica set', 'Single-node Replica Set' in design)
|
|
check('standalone smoke only', 'Standalone은 smoke test' in design)
|
|
check('bson manifest', 'BSON 표현 Manifest' in design)
|
|
check('transaction retry separation', 'Transaction 본문 Retry와 Commit Retry를 분리' in design)
|
|
check('change stream at least once', 'at-least-once projector' in design)
|
|
check('ttl cleanup only', 'TTL은 물리 cleanup' in design)
|
|
check('gridfs compatibility only', 'GridFS는 compatibility adapter' in design)
|
|
check('driver native observability', 'Driver native ObservabilitySettings' in design)
|
|
|
|
for label, text, expected in [('stable', plan, 50), ('advanced', adv, 15)]:
|
|
nums = [int(x) for x in re.findall(r'^### Task (\d+):', text, re.M)]
|
|
check(f'{label} task count', len(nums) == expected, f'{len(nums)}')
|
|
check(f'{label} task sequence', nums == list(range(1, expected + 1)), str(nums[:3]) + '...' + str(nums[-3:]))
|
|
sections = re.split(r'(?=^### Task \d+:)', text, flags=re.M)[1:]
|
|
for idx, section in enumerate(sections, 1):
|
|
for marker in ['**Files:**', '**Interfaces:**', '**Implementation requirements:**',
|
|
'**Step 1:', '**Step 2:', '**Step 3:', '**Step 4:', '**Step 5:']:
|
|
check(f'{label} task {idx} has {marker}', marker in section)
|
|
check(f'{label} task {idx} has commit', 'git commit -m' in section)
|
|
check(f'{label} task {idx} has expected result', 'Expected:' in section)
|
|
|
|
for name, text in texts.items():
|
|
check(f'{name} code fences balanced', text.count('```') % 2 == 0, str(text.count('```')))
|
|
check(f'{name} no TODO markers', not re.search(r'\b(TODO|TBD|FIXME)\b', text))
|
|
check(f'{name} no wildcard create paths', not re.search(r'- Create: `[^`]*[\*?][^`]*`', text))
|
|
|
|
for label, text in [('stable', plan), ('advanced', adv)]:
|
|
created = re.findall(r'- Create: `([^`]+)`', text)
|
|
duplicates = sorted({p for p in created if created.count(p) > 1})
|
|
check(f'{label} no duplicate create paths', not duplicates, ', '.join(duplicates))
|
|
|
|
stable_created = set(re.findall(r'- Create: `([^`]+)`', plan))
|
|
advanced_created = set(re.findall(r'- Create: `([^`]+)`', adv))
|
|
check('stable and advanced create paths do not collide', not (stable_created & advanced_created),
|
|
', '.join(sorted(stable_created & advanced_created)))
|
|
check('no real generic repository declaration', not re.search(r'public\s+interface\s+(Common|Generic)MongoRepository', design + plan + adv))
|
|
check('no public arbitrary run command', not re.search(r'public\s+[^\n]+\s+runCommand\s*\(', design + plan + adv))
|
|
check('stable starter excludes advanced', 'no advanced module' in plan.lower() and 'Stable Starter' in adv)
|
|
check('unknown commit body retry forbidden', 'UnknownTransactionCommitResult' in plan and '업무 본문을 재실행하지 않는다' in plan)
|
|
check('mongo seven and eight matrix', 'MongoDB 7.0' in plan and 'MongoDB 8.0' in plan)
|
|
check('advanced actual topology gate', 'actual topology' in adv.lower() or '실제 topology' in adv)
|
|
|
|
failed = [c for c in checks if not c[1]]
|
|
for name, ok, detail in checks:
|
|
print(('PASS' if ok else 'FAIL') + ' | ' + name + ((' | ' + detail) if detail else ''))
|
|
print(f'SUMMARY | total={len(checks)} pass={len(checks)-len(failed)} fail={len(failed)}')
|
|
sys.exit(1 if failed else 0)
|