tools_test.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package tools
  2. import (
  3. "testing"
  4. "fwh-toolbox-server/domain"
  5. "fwh-toolbox-server/dto"
  6. )
  7. func TestRecognizeMode(t *testing.T) {
  8. cases := []struct {
  9. solar, load string
  10. params map[string]any
  11. want string
  12. issues bool
  13. }{
  14. {"1,2,0", "1,3,2", nil, "A", false},
  15. {"2,5,0", "2,9,5", nil, "A", false}, // 归一化后匹配
  16. {"1,2,0", "1,2,0", nil, "B", false},
  17. {"1,3,2", "1,2,0", nil, "C", false},
  18. {"1,3,2", "1,3,2", nil, "D", false},
  19. {"1,2,3", "1,2,0", nil, "E/G", false},
  20. {"1,2,0", "1,2,3", nil, "F/H", false},
  21. {"9,9,9", "9,9,9", map[string]any{"gridDischargeMax": 0.1, "gridFeedMax": 0.1}, "Zero Import", false},
  22. {"9,9,9", "9,9,9", map[string]any{"gridFeedMax": 5.0}, "Zero Import", true},
  23. {"5,5,5", "5,5,5", nil, "自定义", false}, // 归一化为 1,1,1,不是 Zero Import
  24. {"", "", nil, "未设置", false},
  25. {"", "", map[string]any{"chargePower": 8.0}, "强制充电", false},
  26. {"", "", map[string]any{"dischargePower": 10.0}, "强制放电", false},
  27. {"", "", map[string]any{"chargePower": 8.0, "dischargePower": 10.0}, "自定义", true},
  28. {"1,2,0", "", nil, "自定义", true},
  29. }
  30. for _, c := range cases {
  31. got, issues := RecognizeMode(c.solar, c.load, c.params)
  32. if got != c.want || (len(issues) > 0) != c.issues {
  33. t.Errorf("RecognizeMode(%q,%q)=%s issues=%v,期望 %s issues=%v", c.solar, c.load, got, issues, c.want, c.issues)
  34. }
  35. }
  36. }
  37. func TestNormalizePriority(t *testing.T) {
  38. for in, want := range map[string]string{"9,9,9": "1,1,1", "2,5,1": "2,3,1", "0,5,9": "0,1,2"} {
  39. if got, ok := normalizePriority(in); !ok || got != want {
  40. t.Errorf("normalizePriority(%s)=%s,期望 %s", in, got, want)
  41. }
  42. }
  43. if _, ok := normalizePriority("1,2"); ok {
  44. t.Error("长度不为 3 应失败")
  45. }
  46. }
  47. func TestOrganizeTouDetail(t *testing.T) {
  48. detail := map[string]any{
  49. "template": map[string]any{"name": "T", "templateId": 1},
  50. "strategyList": []any{map[string]any{
  51. "seasonName": "All", "month": "1,2,3", "dayName": "Every day",
  52. "eleticRatePeak": 0.3,
  53. "detailVoList": []any{
  54. map[string]any{"startHourTime": "06:00", "endHourTime": "24:00", "waveType": 2, "solarPriority": "1,3,2", "loadPriority": "1,3,2", "chargeMax": -1},
  55. map[string]any{"startHourTime": "00:00", "endHourTime": "06:00", "waveType": 0, "solarPriority": "", "loadPriority": "", "chargePower": 8},
  56. },
  57. }},
  58. }
  59. out, ok := OrganizeTouDetail(detail)
  60. if !ok {
  61. t.Fatal("应能整理")
  62. }
  63. st := out["strategies"].([]map[string]any)[0]
  64. ps := st["periods"].([]TouPeriod)
  65. if ps[0].Start != "00:00" || ps[0].Mode != "强制充电" || ps[1].Mode != "D" || ps[1].WaveType != "峰" {
  66. t.Errorf("时段整理不正确: %+v", ps)
  67. }
  68. if _, has := ps[1].Params["chargeMax"]; has {
  69. t.Error("-1 应视为未设置")
  70. }
  71. if _, has := st["issues"]; has {
  72. t.Errorf("时段覆盖全天,不应有问题: %v", st["issues"])
  73. }
  74. if _, ok := OrganizeTouDetail(map[string]any{"foo": 1}); ok {
  75. t.Error("没有 strategyList 时应返回 false")
  76. }
  77. }
  78. func TestCheckCoverageGap(t *testing.T) {
  79. issues := checkCoverage([]TouPeriod{{Start: "00:00", End: "06:00"}, {Start: "07:00", End: "24:00"}})
  80. if len(issues) != 1 {
  81. t.Errorf("应发现 1 处不衔接: %v", issues)
  82. }
  83. }
  84. func TestPowerFlowToRows(t *testing.T) {
  85. p := &dto.SitePowerFlow{
  86. DeviceTimeArray: []string{"2026-10-01 00:00:00", "2026-10-01 00:05:00"},
  87. PowerSunArray: []float32{1.5, 2},
  88. SocArray: []float32{80},
  89. RunStatusArray: []uint{1, 2},
  90. }
  91. rows := PowerFlowToRows(p)
  92. if len(rows) != 2 || len(rows[0]) != len(powerFlowCols) {
  93. t.Fatalf("行列数不正确: %v", rows)
  94. }
  95. if rows[0][1] != "1.5" || rows[0][6] != "80" || rows[1][6] != "" || rows[1][7] != "2" {
  96. t.Errorf("映射不正确: %v", rows)
  97. }
  98. }
  99. func TestMapEventRows(t *testing.T) {
  100. end := "2026-10-01 10:00:00"
  101. rows := MapEventRows([]domain.EventAlarmItem{
  102. {LogType: "0", AlarmCode: "0", LogTime: "2026-10-01 08:00:00", LogName: "启动"},
  103. {LogType: "1", AlarmCode: "E101", LogTime: "2026-10-01 09:00:00", EndTime: &end},
  104. })
  105. if rows[0].Kind != "告警" || rows[0].EndTime != end || rows[1].Code != "" {
  106. t.Errorf("映射或排序不正确: %+v", rows)
  107. }
  108. }
  109. func TestParseSN(t *testing.T) {
  110. r, err := ParseSN("10060006A03X25310091")
  111. if err != nil {
  112. t.Fatal(err)
  113. }
  114. if r["productName"] != "aGate (网关)" || r["year"] != "2025" || r["week"] != "31" || r["serialNumber"] != "0091" {
  115. t.Errorf("解析不正确: %v", r)
  116. }
  117. if _, err := ParseSN("1006"); err == nil {
  118. t.Error("长度不足应报错")
  119. }
  120. }
  121. func TestParseIdentifier(t *testing.T) {
  122. p, n, err := parseIdentifier("fwh-42")
  123. if err != nil || p != "FWH" || n != 42 {
  124. t.Errorf("parseIdentifier 错误: %s %d %v", p, n, err)
  125. }
  126. if _, _, err := parseIdentifier("FWH"); err == nil {
  127. t.Error("缺少序号应报错")
  128. }
  129. }
  130. func TestSortCodeCounts(t *testing.T) {
  131. got := SortCodeCounts(map[string]uint{"b": 2, "a": 2, "c": 5})
  132. if got[0].Code != "c" || got[1].Code != "a" || got[2].Code != "b" {
  133. t.Errorf("排序不正确: %v", got)
  134. }
  135. }