feat: 初始化小米 IoT (MIoT) 智能家居 Go 库
- 实现 MIoT 客户端核心功能(MQTT 连接、设备管理、属性读写) - 支持云端 API 调用与局域网设备发现(mDNS) - 集成国际化(i18n)多语言支持 - 添加 MIoT 设备规约解析器(spec_parser) - 包含单元测试与使用示例
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
// Package miot_test provides black-box tests for miot_error.go error types.
|
||||
package miot_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
miot "miot"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// Helper: verify error fields
|
||||
// ============================================================================
|
||||
|
||||
func checkError(t *testing.T, err error, wantCode int, wantMsg string, typeName string) {
|
||||
t.Helper()
|
||||
if err == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
}
|
||||
|
||||
// Check Error() format
|
||||
errStr := err.Error()
|
||||
expected := fmt.Sprintf("code=%d, message=%s", wantCode, wantMsg)
|
||||
if errStr != expected {
|
||||
t.Errorf("%s.Error() = %q, want %q", typeName, errStr, expected)
|
||||
}
|
||||
|
||||
// Check type assertion
|
||||
asserted := false
|
||||
switch err.(type) {
|
||||
case *miot.MIoTOauthError:
|
||||
asserted = typeName == "MIoTOauthError"
|
||||
case *miot.MIoTHttpError:
|
||||
asserted = typeName == "MIoTHttpError"
|
||||
case *miot.MIoTMipsError:
|
||||
asserted = typeName == "MIoTMipsError"
|
||||
case *miot.MIoTDeviceError:
|
||||
asserted = typeName == "MIoTDeviceError"
|
||||
case *miot.MIoTSpecError:
|
||||
asserted = typeName == "MIoTSpecError"
|
||||
case *miot.MIoTStorageError:
|
||||
asserted = typeName == "MIoTStorageError"
|
||||
case *miot.MIoTCertError:
|
||||
asserted = typeName == "MIoTCertError"
|
||||
case *miot.MIoTClientError:
|
||||
asserted = typeName == "MIoTClientError"
|
||||
case *miot.MIoTEvError:
|
||||
asserted = typeName == "MIoTEvError"
|
||||
case *miot.MipsServiceError:
|
||||
asserted = typeName == "MipsServiceError"
|
||||
case *miot.MIoTConfigError:
|
||||
asserted = typeName == "MIoTConfigError"
|
||||
case *miot.MIoTOptionsError:
|
||||
asserted = typeName == "MIoTOptionsError"
|
||||
case *miot.MIoTLanError:
|
||||
asserted = typeName == "MIoTLanError"
|
||||
}
|
||||
if !asserted {
|
||||
t.Errorf("expected %s type, got %T", typeName, err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkToDict(t *testing.T, err error, wantCode int, wantMsg string) {
|
||||
t.Helper()
|
||||
// All MIoT*Error types embed MIoTError, so we can cast to that
|
||||
var miotErr interface{ ToDict() map[string]interface{} }
|
||||
switch e := err.(type) {
|
||||
case *miot.MIoTOauthError:
|
||||
miotErr = e
|
||||
case *miot.MIoTHttpError:
|
||||
miotErr = e
|
||||
case *miot.MIoTMipsError:
|
||||
miotErr = e
|
||||
case *miot.MIoTDeviceError:
|
||||
miotErr = e
|
||||
case *miot.MIoTSpecError:
|
||||
miotErr = e
|
||||
case *miot.MIoTStorageError:
|
||||
miotErr = e
|
||||
case *miot.MIoTCertError:
|
||||
miotErr = e
|
||||
case *miot.MIoTClientError:
|
||||
miotErr = e
|
||||
case *miot.MIoTEvError:
|
||||
miotErr = e
|
||||
case *miot.MipsServiceError:
|
||||
miotErr = e
|
||||
case *miot.MIoTConfigError:
|
||||
miotErr = e
|
||||
case *miot.MIoTOptionsError:
|
||||
miotErr = e
|
||||
case *miot.MIoTLanError:
|
||||
miotErr = e
|
||||
default:
|
||||
t.Fatalf("unknown error type: %T", err)
|
||||
return
|
||||
}
|
||||
dict := miotErr.ToDict()
|
||||
if code, ok := dict["code"].(int); !ok || code != wantCode {
|
||||
t.Errorf("ToDict() code = %v, want %d", dict["code"], wantCode)
|
||||
}
|
||||
if msg, ok := dict["message"].(string); !ok || msg != wantMsg {
|
||||
t.Errorf("ToDict() message = %v, want %q", dict["message"], wantMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// All 12 Error Constructors
|
||||
// ============================================================================
|
||||
|
||||
func TestNewMIoTOauthError(t *testing.T) {
|
||||
err := miot.NewMIoTOauthError("oauth failed")
|
||||
checkError(t, err, miot.CodeOauthUnauthorized, "oauth failed", "MIoTOauthError")
|
||||
checkToDict(t, err, miot.CodeOauthUnauthorized, "oauth failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTHttpError(t *testing.T) {
|
||||
err := miot.NewMIoTHttpError("http failed")
|
||||
checkError(t, err, miot.CodeHttpInvalidAccessToken, "http failed", "MIoTHttpError")
|
||||
checkToDict(t, err, miot.CodeHttpInvalidAccessToken, "http failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTMipsError(t *testing.T) {
|
||||
err := miot.NewMIoTMipsError("mips failed")
|
||||
checkError(t, err, miot.CodeMipsInvalidResult, "mips failed", "MIoTMipsError")
|
||||
checkToDict(t, err, miot.CodeMipsInvalidResult, "mips failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTDeviceError(t *testing.T) {
|
||||
err := miot.NewMIoTDeviceError("device failed")
|
||||
checkError(t, err, miot.CodeUnknown, "device failed", "MIoTDeviceError")
|
||||
checkToDict(t, err, miot.CodeUnknown, "device failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTSpecError(t *testing.T) {
|
||||
err := miot.NewMIoTSpecError("spec failed")
|
||||
checkError(t, err, miot.CodeUnknown, "spec failed", "MIoTSpecError")
|
||||
checkToDict(t, err, miot.CodeUnknown, "spec failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTStorageError(t *testing.T) {
|
||||
err := miot.NewMIoTStorageError("storage failed")
|
||||
checkError(t, err, miot.CodeUnknown, "storage failed", "MIoTStorageError")
|
||||
checkToDict(t, err, miot.CodeUnknown, "storage failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTCertError(t *testing.T) {
|
||||
err := miot.NewMIoTCertError("cert failed")
|
||||
checkError(t, err, miot.CodeCertInvalidCert, "cert failed", "MIoTCertError")
|
||||
checkToDict(t, err, miot.CodeCertInvalidCert, "cert failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTClientError(t *testing.T) {
|
||||
err := miot.NewMIoTClientError("client failed")
|
||||
checkError(t, err, miot.CodeUnknown, "client failed", "MIoTClientError")
|
||||
checkToDict(t, err, miot.CodeUnknown, "client failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTEvError(t *testing.T) {
|
||||
err := miot.NewMIoTEvError("event failed")
|
||||
checkError(t, err, miot.CodeUnknown, "event failed", "MIoTEvError")
|
||||
checkToDict(t, err, miot.CodeUnknown, "event failed")
|
||||
}
|
||||
|
||||
func TestNewMipsServiceError(t *testing.T) {
|
||||
err := miot.NewMipsServiceError("service failed")
|
||||
checkError(t, err, miot.CodeUnknown, "service failed", "MipsServiceError")
|
||||
checkToDict(t, err, miot.CodeUnknown, "service failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTConfigError(t *testing.T) {
|
||||
err := miot.NewMIoTConfigError("config failed")
|
||||
checkError(t, err, miot.CodeConfigInvalidInput, "config failed", "MIoTConfigError")
|
||||
checkToDict(t, err, miot.CodeConfigInvalidInput, "config failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTOptionsError(t *testing.T) {
|
||||
err := miot.NewMIoTOptionsError("options failed")
|
||||
checkError(t, err, miot.CodeUnknown, "options failed", "MIoTOptionsError")
|
||||
checkToDict(t, err, miot.CodeUnknown, "options failed")
|
||||
}
|
||||
|
||||
func TestNewMIoTLanError(t *testing.T) {
|
||||
err := miot.NewMIoTLanError("lan failed")
|
||||
checkError(t, err, miot.CodeLanUnavailable, "lan failed", "MIoTLanError")
|
||||
checkToDict(t, err, miot.CodeLanUnavailable, "lan failed")
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Empty message
|
||||
// ============================================================================
|
||||
|
||||
func TestErrors_EmptyMessage(t *testing.T) {
|
||||
err := miot.NewMIoTDeviceError("")
|
||||
if err == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
}
|
||||
// Error() with empty message should still be valid
|
||||
if !strings.Contains(err.Error(), "message=") {
|
||||
t.Errorf("Error() should contain 'message=', got %q", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Error implements error interface
|
||||
// ============================================================================
|
||||
|
||||
func TestErrors_ImplementsErrorInterface(t *testing.T) {
|
||||
var _ error = miot.NewMIoTDeviceError("test")
|
||||
var _ error = miot.NewMIoTOauthError("test")
|
||||
var _ error = miot.NewMIoTHttpError("test")
|
||||
// Compile-time check — if this compiles, it passes
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MIoTError embedded in sub-types
|
||||
// ============================================================================
|
||||
|
||||
func TestMIoTError_EmbeddedAccess(t *testing.T) {
|
||||
err := miot.NewMIoTDeviceError("test message")
|
||||
|
||||
// The embedded MIoTError fields should be accessible via the Error() method
|
||||
if !strings.Contains(err.Error(), "test message") {
|
||||
t.Errorf("Error() = %q, should contain 'test message'", err.Error())
|
||||
}
|
||||
|
||||
// MIoTError.ToDict() should be accessible on sub-error types
|
||||
dict := err.ToDict()
|
||||
if dict["message"] != "test message" {
|
||||
t.Errorf("ToDict() message = %v, want 'test message'", dict["message"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user