- internal/plugin: Plugin/IoTProvider/ToolProvider/RAGProvider interfaces - internal/plugin: Registry with type-safe auto-detection, 11 tests - plugins/iot-mock: 8 simulated smart home devices, 11 tests - 63 total tests, build + vet clean
201 lines
4.7 KiB
Go
201 lines
4.7 KiB
Go
package iotmock
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/plugin"
|
|
)
|
|
|
|
func TestNewPlugin(t *testing.T) {
|
|
p := New()
|
|
|
|
if p.Name() != "iot-mock" {
|
|
t.Errorf("expected name iot-mock, got %s", p.Name())
|
|
}
|
|
if p.Version() != "1.0.0" {
|
|
t.Errorf("expected version 1.0.0, got %s", p.Version())
|
|
}
|
|
if !p.Enabled() {
|
|
t.Error("plugin should be enabled by default")
|
|
}
|
|
}
|
|
|
|
func TestListDevices(t *testing.T) {
|
|
p := New()
|
|
|
|
devices, err := p.ListDevices(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("ListDevices failed: %v", err)
|
|
}
|
|
if len(devices) != 8 {
|
|
t.Errorf("expected 8 devices, got %d", len(devices))
|
|
}
|
|
|
|
// Verify devices are clones (modifying returned device doesn't affect internal state)
|
|
devices[0].State["power"] = true
|
|
d, _ := p.GetDevice(context.Background(), "light-livingroom")
|
|
if d.State["power"] != false {
|
|
t.Error("returned devices should be clones, internal state modified")
|
|
}
|
|
}
|
|
|
|
func TestGetDevice(t *testing.T) {
|
|
p := New()
|
|
|
|
d, err := p.GetDevice(context.Background(), "light-livingroom")
|
|
if err != nil {
|
|
t.Fatalf("GetDevice failed: %v", err)
|
|
}
|
|
if d.Name != "客厅灯" {
|
|
t.Errorf("expected name 客厅灯, got %s", d.Name)
|
|
}
|
|
if d.Type != plugin.DeviceLight {
|
|
t.Errorf("expected type light, got %s", d.Type)
|
|
}
|
|
|
|
// Unknown device
|
|
_, err = p.GetDevice(context.Background(), "nonexistent")
|
|
if err == nil {
|
|
t.Error("expected error for unknown device")
|
|
}
|
|
}
|
|
|
|
func TestToggleDevice(t *testing.T) {
|
|
p := New()
|
|
|
|
// Initially off
|
|
d, _ := p.GetDevice(context.Background(), "light-livingroom")
|
|
if d.State["power"] != false {
|
|
t.Error("expected light to be off initially")
|
|
}
|
|
|
|
// Toggle on
|
|
err := p.ToggleDevice(context.Background(), "light-livingroom")
|
|
if err != nil {
|
|
t.Fatalf("ToggleDevice failed: %v", err)
|
|
}
|
|
|
|
d, _ = p.GetDevice(context.Background(), "light-livingroom")
|
|
if d.State["power"] != true {
|
|
t.Error("expected light to be on after toggle")
|
|
}
|
|
|
|
// Toggle off
|
|
p.ToggleDevice(context.Background(), "light-livingroom")
|
|
d, _ = p.GetDevice(context.Background(), "light-livingroom")
|
|
if d.State["power"] != false {
|
|
t.Error("expected light to be off after second toggle")
|
|
}
|
|
}
|
|
|
|
func TestSetDevice(t *testing.T) {
|
|
p := New()
|
|
|
|
err := p.SetDevice(context.Background(), "light-livingroom", "brightness", 50)
|
|
if err != nil {
|
|
t.Fatalf("SetDevice failed: %v", err)
|
|
}
|
|
|
|
d, _ := p.GetDevice(context.Background(), "light-livingroom")
|
|
if d.State["brightness"] != 50 {
|
|
t.Errorf("expected brightness=50, got %v", d.State["brightness"])
|
|
}
|
|
|
|
// History should have an entry
|
|
if len(d.History) != 1 {
|
|
t.Errorf("expected 1 history entry, got %d", len(d.History))
|
|
}
|
|
}
|
|
|
|
func TestSetDeviceNotFound(t *testing.T) {
|
|
p := New()
|
|
err := p.SetDevice(context.Background(), "nonexistent", "power", true)
|
|
if err == nil {
|
|
t.Error("expected error for unknown device")
|
|
}
|
|
}
|
|
|
|
func TestSubscribeChanges(t *testing.T) {
|
|
p := New()
|
|
|
|
ch, err := p.SubscribeChanges(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("SubscribeChanges failed: %v", err)
|
|
}
|
|
|
|
// Toggle should emit an event
|
|
go p.ToggleDevice(context.Background(), "light-livingroom")
|
|
|
|
event := <-ch
|
|
if event.DeviceID != "light-livingroom" {
|
|
t.Errorf("expected event for light-livingroom, got %s", event.DeviceID)
|
|
}
|
|
if event.Field != "power" {
|
|
t.Errorf("expected event field power, got %s", event.Field)
|
|
}
|
|
if event.NewValue != true {
|
|
t.Errorf("expected new value true, got %v", event.NewValue)
|
|
}
|
|
}
|
|
|
|
func TestInitAndClose(t *testing.T) {
|
|
p := New()
|
|
|
|
err := p.Init(context.Background(), plugin.PluginConfig{})
|
|
if err != nil {
|
|
t.Fatalf("Init failed: %v", err)
|
|
}
|
|
|
|
err = p.Close()
|
|
if err != nil {
|
|
t.Fatalf("Close failed: %v", err)
|
|
}
|
|
|
|
// Second close should be safe
|
|
err = p.Close()
|
|
if err != nil {
|
|
t.Errorf("second Close should not error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDeviceHistoryLimit(t *testing.T) {
|
|
p := New()
|
|
|
|
// Generate 25 events (limit is 20)
|
|
for i := range 25 {
|
|
p.SetDevice(context.Background(), "light-livingroom", "brightness", i)
|
|
}
|
|
|
|
d, _ := p.GetDevice(context.Background(), "light-livingroom")
|
|
if len(d.History) > 20 {
|
|
t.Errorf("history should be capped at 20, got %d", len(d.History))
|
|
}
|
|
}
|
|
|
|
func TestAllDevicesExist(t *testing.T) {
|
|
p := New()
|
|
expectedIDs := []string{
|
|
"light-livingroom", "light-bedroom",
|
|
"ac-livingroom", "ac-bedroom",
|
|
"curtain-livingroom",
|
|
"sensor-temperature", "sensor-humidity",
|
|
"lock-door",
|
|
}
|
|
|
|
for _, id := range expectedIDs {
|
|
_, err := p.GetDevice(context.Background(), id)
|
|
if err != nil {
|
|
t.Errorf("expected device %s to exist", id)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPluginInterface(t *testing.T) {
|
|
p := New()
|
|
// Verify it implements the IoTProvider interface
|
|
var _ plugin.IoTProvider = p
|
|
// Verify it implements the base Plugin interface
|
|
var _ plugin.Plugin = p
|
|
}
|