go.mod 模块名为 xiaomihome,将所有内部 import 从 "miot" 更新为 "xiaomihome/miot",涉及所有测试文件和示例文件。新增 README.md 项目 介绍文档(架构概览、模块说明、移植进度等)。新增 ARCH_PLAN.md 架构 设计文档。
284 lines
8.4 KiB
Go
284 lines
8.4 KiB
Go
// Package miot_test provides black-box tests for const.go constants and enums.
|
|
package miot_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
miot "xiaomihome/miot"
|
|
)
|
|
|
|
// ============================================================================
|
|
// CtrlMode.String()
|
|
// ============================================================================
|
|
|
|
func TestCtrlMode_String(t *testing.T) {
|
|
tests := []struct {
|
|
mode miot.CtrlMode
|
|
want string
|
|
}{
|
|
{miot.CtrlModeCloud, "cloud"},
|
|
{miot.CtrlModeGateway, "gateway"},
|
|
{miot.CtrlModeLAN, "lan"},
|
|
{miot.CtrlMode(99), "unknown"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.want, func(t *testing.T) {
|
|
if got := tt.mode.String(); got != tt.want {
|
|
t.Errorf("CtrlMode(%d).String() = %q, want %q", tt.mode, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCtrlMode_IotaValues(t *testing.T) {
|
|
// Verify sequential iota values
|
|
if int(miot.CtrlModeCloud) != 0 {
|
|
t.Errorf("CtrlModeCloud = %d, want 0", miot.CtrlModeCloud)
|
|
}
|
|
if int(miot.CtrlModeGateway) != 1 {
|
|
t.Errorf("CtrlModeGateway = %d, want 1", miot.CtrlModeGateway)
|
|
}
|
|
if int(miot.CtrlModeLAN) != 2 {
|
|
t.Errorf("CtrlModeLAN = %d, want 2", miot.CtrlModeLAN)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// String Constants
|
|
// ============================================================================
|
|
|
|
func TestSupportedPlatforms(t *testing.T) {
|
|
if len(miot.SUPPORTED_PLATFORMS) == 0 {
|
|
t.Error("SUPPORTED_PLATFORMS should not be empty")
|
|
}
|
|
mustContain := []string{"switch", "light", "sensor", "binary_sensor", "climate", "cover"}
|
|
for _, p := range mustContain {
|
|
t.Run("contains_"+p, func(t *testing.T) {
|
|
found := false
|
|
for _, sp := range miot.SUPPORTED_PLATFORMS {
|
|
if sp == p {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("SUPPORTED_PLATFORMS should contain %q", p)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnsupportedModels(t *testing.T) {
|
|
if len(miot.UNSUPPORTED_MODELS) == 0 {
|
|
t.Error("UNSUPPORTED_MODELS should not be empty")
|
|
}
|
|
// All entries should be non-empty strings
|
|
for i, m := range miot.UNSUPPORTED_MODELS {
|
|
if m == "" {
|
|
t.Errorf("UNSUPPORTED_MODELS[%d] is empty", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCloudServers(t *testing.T) {
|
|
mustHave := []string{"cn", "de", "us", "ru", "sg", "i2"}
|
|
for _, k := range mustHave {
|
|
t.Run("contains_"+k, func(t *testing.T) {
|
|
if name, ok := miot.CLOUD_SERVERS[k]; !ok || name == "" {
|
|
t.Errorf("CLOUD_SERVERS should contain key %q with non-empty name", k)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIntegrationLanguages(t *testing.T) {
|
|
mustHave := map[string]string{
|
|
"en": "English",
|
|
"zh-Hans": "简体中文",
|
|
"zh-Hant": "繁體中文",
|
|
}
|
|
for code, name := range mustHave {
|
|
t.Run(code, func(t *testing.T) {
|
|
if got, ok := miot.INTEGRATION_LANGUAGES[code]; !ok || got != name {
|
|
t.Errorf("INTEGRATION_LANGUAGES[%q] = %q, want %q", code, got, name)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSupportedCentralGatewayCtrl(t *testing.T) {
|
|
if len(miot.SUPPORT_CENTRAL_GATEWAY_CTRL) == 0 {
|
|
t.Error("SUPPORT_CENTRAL_GATEWAY_CTRL should not be empty")
|
|
}
|
|
// "cn" must be in the list
|
|
found := false
|
|
for _, r := range miot.SUPPORT_CENTRAL_GATEWAY_CTRL {
|
|
if r == "cn" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("SUPPORT_CENTRAL_GATEWAY_CTRL should contain \"cn\"")
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Numeric Constants
|
|
// ============================================================================
|
|
|
|
func TestNumericConstants(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value int
|
|
min int
|
|
max int
|
|
}{
|
|
{"MIHOME_HTTP_API_TIMEOUT", miot.MIHOME_HTTP_API_TIMEOUT, 1, 120},
|
|
{"MIHOME_MQTT_KEEPALIVE", miot.MIHOME_MQTT_KEEPALIVE, 1, 3600},
|
|
{"MIHOME_CERT_EXPIRE_MARGIN", miot.MIHOME_CERT_EXPIRE_MARGIN, 1, 3600 * 24 * 30},
|
|
{"NETWORK_REFRESH_INTERVAL", miot.NETWORK_REFRESH_INTERVAL, 1, 3600},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.value < tt.min || tt.value > tt.max {
|
|
t.Errorf("%s = %d, expected between %d and %d", tt.name, tt.value, tt.min, tt.max)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRefreshConstants(t *testing.T) {
|
|
if miot.RefreshPropsDelay <= 0 {
|
|
t.Errorf("RefreshPropsDelay = %d, should be positive", miot.RefreshPropsDelay)
|
|
}
|
|
if miot.RefreshPropsRetryMax <= 0 {
|
|
t.Errorf("RefreshPropsRetryMax = %d, should be positive", miot.RefreshPropsRetryMax)
|
|
}
|
|
if miot.RefreshPropsRetryDelay <= 0 {
|
|
t.Errorf("RefreshPropsRetryDelay = %d, should be positive", miot.RefreshPropsRetryDelay)
|
|
}
|
|
if miot.RefreshCloudDevicesDelay <= 0 {
|
|
t.Errorf("RefreshCloudDevicesDelay = %d, should be positive", miot.RefreshCloudDevicesDelay)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// CA Certificate Constants
|
|
// ============================================================================
|
|
|
|
func TestCA_Cert(t *testing.T) {
|
|
if miot.MIHOME_CA_CERT_STR == "" {
|
|
t.Error("MIHOME_CA_CERT_STR should not be empty")
|
|
}
|
|
if !strings.HasPrefix(miot.MIHOME_CA_CERT_STR, "-----BEGIN CERTIFICATE-----") {
|
|
t.Error("MIHOME_CA_CERT_STR should start with BEGIN CERTIFICATE")
|
|
}
|
|
if len(miot.MIHOME_CA_CERT_SHA256) != 64 {
|
|
t.Errorf("MIHOME_CA_CERT_SHA256 length = %d, want 64 (SHA256 hex)", len(miot.MIHOME_CA_CERT_SHA256))
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// MQTT Topics
|
|
// ============================================================================
|
|
|
|
func TestMQTTTopics(t *testing.T) {
|
|
if miot.TopicProp != "properties_changed" {
|
|
t.Errorf("TopicProp = %q, want %q", miot.TopicProp, "properties_changed")
|
|
}
|
|
if miot.TopicEvent != "event_occured" {
|
|
t.Errorf("TopicEvent = %q, want %q", miot.TopicEvent, "event_occured")
|
|
}
|
|
if miot.TopicState != "state" {
|
|
t.Errorf("TopicState = %q, want %q", miot.TopicState, "state")
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// API Paths
|
|
// ============================================================================
|
|
|
|
func TestAPIPaths(t *testing.T) {
|
|
if miot.APIPathGetDevices == "" {
|
|
t.Error("APIPathGetDevices should not be empty")
|
|
}
|
|
if miot.APIPathGetProps == "" {
|
|
t.Error("APIPathGetProps should not be empty")
|
|
}
|
|
if miot.APIPathSetProps == "" {
|
|
t.Error("APIPathSetProps should not be empty")
|
|
}
|
|
if miot.APIPathDoAction == "" {
|
|
t.Error("APIPathDoAction should not be empty")
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Default Values
|
|
// ============================================================================
|
|
|
|
func TestDefaultValues(t *testing.T) {
|
|
if miot.DefaultCloudServer != "cn" {
|
|
t.Errorf("DefaultCloudServer = %q, want %q", miot.DefaultCloudServer, "cn")
|
|
}
|
|
if miot.DefaultOAUTH2APIHost == "" {
|
|
t.Error("DefaultOAUTH2APIHost should not be empty")
|
|
}
|
|
if miot.MHomeHTTPAPITimeout <= 0 {
|
|
t.Errorf("MHomeHTTPAPITimeout = %d, should be positive", miot.MHomeHTTPAPITimeout)
|
|
}
|
|
if miot.DefaultMQTTBroker == "" {
|
|
t.Error("DefaultMQTTBroker should not be empty")
|
|
}
|
|
if miot.DefaultMQTTKeepAlive <= 0 {
|
|
t.Errorf("DefaultMQTTKeepAlive = %d, should be positive", miot.DefaultMQTTKeepAlive)
|
|
}
|
|
if miot.DefaultMQTTQoS < 0 || miot.DefaultMQTTQoS > 2 {
|
|
t.Errorf("DefaultMQTTQoS = %d, should be between 0 and 2", miot.DefaultMQTTQoS)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Domain and OAuth Constants
|
|
// ============================================================================
|
|
|
|
func TestDomainConstants(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value string
|
|
}{
|
|
{"DOMAIN", miot.DOMAIN},
|
|
{"DEFAULT_NAME", miot.DEFAULT_NAME},
|
|
{"OAUTH2_CLIENT_ID", miot.OAUTH2_CLIENT_ID},
|
|
{"OAUTH2_AUTH_URL", miot.OAUTH2_AUTH_URL},
|
|
{"OAUTH_REDIRECT_URL", miot.OAUTH_REDIRECT_URL},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.value == "" {
|
|
t.Errorf("%s should not be empty", tt.name)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Cover Dead Zone
|
|
// ============================================================================
|
|
|
|
func TestCoverDeadZone(t *testing.T) {
|
|
if miot.DEFAULT_COVER_DEAD_ZONE_WIDTH < miot.MIN_COVER_DEAD_ZONE_WIDTH {
|
|
t.Errorf("DEFAULT_COVER_DEAD_ZONE_WIDTH (%d) < MIN (%d)",
|
|
miot.DEFAULT_COVER_DEAD_ZONE_WIDTH, miot.MIN_COVER_DEAD_ZONE_WIDTH)
|
|
}
|
|
if miot.DEFAULT_COVER_DEAD_ZONE_WIDTH > miot.MAX_COVER_DEAD_ZONE_WIDTH {
|
|
t.Errorf("DEFAULT_COVER_DEAD_ZONE_WIDTH (%d) > MAX (%d)",
|
|
miot.DEFAULT_COVER_DEAD_ZONE_WIDTH, miot.MAX_COVER_DEAD_ZONE_WIDTH)
|
|
}
|
|
}
|