feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具
落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,24 +1,89 @@
|
||||
package portrait
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBuildDeterministic(t *testing.T) {
|
||||
birth := time.Date(1990, 1, 15, 0, 0, 0, 0, time.UTC)
|
||||
func TestCalcMatchesClassicJS(t *testing.T) {
|
||||
// Snapshot from js/common.js calc(1990,5,12)
|
||||
v := Calc(1990, 5, 12)
|
||||
if v.O != 9 || v.M != 8 || v.N != 1 || v.I != 3 || v.J != 5 || v.K != 1 || v.L != 9 {
|
||||
t.Fatalf("triangle mismatch: %+v", v)
|
||||
}
|
||||
if v.Q != 1 || v.P != 8 || v.R != 9 || v.T != 2 || v.S != 4 || v.U != 6 {
|
||||
t.Fatalf("outer mismatch: %+v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDecodeShape(t *testing.T) {
|
||||
birth := time.Date(1990, 5, 12, 0, 0, 0, 0, time.UTC)
|
||||
a := Build(birth, "小愈")
|
||||
b := Build(birth, "小愈")
|
||||
if a.Summary["headline"] != b.Summary["headline"] {
|
||||
t.Fatalf("expected deterministic headline")
|
||||
}
|
||||
if a.Summary["one_liner"] == nil || a.Detail["growth_direction"] == nil {
|
||||
t.Fatalf("missing summary/detail fields")
|
||||
if a.Summary["main_number"].(int) != 9 {
|
||||
t.Fatalf("main_number want 9 got %#v", a.Summary["main_number"])
|
||||
}
|
||||
for _, kw := range []string{"运势", "吉凶", "算命"} {
|
||||
s := a.Summary["one_liner"].(string) + a.Detail["behavior_pattern"].(string)
|
||||
if strings.Contains(s, kw) {
|
||||
if a.Summary["person_title"] == nil || a.Summary["pyramid"] == nil {
|
||||
t.Fatalf("missing decode free fields")
|
||||
}
|
||||
wx, ok := a.Summary["wuxing"].(map[string]any)
|
||||
if !ok || wx["bars"] == nil || wx["primary"] == nil {
|
||||
t.Fatalf("missing wuxing summary: %#v", a.Summary["wuxing"])
|
||||
}
|
||||
if a.Summary["today_tip"] == nil {
|
||||
t.Fatalf("missing today_tip")
|
||||
}
|
||||
nd, ok := a.Detail["number_deep"].(map[string]any)
|
||||
if !ok || nd["talent"] == nil || nd["joints"] == nil {
|
||||
t.Fatalf("missing number_deep")
|
||||
}
|
||||
cd, ok := a.Detail["constitution_deep"].(map[string]any)
|
||||
if !ok || cd["body"] == nil || cd["food"] == nil {
|
||||
t.Fatalf("missing constitution_deep")
|
||||
}
|
||||
dims, ok := a.Summary["dimensions"].([]map[string]any)
|
||||
if !ok || len(dims) < 6 {
|
||||
t.Fatalf("expected 6 dimensions for relation compat")
|
||||
}
|
||||
assertNoForbidden(t, a)
|
||||
}
|
||||
|
||||
func TestWuxingDeterministic(t *testing.T) {
|
||||
bz := getBazi(1990, 5, 12, 12)
|
||||
p := calc5E(bz)
|
||||
// Snapshot aligned with js calc5E(getBazi(new Date(1990,4,12),12))
|
||||
want := map[string]int{"木": 25, "火": 50, "土": 13, "金": 13, "水": 0}
|
||||
for k, w := range want {
|
||||
if p[k] != w {
|
||||
t.Fatalf("wuxing[%s]=%d want %d (full %#v)", k, p[k], w, p)
|
||||
}
|
||||
}
|
||||
if strongestEl(p) != "火" {
|
||||
t.Fatalf("strong want 火 got %s", strongestEl(p))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllMainNumbersLexicon(t *testing.T) {
|
||||
for day := 1; day <= 28; day++ {
|
||||
out := Build(time.Date(1990, 3, day, 0, 0, 0, 0, time.UTC), "测")
|
||||
assertNoForbidden(t, out)
|
||||
}
|
||||
}
|
||||
|
||||
func assertNoForbidden(t *testing.T, out Output) {
|
||||
t.Helper()
|
||||
raw, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blob := string(raw)
|
||||
for _, kw := range []string{"占卜", "算命", "改命", "塔罗"} {
|
||||
if strings.Contains(blob, kw) {
|
||||
t.Fatalf("forbidden word %q in portrait copy", kw)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user