- 实现 MIoT 客户端核心功能(MQTT 连接、设备管理、属性读写) - 支持云端 API 调用与局域网设备发现(mDNS) - 集成国际化(i18n)多语言支持 - 添加 MIoT 设备规约解析器(spec_parser) - 包含单元测试与使用示例
292 lines
7.4 KiB
Go
292 lines
7.4 KiB
Go
// Package miot provides white-box tests for miot_client.go core client.
|
|
// Complements existing tests in miot_test.go with additional coverage.
|
|
package miot
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// ============================================================================
|
|
// NewMIoTClient — field initialization
|
|
// ============================================================================
|
|
|
|
func TestNewMIoTClient_FieldsInitialized(t *testing.T) {
|
|
c := NewMIoTClient("entry1", map[string]interface{}{"key": "val"}, "uid1", "cn", CtrlModeCloud)
|
|
|
|
if c.entryID != "entry1" {
|
|
t.Errorf("entryID = %q, want 'entry1'", c.entryID)
|
|
}
|
|
if c.uid != "uid1" {
|
|
t.Errorf("uid = %q, want 'uid1'", c.uid)
|
|
}
|
|
if c.cloudServer != "cn" {
|
|
t.Errorf("cloudServer = %q, want 'cn'", c.cloudServer)
|
|
}
|
|
if c.ctx == nil {
|
|
t.Error("ctx should not be nil")
|
|
}
|
|
if c.cancel == nil {
|
|
t.Error("cancel should not be nil")
|
|
}
|
|
if c.subTree == nil {
|
|
t.Error("subTree should not be nil")
|
|
}
|
|
if c.deviceListCache == nil {
|
|
t.Error("deviceListCache should not be nil")
|
|
}
|
|
if c.refreshPropsList == nil {
|
|
t.Error("refreshPropsList should not be nil")
|
|
}
|
|
if c.mipsLocal == nil {
|
|
t.Error("mipsLocal should not be nil")
|
|
}
|
|
|
|
c.Stop()
|
|
}
|
|
|
|
func TestNewMIoTClient_NoEntryData(t *testing.T) {
|
|
c := NewMIoTClient("entry2", nil, "uid2", "us", CtrlModeLAN)
|
|
|
|
if c.ctrlMode != CtrlModeLAN {
|
|
t.Errorf("ctrlMode = %v, want LAN", c.ctrlMode)
|
|
}
|
|
c.Stop()
|
|
}
|
|
|
|
// ============================================================================
|
|
// SetStorage / SetHTTPClient / SetMipsCloud / SetMipsLocal
|
|
// ============================================================================
|
|
|
|
func TestSetStorage(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
s := NewMIoTStorage(t.TempDir())
|
|
c.SetStorage(s)
|
|
|
|
if c.storage != s {
|
|
t.Error("SetStorage did not set storage")
|
|
}
|
|
}
|
|
|
|
func TestSetHTTPClient(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
http := &MIoTHttpClient{}
|
|
c.SetHTTPClient(http)
|
|
|
|
if c.http != http {
|
|
t.Error("SetHTTPClient did not set http")
|
|
}
|
|
}
|
|
|
|
func TestSetMipsCloud(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
|
|
mc := NewMipsCloudClient("ssl://host:8883", "client1", "user", "pass")
|
|
c.SetMipsCloud(mc)
|
|
|
|
if c.mipsCloud != mc {
|
|
t.Error("SetMipsCloud did not set mipsCloud")
|
|
}
|
|
|
|
// Clean up: nil out mipsCloud before Stop/Deinit to avoid Disconnect panic
|
|
c.mipsCloud = nil
|
|
c.Stop()
|
|
}
|
|
|
|
func TestSetMipsLocal(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
|
|
ml := NewMipsLocalClient("did_local", "host", 8883, "", "", "")
|
|
c.SetMipsLocal("group1", ml)
|
|
|
|
if got := c.mipsLocal["group1"]; got != ml {
|
|
t.Errorf("SetMipsLocal = %v, want %v", got, ml)
|
|
}
|
|
|
|
// Clean up before Stop
|
|
c.mipsLocal = make(map[string]*MipsLocalClient)
|
|
c.Stop()
|
|
}
|
|
|
|
// ============================================================================
|
|
// Init — no entryData (no HTTP client created)
|
|
// ============================================================================
|
|
|
|
func TestInit_NoCredentials(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
s := NewMIoTStorage(t.TempDir())
|
|
c.SetStorage(s)
|
|
|
|
err := c.Init()
|
|
if err != nil {
|
|
t.Fatalf("Init should not error: %v", err)
|
|
}
|
|
|
|
// Without entryData credentials, http should remain nil
|
|
if c.http != nil {
|
|
t.Error("http should be nil without credentials in entryData")
|
|
}
|
|
}
|
|
|
|
func TestInit_WithCredentials(t *testing.T) {
|
|
c := NewMIoTClient("test", map[string]interface{}{
|
|
"client_id": "test_client_id",
|
|
"access_token": "test_token",
|
|
}, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
s := NewMIoTStorage(t.TempDir())
|
|
c.SetStorage(s)
|
|
|
|
err := c.Init()
|
|
if err != nil {
|
|
t.Fatalf("Init should not error: %v", err)
|
|
}
|
|
|
|
// With credentials, http should be created
|
|
if c.http == nil {
|
|
t.Error("http should not be nil with valid entryData credentials")
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Deinit
|
|
// ============================================================================
|
|
|
|
func TestDeinit_ClearsSubTree(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
// Add a subscription
|
|
h := func(params map[string]interface{}, ctx interface{}) {}
|
|
c.subTree.Sub("did1/p/1", h, nil)
|
|
|
|
if c.subTree.Size() != 1 {
|
|
t.Fatal("subTree should have 1 entry")
|
|
}
|
|
|
|
c.Deinit()
|
|
|
|
if c.subTree.Size() != 0 {
|
|
t.Error("subTree should be cleared after Deinit")
|
|
}
|
|
}
|
|
|
|
func TestDeinit_StopsTimers(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
|
|
// Simulate a timer being set
|
|
c.refreshTokenTimer = time.NewTimer(time.Hour)
|
|
c.refreshCloudDevicesTimer = time.NewTimer(time.Hour)
|
|
|
|
c.Deinit()
|
|
|
|
// Timers should be stopped (check by trying to stop again)
|
|
if c.refreshTokenTimer != nil {
|
|
// Already stopped by Deinit, Stop() returns false
|
|
_ = c.refreshTokenTimer
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Start / Stop
|
|
// ============================================================================
|
|
|
|
func TestStart(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
s := NewMIoTStorage(t.TempDir())
|
|
c.SetStorage(s)
|
|
|
|
err := c.Start()
|
|
if err != nil {
|
|
t.Fatalf("Start should not error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStop(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
err := c.Stop()
|
|
if err != nil {
|
|
t.Errorf("Stop should not error: %v", err)
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// LoadDevices (complement to existing test)
|
|
// ============================================================================
|
|
|
|
func TestLoadDevices_NoStorage(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
// LoadDevices without storage should not panic
|
|
err := c.LoadDevices()
|
|
// May error due to missing storage, but should not panic
|
|
_ = err
|
|
}
|
|
|
|
// ============================================================================
|
|
// DeviceList copy
|
|
// ============================================================================
|
|
|
|
func TestDeviceList_ReturnsCopy(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
c.deviceListMu.Lock()
|
|
c.deviceListCache["did1"] = map[string]interface{}{"name": "test"}
|
|
c.deviceListMu.Unlock()
|
|
|
|
list1 := c.DeviceList()
|
|
list2 := c.DeviceList()
|
|
|
|
// Modifying one copy should not affect the other
|
|
list1["new"] = map[string]interface{}{}
|
|
if _, ok := list2["new"]; ok {
|
|
t.Error("DeviceList should return independent copies")
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// loadCacheDevice
|
|
// ============================================================================
|
|
|
|
func TestLoadCacheDevice_NoStorage(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
|
|
err := c.loadCacheDevice()
|
|
if err == nil {
|
|
t.Error("loadCacheDevice without storage should error")
|
|
}
|
|
|
|
c.Stop()
|
|
}
|
|
|
|
func TestLoadCacheDevice_WithStorage(t *testing.T) {
|
|
c := NewMIoTClient("test", nil, "uid", "cn", CtrlModeCloud)
|
|
defer c.Stop()
|
|
|
|
s := NewMIoTStorage(t.TempDir())
|
|
c.SetStorage(s)
|
|
|
|
err := c.loadCacheDevice()
|
|
if err != nil {
|
|
t.Fatalf("loadCacheDevice should succeed: %v", err)
|
|
}
|
|
|
|
// Cache should be initialized (empty)
|
|
if c.deviceListCache == nil {
|
|
t.Error("deviceListCache should not be nil")
|
|
}
|
|
}
|