// 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) } }