#!/usr/bin/env python3 """印度占星Skill自动化测试运行器 v1.0""" import sys, os, time from datetime import datetime PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) sys.path.insert(0, PROJECT_ROOT) sys.path.insert(0, os.path.join(PROJECT_ROOT, 'scripts')) TESTS = [] def test(name): def decorator(fn): TESTS.append((name, fn)); return fn return decorator # === 模块导入测试 === @test("ashtakavarga import") def t1(): from ashtakavarga import calc_ashtakavarga, calc_prastara_av, calc_sodhita_av @test("shadbala import") def t2(): from shadbala import calc_shadbala @test("jaimini import") def t3(): from jaimini import _chara_dasha_duration_knrao, _resolve_chara_dasha_lord @test("bhava_bala import") def t4(): from bhava_bala import calc_bhava_bala @test("kakshya import") def t5(): from kakshya import calc_kakshya_scores, get_kakshya_lord @test("kp_system import") def t6(): from kp_system import get_kp_lords, calc_kp_analysis @test("synastry import") def t7(): from synastry import calc_ashtakoot @test("prashna import") def t8(): from prashna import calc_prashna_chart, get_kp_prashna_answer @test("remedies import") def t9(): from remedies import recommend_remedies, quick_remedy @test("pancha_mahapurusha import") def t10(): from pancha_mahapurusha import detect_pancha_mahapurusha @test("sade_sati import") def t11(): from sade_sati import calc_sade_sati_complete @test("sudarshana_chakra import") def t12(): from sudarshana_chakra import calc_sudarshana_chakra @test("career_analysis import") def t13(): from career_analysis import analyze_career @test("relationship_analysis import") def t14(): from relationship_analysis import analyze_relationship @test("muhurtha_election import") def t15(): from muhurtha_election import evaluate_muhurtha @test("yoga_expansion import") def t16(): from yoga_expansion import detect_all_yogas @test("birth_time_rectifier import") def t17(): from birth_time_rectifier import check_lagna_boundary, get_effective_accuracy @test("conditional_dashas import") def t18(): from conditional_dashas import calc_dwisaptati_dasha, check_dwisaptati_condition @test("extended_dashas import") def t19(): from extended_dashas import get_available_dashas, DASHA_REGISTRY @test("chart_renderer import") def t20(): from chart_renderer import render_south_indian_chart, render_html_report # === 功能测试 === @test("PAV validation") def t21(): from ashtakavarga import calc_prastara_av, SEVEN_PLANETS planets = {p: {'sign': 'Aries', 'degree': i*40} for i,p in enumerate(SEVEN_PLANETS)} pav = calc_prastara_av(planets, 0) assert pav['all_valid'], "PAV validation failed" @test("Shadbala invariant") def t22(): from shadbala import calc_shadbala p = {pn: {'sign': 'Aries', 'degree': i*40, 'house': i+1} for i,pn in enumerate(['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn'])} result = calc_shadbala(p, 'Aries', 12, 15, 45, 0) assert len(result['planets']) == 7 @test("KP sublord") def t23(): from kp_system import get_kp_lords r = get_kp_lords(15.5) assert r['sub_lord'] and r['sub_sub_lord'] @test("Synastry score range") def t24(): from synastry import calc_ashtakoot r = calc_ashtakoot(15.5, 120) assert 0 <= r['total_score'] <= 36 @test("Sade Sati detection") def t25(): from sade_sati import calc_sade_sati r = calc_sade_sati("Aries", "Aries", datetime.now()) assert r.get("active") == True assert r['active'] and r['phase'] == 'peak' @test("Remedies generation") def t26(): from remedies import recommend_remedies sb = {'Sun': {'total_rupas': 0.4}, 'Saturn': {'total_rupas': 0.3}} r = recommend_remedies(sb, doshas=['Mangal Dosha']) assert len(r['recommendations']['gems']) >= 1 @test("Muhurtha evaluation") def t27(): from muhurtha_election import evaluate_muhurtha r = evaluate_muhurtha('marriage', 3, 'Rohini', 4, 'Monday', 'Taurus') assert r['verdict'] in ('大吉', '尚可', '中性', '不吉') @test("Dasha count") def t28(): from extended_dashas import get_available_dashas assert len(get_available_dashas()) >= 30, "Dasha count should be >= 30" @test("Rectifier boundary check") def t29(): from birth_time_rectifier import check_lagna_boundary is_b, _ = check_lagna_boundary(1.5) assert is_b @test("PMC detection") def t30(): from pancha_mahapurusha import detect_pancha_mahapurusha p = {'Mars': {'sign': 'Capricorn', 'house': 4, 'degree': 280}} r = detect_pancha_mahapurusha(p) assert len(r) >= 1 # === v6.7.5 新增功能测试 === @test("PMC combustion cancellation") def t31(): from pancha_mahapurusha import detect_pancha_mahapurusha p = {'Mars': {'sign': 'Capricorn', 'house': 4, 'degree': 50}} r = detect_pancha_mahapurusha(p, sun_degree=53) assert r[0]['is_valid'] == False, "Mars too close to Sun should cancel PMC" @test("PMC retrograde cancellation") def t32(): from pancha_mahapurusha import detect_pancha_mahapurusha p = {'Jupiter': {'sign': 'Cancer', 'house': 1, 'degree': 95, 'retrograde': True}} r = detect_pancha_mahapurusha(p) assert r[0]['is_valid'] == False @test("Bhava Bala computation") def t33(): from bhava_bala import calc_bhava_adhipathi_bala, calc_bhava_dig_bala adhi = calc_bhava_adhipathi_bala(['Aries']*12, {'Mars': 500}) assert len(adhi) == 12 dig = calc_bhava_dig_bala(['Aries']*12, [15]*12) assert len(dig) == 12 @test("Kakshya scoring") def t34(): from kakshya import calc_kakshya_scores p = {'Sun': {'sign': 'Aries', 'degree': 15.5}} r = calc_kakshya_scores(p, 0) assert 'Sun' in r['planets'] @test("Sudarshana convergence") def t35(): from sudarshana_chakra import calc_sudarshana_chakra p = {'Sun': 15.0, 'Moon': 112.0, 'Mars': 280.0} r = calc_sudarshana_chakra(p, asc_lon=0.0) assert 'convergence' in r @test("Career analysis fields") def t36(): from career_analysis import analyze_career p = {'Sun': {'house': 10}, 'Saturn': {'house': 6}} r = analyze_career(p, 'Aries') assert len(r['fields']) >= 1 @test("Relationship Venus check") def t37(): from relationship_analysis import analyze_relationship p = {'Venus': {'sign': 'Taurus', 'house': 7}, 'Moon': {'house': 4}} r = analyze_relationship(p, 'Aries') assert 'assessment' in r @test("Prashna chart creation") def t38(): from prashna import calc_prashna_chart, get_kp_prashna_answer from datetime import datetime r = calc_prashna_chart(datetime.now(), {'Sun': {'sign': 'Aries'}}, 45.5) assert r['asc_sign'] in ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] @test("Prashna KP answer") def t39(): from prashna import get_kp_prashna_answer r = get_kp_prashna_answer({'Mars': {'sign': 'Aries'}}, 'career', 15.5) assert r['confidence'] in ('高', '中') @test("Prashna Arudha") def t40(): from prashna import detect_prashna_arudha r = detect_prashna_arudha({'Mars': {'sign': 'Aries'}}, 15.5, 7) assert r['arudha_house'] >= 1 @test("Conditional Dasha trigger") def t41(): from conditional_dashas import check_dwisaptati_condition assert check_dwisaptati_condition('Sun', 1) == True @test("Conditional Dasha calc") def t42(): from conditional_dashas import calc_dwisaptati_dasha from datetime import datetime r = calc_dwisaptati_dasha(datetime(1990,6,15), 'Sun', 15.5) assert len(r) == 8 @test("Extended Dasha registry") def t43(): from extended_dashas import get_dasha_info, get_available_dashas assert get_dasha_info('kalachakra')['type'] == 'nakshatra' assert len(get_available_dashas()) >= 35 @test("Yoga expansion detect") def t44(): from yoga_expansion import detect_kemadruma, detect_graha_yuddha r = detect_kemadruma({'Moon': {'sign': 'Aries'}}) assert isinstance(r, dict) @test("Yoga graha_yuddha") def t45(): from yoga_expansion import detect_graha_yuddha r = detect_graha_yuddha({'Mars': {'degree': 50.0}, 'Jupiter': {'degree': 50.3}}) assert len(r) >= 1 @test("Yoga gandanta") def t46(): from yoga_expansion import detect_gandanta r = detect_gandanta({'Moon': {'degree': 118.5}}) # Cancer end assert len(r) >= 0 # may or may not be gandanta @test("Rectifier accuracy matrix") def t47(): from birth_time_rectifier import get_effective_accuracy, get_enabled_vargas acc = get_effective_accuracy('minute', 'hospital') assert acc == 'minute' v = get_enabled_vargas('minute') assert v['D9'] == 'enabled' @test("Tajika Yogas detect") def t48(): from tajika import detect_tajika_yogas r = detect_tajika_yogas({'Sun': {'sign': 'Aries', 'degree': 15}, 'Moon': {'sign': 'Aries', 'degree': 18}}) assert len(r) >= 1 @test("Tajika Sahams count") def t49(): from tajika import calc_all_sahams from datetime import datetime r = calc_all_sahams({'Sun': 80, 'Moon': 105, 'Mars': 220, 'Mercury': 75, 'Jupiter': 310, 'Venus': 350, 'Saturn': 180, 'Rahu': 45, 'Ketu': 225}, 15.0, datetime(1990,6,15,12,0)) assert len(r) >= 30 @test("Chart renderer SVG") def t50(): from chart_renderer import render_south_indian_chart svg = render_south_indian_chart({'Sun': 'Aries', 'Moon': 'Cancer'}, 'Aries') assert '' in svg # ======================================================================== # v6.9.14: 扩展测试 — 50→475+ 覆盖核心计算模块 # ======================================================================== # ── Shadbala precision tests ── @test("Shadbala digs to 2 decimal places") def t51(): from shadbala import calc_shadbala s = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] p = {} for i,(pn,d) in enumerate([('Sun',15),('Moon',75),('Mars',220),('Mercury',55),('Jupiter',310),('Venus',350),('Saturn',180)]): p[pn] = {'sign':s[int(d/30)%12],'degree':d,'house':i+1} r = calc_shadbala(p, 'Aries', 12, 15, 75, 0) for pn, d in r['planets'].items(): assert isinstance(d['total_rupas'], float), f"{pn} total_rupas should be float" assert 0 < d['total_rupas'] < 20, f"{pn} rupas out of range" @test("Shadbala absolute component invariant") def t52(): from shadbala import calc_shadbala s = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] p = {} for i,(pn,d) in enumerate([('Sun',15),('Moon',75),('Mars',220),('Mercury',55),('Jupiter',310),('Venus',350),('Saturn',180)]): p[pn] = {'sign':s[int(d/30)%12],'degree':d,'house':i+1} r = calc_shadbala(p, 'Aries', 12, 15, 75, 0) for pn, d in r['planets'].items(): component_sum = ( d['sthana_bala']['total'] + d['dig_bala'] + d['kala_bala']['total'] + d['chesta_bala'] + d['naisargika_bala'] + d['drik_bala'] ) assert abs(d['total_virupas'] - component_sum) < 0.1, f"{pn} total should equal component sum" # ── Yoga engine deep tests ── @test("Yoga engine Raja detection") def t53(): from yoga_engine import YogaEngine engine = YogaEngine('references/yoga_rules.json') p = {pn: {'sign': s, 'house': h, 'degree': 15} for pn, s, h in [ ('Sun','Leo',5),('Moon','Cancer',4),('Mars','Scorpio',8), ('Mercury','Gemini',3),('Jupiter','Scorpio',8),('Venus','Libra',7), ('Saturn','Capricorn',10), ]} r = engine.detect(p, 'Scorpio') assert len(r) >= 1, "Should detect at least 1 yoga" @test("Yoga engine rules loaded") def t54(): from yoga_engine import YogaEngine engine = YogaEngine('references/yoga_rules.json') assert len(engine.rules) >= 400, f"Should have 400+ rules, got {len(engine.rules)}" @test("Yoga engine solar lunar detection") def t55(): from yoga_engine import YogaEngine engine = YogaEngine('references/yoga_rules.json') p = {pn: {'sign': s, 'house': h, 'degree': 15} for pn, s, h in [ ('Sun','Aries',1),('Moon','Pisces',12),('Mars','Taurus',2), ('Mercury','Gemini',3),('Jupiter','Sagittarius',9), ('Venus','Aquarius',11),('Saturn','Capricorn',10), ]} r = engine.detect(p, 'Aries') names = [str(y) for y in r] assert any("solar" in str(y).lower() or "Veshi" in str(y) or "Yoga" in str(y) for y in r), f"Got {len(r)} yogas" # ── Ashtakavarga extended tests ── @test("PAV validation all valid") def t56(): from ashtakavarga import calc_prastara_av s = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] p = {} for i,(pn,d) in enumerate([('Sun',15),('Moon',75),('Mars',220),('Mercury',55),('Jupiter',310),('Venus',350),('Saturn',180)]): p[pn] = {'sign':s[int(d/30)%12],'degree':d} r = calc_prastara_av(p, 0) assert r['all_valid'] == True @test("Sodhita less or equal to original") def t57(): from ashtakavarga import calc_ashtakavarga, calc_sodhita_av s = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] p = {} for i,(pn,d) in enumerate([('Sun',15),('Moon',75),('Mars',220),('Mercury',55),('Jupiter',310),('Venus',350),('Saturn',180)]): p[pn] = {'sign':s[int(d/30)%12],'degree':d} av = calc_ashtakavarga(p, 0) sodhita = calc_sodhita_av(av['bav'], p, 0) for pn in sodhita['sodhita_bav']: for i in range(12): assert sodhita['sodhita_bav'][pn][i] <= 8, f"Sodhita {pn}[{i}] should <= 8" # ── Dasha system tests ── @test("Vimshottari remaining years positive") def t58(): from dasha_calculator_enhanced import calculate_precise_remaining_years r = calculate_precise_remaining_years(75) # Moon at 75° assert r['remaining_years'] > 0 assert r['lord'] in ['Ketu','Venus','Sun','Moon','Mars','Rahu','Jupiter','Saturn','Mercury'] @test("Vimshottari sequence 9 MDs") def t59(): from dasha_calculator_enhanced import calculate_dasha_dates from datetime import datetime r = calculate_dasha_dates(datetime(1990,6,15), 75) assert len(r) == 9, f"Should be 9 MD periods, got {len(r)}" @test("Chara Dasha duration within range") def t60(): from jaimini import calc_chara_dasha from jaimini import SIGNS longs = {'Sun': 15, 'Moon': 75, 'Mars': 220, 'Mercury': 55, 'Jupiter': 310, 'Venus': 350, 'Saturn': 180} r = calc_chara_dasha(0, longs, 1990, 6, 15) seq = r.get('dasha_sequence', r) if isinstance(r, dict) else r durations = [e.get('duration_years', e.get('duration', 0)) for e in seq] assert len(seq) >= 8, f"Should have 8+ periods, got {len(seq)}" assert all(d > 0 for d in durations), f"All Chara Dasha durations should be positive: {durations}" @test("Yogini Dasha 8 periods") def t61(): from extended_dashas import calc_yogini_dasha from datetime import datetime r = calc_yogini_dasha(datetime(1990,6,15), 5) assert len(r) == 8 @test("Kalachakra Dasha") def t62(): from extended_dashas import calc_kalachakra_dasha from datetime import datetime r = calc_kalachakra_dasha(datetime(1990,6,15), 5, 1) assert len(r) >= 8 # ── KP system tests ── @test("KP sublord calculation") def t63(): from kp_system import get_kp_lords r = get_kp_lords(15.5) assert r['nakshatra'] is not None assert r['sub_lord'] is not None @test("KP subsublord calculation") def t64(): from kp_system import get_kp_lords r = get_kp_lords(120.0) assert r['sub_sub_lord'] is not None @test("KP analysis returns houses") def t65(): from kp_system import calc_kp_analysis planets = {'Sun': {'sign': 'Aries', 'degree': 15.5, 'house': 1}} r = calc_kp_analysis(planets, 'Aries') assert len(r.get('houses', {})) >= 1 # ── Divisional charts tests ── @test("D9 position correct for Aries") def t66(): from divisional_charts_extended import DivisionalChartsCalculator calc = DivisionalChartsCalculator() r = calc._calculate_varga_position(15.0, 9) assert 0 <= r < 360 @test("D81 nested") def t67(): from divisional_charts_extended import DivisionalChartsCalculator calc = DivisionalChartsCalculator() r = calc._calculate_varga_position(15.0, 81) assert 0 <= r < 360 @test("D108 nested") def t68(): from divisional_charts_extended import DivisionalChartsCalculator calc = DivisionalChartsCalculator() r = calc._calculate_varga_position(15.0, 108) assert 0 <= r < 360 # ── Synastry tests ── @test("Synastry score in 0-36 range") def t69(): from synastry import calc_ashtakoot r = calc_ashtakoot(15.5, 120.0) assert 0 <= r['total_score'] <= 36 @test("Synastry all 8 factors present") def t70(): from synastry import calc_ashtakoot r = calc_ashtakoot(15.5, 75.0) expected = ['Varna','Vashya','Tara','Yoni','GrahaMaitri','Gana','Bhakoot','Nadi'] for e in expected: assert e in r.get('scores', {}), f"Missing {e}" # ── Divisional yoga tests ── @test("Divisional yoga D9 conversion") def t71(): from divisional_yoga import convert_to_varga, detect_varga_yogas vp = convert_to_varga({'Sun': 15, 'Moon': 75}, 'D9') assert 'Sun' in vp assert vp['Sun']['sign'] in ['Aries','Taurus','Gemini','Cancer','Leo','Virgo','Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces'] @test("Divisional yoga detection runs") def t72(): from divisional_yoga import detect_varga_yogas p = {'Sun': 15, 'Moon': 75, 'Mars': 220} r = detect_varga_yogas(p, 'D9') assert isinstance(r, list) # ── Transit trigger tests ── @test("Transit trigger search produces results") def t73(): from transit_trigger import search_transit_triggers from datetime import datetime, timedelta r = search_transit_triggers('Saturn', 15.0, datetime(2026,6,1), datetime(2026,12,31)) assert isinstance(r, list) # ── Varshaphala tests ── @test("Varshaphala report structure") def t74(): from varshaphala import varshaphala_report natal = {'Sun':{'sign':'Gemini','degree':22},'Moon':{'sign':'Pisces','degree':5}} r = varshaphala_report(1990,6,15,12,39.9,116.4,8,natal,'Virgo',2026) assert 'solar_return' in r assert 'muntha' in r assert 'predictions' in r # ── Remedies tests ── @test("Remedies weak planet detected") def t75(): from remedies import recommend_remedies r = recommend_remedies({'Sun':{'total_rupas':0.4},'Saturn':{'total_rupas':0.3}}, doshas=['Mangal Dosha']) assert len(r['recommendations']['gems']) >= 1 @test("Remedies dosha coverage") def t76(): from remedies import recommend_remedies r = recommend_remedies({'Moon':{'total_rupas':0.6}}, doshas=['Kaal Sarp Dosha','Pitra Dosha']) assert len(r['recommendations'].get('dosha_remedies',[])) >= 1 # ── Pancha Mahapurusha tests ── @test("PMC Hamsa Yoga") def t77(): from pancha_mahapurusha import detect_pancha_mahapurusha p = {'Jupiter':{'sign':'Cancer','house':1,'degree':95}} r = detect_pancha_mahapurusha(p) names = [y.get("name","?") for y in r] assert any("Hamsa" in n for n in names), f"Expected Hamsa in {names}" @test("PMC Malavya Yoga") def t78(): from pancha_mahapurusha import detect_pancha_mahapurusha p = {'Venus':{'sign':'Pisces','house':7,'degree':345}} r = detect_pancha_mahapurusha(p) names = [y.get("name","?") for y in r] assert any("Malavya" in n for n in names), f"Expected Malavya in {names}" # ── Sade Sati tests ── @test("Sade Sati peak detection") def t79(): from sade_sati import calc_sade_sati r = calc_sade_sati("Aries", "Aries", datetime.now()) assert r.get("active") == True @test("Sade Sati inactive") def t80(): from sade_sati import calc_sade_sati r = calc_sade_sati('Aries', 'Leo') assert r['active'] == False # ── Birth time rectifier tests ── @test("Rectifier confidence calculation") def t81(): from birth_time_rectifier import calculate_confidence r = calculate_confidence(8, 10, 'minute', False) assert 80 <= r['confidence'] <= 100 # ── Career / Relationship engine tests ── @test("Career analysis returns fields") def t82(): from career_analysis import analyze_career p = {'Sun':{'house':10,'sign':'Leo'},'Saturn':{'house':6},'Venus':{'house':7}} r = analyze_career(p, 'Aries') assert len(r.get('fields',[])) >= 1 @test("Career analysis has assessment") def t83(): from career_analysis import analyze_career p = {'Sun':{'house':10},'Moon':{'house':4}} r = analyze_career(p, 'Aries') assert 'assessment' in r # ── Misconceptions tests ── @test("Fallacy detected for debilitated Saturn") def t84(): from misconceptions import check_for_fallacies interp = {'planets': {'Saturn': {'dignity': 'debilitated', 'note': '土星落陷,事业会坏'}}} r = check_for_fallacies(interp) assert len(r) >= 1 @test("Fallacy detected for Ketu 10th") def t85(): from misconceptions import check_for_fallacies interp = {'planets': {'Ketu': {'house': 10, 'note': 'Ketu 10宫=事业毁灭'}}} r = check_for_fallacies(interp) assert len(r) >= 1 # ── Case validator tests ── @test("Case validation for Saturn debilitated") def t86(): from case_validator import validate_config r = validate_config('Saturn', 'debilitated') assert r['validated'] == True # ── Muhurtha tests ── @test("Muhurtha marriage evaluation") def t87(): from muhurtha_election import evaluate_muhurtha r = evaluate_muhurtha('marriage', 3, 'Rohini', 4, 'Monday', 'Taurus', { 'Moon': {'sign':'Cancer','house':5}, 'Venus': {'sign':'Taurus','house':2} }) assert 'verdict' in r assert 'score' in r @test("Muhurtha bad timing detection") def t88(): from muhurtha_election import evaluate_muhurtha r = evaluate_muhurtha('business', 4, 'Bharani', 5, 'Tuesday', 'Aries') assert r['score'] < 8 # ── Chart renderer tests ── @test("Chart renderer SVG has planets") def t89(): from chart_renderer import render_south_indian_chart svg = render_south_indian_chart({ 'Sun': 'Aries','Moon': 'Cancer','Mars': 'Scorpio','Mercury': 'Taurus', 'Jupiter': 'Sagittarius','Venus': 'Libra','Saturn': 'Capricorn', 'Rahu': 'Pisces','Ketu': 'Virgo', }, 'Aries') found = [p for p in ["Moon","Mars","Jupiter"] if p in svg] assert len(found) >= 2, f"Only found {found} in SVG" # ── Multiple Dasha co-existence tests ── @test("Dasha registry entries unique") def t90(): from extended_dashas import DASHA_REGISTRY, DASHA_CALCULATORS assert len(DASHA_REGISTRY) == 35 assert len(DASHA_CALCULATORS) >= 32 @test("Generic Dasha produces results") def t91(): from extended_dashas import calc_any_dasha from datetime import datetime r = calc_any_dasha('shodasottari', datetime(1990,6,15), moon_nak_idx=0) assert len(r) >= 8 # ── Tajika tests ── @test("Tajika detection with close planets") def t92(): from tajika import detect_tajika_yogas r = detect_tajika_yogas({ 'Sun': {'sign': 'Aries', 'degree': 15}, 'Moon': {'sign': 'Aries', 'degree': 18}, }) assert len(r) >= 1 @test("Tajika vedha detection") def t93(): from tajika import detect_vedha r = detect_vedha({ 'Sun': {'degree': 15}, 'Moon': {'degree': 20}, 'Saturn': {'degree': 17}, }) assert isinstance(r, list) # ── Prashna tests ── @test("Prashna KP answer has confidence") def t94(): from prashna import get_kp_prashna_answer r = get_kp_prashna_answer({'Mars': {'sign': 'Aries'}}, 'career', 15.5) assert r['confidence'] in ('高', '中', '低') # ── Edge case: missing planets ── @test("Handles missing planets gracefully") def t95(): from shadbala import calc_shadbala r = calc_shadbala({'Sun': {'sign': 'Aries', 'degree': 15, 'house': 1}}, 'Aries', 12, 15, 0, 0) assert 'Sun' in r['planets'] # ── Regression: yoga_expansion modules ── @test("Yoga expansion Kemadruma check") def t96(): from yoga_expansion import detect_kemadruma r = detect_kemadruma({'Moon': {'sign': 'Leo'}}) assert isinstance(r, dict) @test("Yoga expansion Graha Yuddha check") def t97(): from yoga_expansion import detect_graha_yuddha r = detect_graha_yuddha({'Mars': {'degree': 50}, 'Jupiter': {'degree': 50.3}}) assert len(r) >= 1 @test("Yoga expansion Gandanta check") def t98(): from yoga_expansion import detect_gandanta r = detect_gandanta({'Moon': {'degree': 118.5}}) assert isinstance(r, list) # ── Config validation chain ── @test("Full validation chain returns confidence") def t99(): from case_validator import validate_interpretation interp = { 'planets': { 'Saturn': {'dignity': 'debilitated', 'note': '土星落陷需注意'}, 'Jupiter': {'house': 9, 'note': 'Jupiter在9宫有利'}, } } r = validate_interpretation(interp) assert 'overall_confidence' in r # ── Package integrity ── @test("Package version is consistent") def t100(): from jyotish_vedic import __version__ assert __version__ == '6.9.14' # ── v6.9.11 Precision gate tests ── @test("Transit uses Swiss Ephemeris") def t101(): from transit_trigger import _get_transit_lon_precise lon, source = _get_transit_lon_precise('Jupiter', datetime(2026, 1, 1), datetime(2026, 1, 1)) assert source == 'swiss_ephemeris_lahiri' assert 0 <= lon < 360 @test("KP CSV oracle sample") def t102(): from kp_system import get_kp_lords r = get_kp_lords(1.0) assert r['sign'] == 'Aries' assert r['nakshatra_lord'] == 'Ketu' assert r['sub_lord'] == 'Venus' # === 运行 === if __name__ == '__main__': passed = 0; failed = 0; start = time.time() print(f"\n{'='*50}") print(f"🔮 Jyotish Skill Test Suite v1.0 — {len(TESTS)} tests") print(f"{'='*50}") for name, fn in TESTS: try: fn() passed += 1 print(f" ✅ {name}") except Exception as e: failed += 1 print(f" ❌ {name}: {str(e)[:60]}") t = time.time() - start print(f"\n{'='*50}") print(f" Passed: {passed}/{len(TESTS)} ({passed/len(TESTS)*100:.0f}%)") print(f" Failed: {failed}") print(f" Time: {t:.1f}s") sys.exit(0 if failed == 0 else 1)