Files
2026-08-14 01:16:50 +08:00

163 lines
5.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"math"
"testing"
)
func ptr(v float64) *float64 { return &v }
func TestComputeRiskScoreWeights(t *testing.T) {
got := ComputeRiskScore(RiskInput{AI: ptr(1), Env: ptr(1), Stage: ptr(1), Uniformity: ptr(1)})
if got.Score != 100 {
t.Errorf("全 1 应得 100,实际 %.2f", got.Score)
}
if len(got.Missing) != 0 {
t.Errorf("全组件可用时不应有 missing,实际 %v", got.Missing)
}
if got.Components["aiAbnormalProbability"] == nil || got.Components["uniformity"] == nil {
t.Errorf("全组件可用时 components 不应为 null: %v", got.Components)
}
}
func TestComputeRiskScoreNormalizesMissingComponents(t *testing.T) {
got := ComputeRiskScore(RiskInput{AI: ptr(1)})
if got.Score != 100 {
t.Errorf("仅 AI=1 归一化后应得 100,实际 %.2f", got.Score)
}
if len(got.Missing) != 3 {
t.Errorf("missing 应为 environment/stage/uniformity,实际 %v", got.Missing)
}
if got.Components["environment"] != nil {
t.Error("缺失 environment 应序列化为 null")
}
}
func TestHealthyHighConfidenceDoesNotIncreaseRisk(t *testing.T) {
got := AbnormalProbability([]AIDetection{{ClassName: "healthy", Confidence: .95}})
if got != 0 {
t.Fatalf("healthy 高置信度不应产生异常概率,实际 %v", got)
}
assessment := ComputeRiskScore(RiskInput{AI: ptr(got)})
if assessment.Score != 0 {
t.Fatalf("healthy 高置信度风险分应为 0,实际 %.2f", assessment.Score)
}
}
func TestSickHighConfidenceIncreasesRisk(t *testing.T) {
got := AbnormalProbability([]AIDetection{{ClassName: "sick", Confidence: .9}})
if got != .9 {
t.Fatalf("sick 高置信度异常概率应为 0.9,实际 %v", got)
}
assessment := ComputeRiskScore(RiskInput{AI: ptr(got)})
if assessment.Score != 90 {
t.Fatalf("仅 AI=0.9 归一化后风险分应为 90,实际 %.2f", assessment.Score)
}
}
func TestAbnormalProbabilityIgnoresUnknown(t *testing.T) {
if got := AbnormalProbability([]AIDetection{{ClassName: "unknown", Confidence: .9}}); got != 0 {
t.Errorf("unknown 不应贡献异常概率,实际 %v", got)
}
}
func TestComputeRiskScoreMissingAll(t *testing.T) {
got := ComputeRiskScore(RiskInput{})
if got.Score != 0 {
t.Errorf("无任何组件时分数应为 0,实际 %.2f", got.Score)
}
if got.Confidence != "unknown" {
t.Errorf("无 AI 组件时 confidence 应为 unknown,实际 %s", got.Confidence)
}
if len(got.Missing) != 4 {
t.Errorf("missing 应为 4 项,实际 %v", got.Missing)
}
}
func TestComputeRiskScoreClamps(t *testing.T) {
got := ComputeRiskScore(RiskInput{AI: ptr(2), Env: ptr(2), Stage: ptr(2), Uniformity: ptr(2)})
if got.Score > 100 {
t.Errorf("应钳制到 100,实际 %.2f", got.Score)
}
if got := ComputeRiskScore(RiskInput{AI: ptr(-1)}).Score; got < 0 {
t.Errorf("应钳制到 0,实际 %.2f", got)
}
}
func TestRiskConfidenceLowMediumUnknown(t *testing.T) {
if got := RiskConfidence(RiskInput{}); got != "unknown" {
t.Errorf("无 AI 应为 unknown,实际 %s", got)
}
if got := RiskConfidence(RiskInput{AI: ptr(0.2)}); got != "low" {
t.Errorf("低异常概率应为 low,实际 %s", got)
}
if got := RiskConfidence(RiskInput{AI: ptr(0.9)}); got != "medium" {
t.Errorf("高异常概率当前最多应为 medium,实际 %s", got)
}
}
func TestComputeRiskScoreBoundaryUsesWeightNormalization(t *testing.T) {
// 0.5*0.8 + 0.2*0.5 的可用权重为 0.70.5/0.7 = 71.43
got := ComputeRiskScore(RiskInput{AI: ptr(0.8), Env: ptr(0.5)})
want := 50.0 / 0.7
if math.Abs(got.Score-want) > 0.01 {
t.Errorf("归一化应得 %.2f,实际 %.2f", want, got.Score)
}
}
func TestRiskLevelBoundaries(t *testing.T) {
cases := map[float64]string{
0: "green",
30: "green",
30.5: "yellow",
60: "yellow",
61: "orange",
80: "orange",
81: "red",
100: "red",
}
for score, want := range cases {
if got := RiskLevel(score); got != want {
t.Errorf("RiskLevel(%.1f) = %s, want %s", score, got, want)
}
}
}
func TestStageCoefficientMapping(t *testing.T) {
if StageCoefficient("pupa") <= 0 {
t.Error("蛹期应有风险系数")
}
if StageCoefficient("") != 0 {
t.Error("空阶段应为 0")
}
if StageCoefficient("unknown") != 0 {
t.Error("未知阶段应为 0")
}
if StageCoefficient("pupa") <= StageCoefficient("egg") {
t.Error("蛹期系数应高于卵期")
}
}
func TestEnvCoefficientRules(t *testing.T) {
// 湿度 >=80 → 高(真菌病)
if c := EnvCoefficient(ptr(25), ptr(85)); c < 0.7 {
t.Errorf("湿度 85 应 ≥0.7,实际 %.2f", c)
}
// 湿度 75-80 → 中
if c := EnvCoefficient(ptr(25), ptr(78)); c < 0.3 {
t.Errorf("湿度 78 应 ≥0.3,实际 %.2f", c)
}
// 温度突变 >30 → 中(核型多角体病诱发)
if c := EnvCoefficient(ptr(32), ptr(60)); c < 0.3 {
t.Errorf("温度 32 应 ≥0.3,实际 %.2f", c)
}
// 舒适环境 → 0
if c := EnvCoefficient(ptr(25), ptr(60)); c != 0 {
t.Errorf("舒适环境应为 0,实际 %.2f", c)
}
// 缺数据 → 0
if c := EnvCoefficient(nil, nil); c != 0 {
t.Errorf("无数据应为 0,实际 %.2f", c)
}
}