Files
xiaomihome/xiaomi/devices/devices_test.go
T
4566704 b808e51b3e refactor: 重大架构升级 — HomeID/RoomID int64化 + 设备驱动重构 + 结构化日志
主要变更:

**类型系统重构**
- HomeInfo.ID/RoomInfo.ID/HomeID 从 string 改为 int64,json tag 使用 ,string
- GetHome/GetRooms/GetRoomDevices 接口同步修改
- 新增 int64FromStr 工具函数,更新 parseHomeInfo 解析逻辑

**新增 Speaker 设备**
- 新增 KindSpeaker 类型
- classify 映射覆盖 speaker/bluetooth-speaker/alarm-clock/media-player/playback/music

**设备驱动重构**
- AC 驱动彻底重写:从硬编码常量改为基于 SPEC ValueList 的动态 valMapper
- 所有设备(Fan/Humidifier/Light/OccupancySensor/Switch/TempHumiditySensor/Thermostat/Vacuum/WaterHeater/Cover)新增 GetState() 和 FetchState(ctx) 接口
- base.go 新增 specResolver.format() 方法

**MIoT 模块增强**
- MIoTClient 新增 homeIDs 字段,支持按家庭过滤设备
- RefreshDevices 支持 homeIDs 参数
- SpecStdLib 引入 logger.Logger,log.Printf 全部迁移为结构化日志

**其他**
- Config 新增 RedirectURL 和 HomeIDs 字段
- AuthInfo.ExpiresIn 类型从 int 改为 int32
- UUID 生成简化为 hex.EncodeToString
- 新增 github.com/sirupsen/logrus 依赖
- .gitignore 新增 examples/*/data/ 忽略规则
- 删除已提交的 config.json 敏感配置
2026-07-13 10:40:20 +08:00

174 lines
3.5 KiB
Go

package devices
import (
"testing"
"xiaomihome/xiaomi"
)
func TestToInt(t *testing.T) {
tests := []struct {
name string
value interface{}
want int
}{
{"float64", float64(42), 42},
{"int", int(42), 42},
{"int64", int64(42), 42},
{"uint8", uint8(100), 100},
{"uint16", uint16(1000), 1000},
{"nil", nil, 0},
{"string", "hello", 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := toInt(tt.value)
if got != tt.want {
t.Errorf("toInt(%v) = %d, want %d", tt.value, got, tt.want)
}
})
}
}
func TestToFloat(t *testing.T) {
tests := []struct {
name string
value interface{}
want float64
}{
{"float64", float64(26.5), 26.5},
{"float32", float32(30.0), 30.0},
{"int", int(25), 25.0},
{"int64", int64(20), 20.0},
{"string", "26.5", 0.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := toFloat(tt.value)
if got != tt.want {
t.Errorf("toFloat(%v) = %f, want %f", tt.value, got, tt.want)
}
})
}
}
func TestAirConditionerModeConstants(t *testing.T) {
if ACModeCool != "cool" || ACModeHeat != "heat" || ACModeDry != "dry" || ACModeAuto != "auto" {
t.Error("AC mode constants mismatch")
}
}
func TestFanSpeedConstants(t *testing.T) {
if ACFanLow != "low" || ACFanAuto != "auto" {
t.Error("Fan speed constants mismatch")
}
}
func TestSwingModeConstants(t *testing.T) {
if ACSwingOff != "off" || ACSwingVertical != "vertical" {
t.Error("Swing mode constants mismatch")
}
}
func TestMatchURN(t *testing.T) {
tests := []struct {
name string
urn string
types []string
want bool
}{
{
"air-conditioner",
"urn:miot-spec-v2:device:air-conditioner:0000A004:xiaomi-c1:1",
[]string{"air-conditioner"},
true,
},
{
"light",
"urn:miot-spec-v2:device:light:0000A001:philips-bulb:1",
[]string{"light"},
true,
},
{
"switch outlet",
"urn:miot-spec-v2:device:outlet:0000A002:xiaomi-plug:1",
[]string{"switch", "outlet"},
true,
},
{
"fan",
"urn:miot-spec-v2:device:fan:0000A003:xiaomi-fan:1",
[]string{"fan"},
true,
},
{
"curtain",
"urn:miot-spec-v2:device:curtain:0000A005:aqara-c1:1",
[]string{"curtain"},
true,
},
{
"humidifier",
"urn:miot-spec-v2:device:humidifier:0000A006:xiaomi-h1:1",
[]string{"humidifier"},
true,
},
{
"vacuum",
"urn:miot-spec-v2:device:vacuum:0000A007:roborock-s5:1",
[]string{"vacuum"},
true,
},
{
"water-heater",
"urn:miot-spec-v2:device:water-heater:0000A008:xiaomi-wh1:1",
[]string{"water-heater"},
true,
},
{
"heater thermostat",
"urn:miot-spec-v2:device:heater:0000A009:xiaomi-h1:1",
[]string{"heater"},
true,
},
{
"no match",
"urn:miot-spec-v2:device:unknown:0000A099:test:1",
[]string{"air-conditioner", "light", "switch"},
false,
},
{
"empty URN",
"",
[]string{"light"},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := matchURN(tt.urn, tt.types...)
if got != tt.want {
t.Errorf("matchURN(%q, %v) = %v, want %v", tt.urn, tt.types, got, tt.want)
}
})
}
}
func TestFactoryNilClientPanic(t *testing.T) {
// Create with a nil client should panic (calling methods on nil client)
// This is a documentation test noting expected behavior
t.Skip("requires real or mock client")
}
func TestCreateNilInfo(t *testing.T) {
client := &xiaomi.Client{}
result, err := Create(client, nil)
if result != nil {
t.Errorf("expected nil result, got %v", result)
}
if err == nil {
t.Error("expected error for nil info")
}
}