package billcost import ( "context" "encoding/json" "testing" ) // TestReflectTool_Pass valid output triggers pass=true with empty // violations. // // TestReflectTool_Pass 合法输出 pass=true violations 空. func TestReflectTool_Pass(t *testing.T) { tool := &ReflectTool{Cfg: ReflectConfig{ MaxWeightG: 50000, }} out := Output{Bands: []Band{{ Partition: "北京", LimitBottom: 0, LimitTop: 50000, BaseWeight: 1000, BaseAmount: 6.0, IncrementWeight: 1000, IncrementPrice: 3.0, }}} in := reflectToolInput{Output: out} raw, err := json.Marshal(in) if err != nil { t.Fatalf("marshal input: %v", err) } res, err := tool.Execute(context.Background(), raw, nil) if err != nil { t.Fatalf("Execute: %v", err) } if res.IsError { t.Errorf("expected no error, got IsError=true: %s", res.Output) } data, ok := res.Data.(reflectToolOutput) if !ok { t.Fatalf("Data type: want reflectToolOutput, got %T", res.Data) } if !data.Pass { t.Errorf("Pass: want true, got false (violations: %v)", data.Violations) } if data.ViolationCount != 0 { t.Errorf("ViolationCount: want 0, got %d", data.ViolationCount) } } // TestReflectTool_Fail invalid output (reverse-logic) triggers // pass=false with violations and structured Data. // // TestReflectTool_Fail 反逻辑输出 pass=false 含 violations + 结构化 // Data. func TestReflectTool_Fail(t *testing.T) { tool := &ReflectTool{Cfg: ReflectConfig{ MaxWeightG: 50000, }} // Reverse logic: first 3元/kg vs continuation 6元/kg. out := Output{Bands: []Band{{ Partition: "北京", LimitBottom: 0, LimitTop: 50000, BaseWeight: 1000, BaseAmount: 3.0, IncrementWeight: 1000, IncrementPrice: 6.0, }}} in := reflectToolInput{Output: out} raw, err := json.Marshal(in) if err != nil { t.Fatalf("marshal input: %v", err) } res, err := tool.Execute(context.Background(), raw, nil) if err != nil { t.Fatalf("Execute: %v", err) } data := res.Data.(reflectToolOutput) if data.Pass { t.Errorf("Pass: want false, got true") } if data.ViolationCount == 0 { t.Errorf("ViolationCount: want > 0, got 0") } hasReverse := false for _, v := range data.Violations { if v.RuleID == RuleFirstLessIncrement { hasReverse = true break } } if !hasReverse { t.Errorf("expected first_lt_increment violation, got %+v", data.Violations) } } // TestReflectTool_BadInput malformed JSON returns IsError=true. // // TestReflectTool_BadInput JSON 解析错返 IsError=true. func TestReflectTool_BadInput(t *testing.T) { tool := &ReflectTool{} res, err := tool.Execute(context.Background(), json.RawMessage(`{not json`), nil) if err != nil { t.Fatalf("Execute: %v", err) } if !res.IsError { t.Errorf("expected IsError=true on bad input, got false") } } // TestReflectTool_Metadata covers the tool's metadata declaration. // // TestReflectTool_Metadata 验工具元数据声明. func TestReflectTool_Metadata(t *testing.T) { tool := &ReflectTool{} if tool.Name() != "billcost_reflect" { t.Errorf("Name: want billcost_reflect, got %s", tool.Name()) } m := tool.Metadata() if !m.ConcurrencySafe { t.Error("ConcurrencySafe: want true, got false") } if !m.ReadOnly { t.Error("ReadOnly: want true, got false") } if m.Destructive { t.Error("Destructive: want false, got true") } if m.PermissionClass != "readonly" { t.Errorf("PermissionClass: want readonly, got %s", m.PermissionClass) } desc := tool.Description(context.Background()) if len(desc) < 100 { t.Errorf("Description too short: %d chars", len(desc)) } schema := tool.InputSchema() if len(schema) == 0 { t.Error("InputSchema: want non-empty, got empty") } }