Reticulum-Go/pkg/interfaces/plugin_test.go

123 lines
3.4 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2024-2026 Quad4.io
package interfaces
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"testing"
"quad4/reticulum-go/pkg/common"
)
func TestApplyOutgoingFromConfig(t *testing.T) {
ui, err := NewUDPInterface("u", "127.0.0.1:0", "127.0.0.1:1", true)
if err != nil {
t.Fatal(err)
}
cfg := &common.InterfaceConfig{Type: "UDPInterface", Enabled: true, Outgoing: false, OutgoingSet: true}
applyOutgoingFromConfig(ui, cfg)
if ui.AllowsOutgoing() {
t.Fatal("expected outgoing blocked")
}
cfg.Outgoing = true
applyOutgoingFromConfig(ui, cfg)
if !ui.AllowsOutgoing() {
t.Fatal("expected outgoing allowed")
}
}
func TestApplyOutgoingFromConfigExternalFactory(t *testing.T) {
const typeName = "OutgoingFactoryIface"
defer UnregisterExternalFactory(typeName)
RegisterExternalFactory(typeName, func(name string, cfg *common.InterfaceConfig, ctx *FromConfigContext) (Interface, error) {
return NewUDPInterface(name, "127.0.0.1:0", "127.0.0.1:9", true)
})
cfg := &common.InterfaceConfig{Type: typeName, Enabled: true, Outgoing: false, OutgoingSet: true}
iface, err := NewFromConfigWithContext("factory", cfg, &FromConfigContext{})
if err != nil {
t.Fatal(err)
}
ui, ok := iface.(*UDPInterface)
if !ok {
t.Fatalf("got %T, want *UDPInterface", iface)
}
if ui.AllowsOutgoing() {
t.Fatal("expected factory-built iface to honor outgoing=no")
}
}
func TestPluginTypePathTraversalRejected(t *testing.T) {
dir := t.TempDir()
cfg := &common.InterfaceConfig{Type: "../escape", Enabled: true}
_, err := loadExternalInterface("bad", cfg, &FromConfigContext{ConfigDir: dir})
if err == nil {
t.Fatal("expected path traversal rejection")
}
}
func TestUDPSendRejectsReceiveOnly(t *testing.T) {
ui, err := NewUDPInterface("ro", "127.0.0.1:0", "127.0.0.1:1", true)
if err != nil {
t.Fatal(err)
}
ui.Online = true
ui.SetOutgoingAllowed(false)
err = ui.Send([]byte("x"), "")
if !errors.Is(err, common.ErrInterfaceReceiveOnly) {
t.Fatalf("got %v, want ErrInterfaceReceiveOnly", err)
}
}
func TestLoadExternalInterfaceManifest(t *testing.T) {
dir := t.TempDir()
ifaceDir := filepath.Join(dir, "interfaces")
if err := os.MkdirAll(ifaceDir, 0o700); err != nil {
t.Fatal(err)
}
man := pluginManifest{Driver: "pipe", Command: "true", RespawnDelay: 1}
data, err := json.Marshal(man)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(ifaceDir, "ExamplePlugin.json"), data, 0o600); err != nil {
t.Fatal(err)
}
cfg := &common.InterfaceConfig{Type: "ExamplePlugin", Enabled: true}
ctx := &FromConfigContext{ConfigDir: dir}
iface, err := loadExternalInterface("ex", cfg, ctx)
if err != nil {
t.Fatal(err)
}
pi, ok := iface.(*PipeInterface)
if !ok {
t.Fatalf("got %T, want *PipeInterface", iface)
}
if pi.GetName() != "ex" {
t.Fatalf("name: got %q", pi.GetName())
}
}
func TestRegisterExternalFactory(t *testing.T) {
const typeName = "TestFactoryIface"
defer UnregisterExternalFactory(typeName)
RegisterExternalFactory(typeName, func(name string, cfg *common.InterfaceConfig, ctx *FromConfigContext) (Interface, error) {
return NewUDPInterface(name, "127.0.0.1:0", "127.0.0.1:9", true)
})
cfg := &common.InterfaceConfig{Type: typeName, Enabled: true}
iface, err := NewFromConfigWithContext("factory", cfg, &FromConfigContext{})
if err != nil {
t.Fatal(err)
}
if _, ok := iface.(*UDPInterface); !ok {
t.Fatalf("got %T, want *UDPInterface", iface)
}
}