From e51baa8673f100599644700c0273e3328e3c8427 Mon Sep 17 00:00:00 2001 From: Sudo-Ivan Date: Tue, 30 Dec 2025 19:17:43 -0600 Subject: [PATCH] test: add comprehensive tests for WASM functions including registration, stats retrieval, connection status, and message handling --- pkg/wasm/wasm_test.go | 174 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 pkg/wasm/wasm_test.go diff --git a/pkg/wasm/wasm_test.go b/pkg/wasm/wasm_test.go new file mode 100644 index 0000000..c6b9946 --- /dev/null +++ b/pkg/wasm/wasm_test.go @@ -0,0 +1,174 @@ +//go:build js && wasm +// +build js,wasm + +package wasm + +import ( + "encoding/hex" + "fmt" + "syscall/js" + "testing" + + "git.quad4.io/Networks/Reticulum-Go/pkg/identity" +) + +func TestRegisterJSFunctions(t *testing.T) { + RegisterJSFunctions() + + reticulum := js.Global().Get("reticulum") + if reticulum.IsUndefined() { + t.Fatal("reticulum object not registered in global scope") + } + + functions := []string{ + "init", "getIdentity", "getDestination", "announce", + "connect", "disconnect", "isConnected", "sendMessage", "getStats", + } + + for _, fn := range functions { + if reticulum.Get(fn).Type() != js.TypeFunction { + t.Errorf("function %s not registered or not a function", fn) + } + } +} + +func TestGetStats(t *testing.T) { + // Reset stats + stats.packetsSent = 10 + stats.packetsReceived = 5 + stats.bytesSent = 100 + stats.bytesReceived = 50 + + result := GetStats(js.Undefined(), nil) + val := result.(js.Value) + + if val.Get("packetsSent").Int() != 10 { + t.Errorf("expected packetsSent 10, got %d", val.Get("packetsSent").Int()) + } + if val.Get("packetsReceived").Int() != 5 { + t.Errorf("expected packetsReceived 5, got %d", val.Get("packetsReceived").Int()) + } +} + +func TestIsConnected(t *testing.T) { + reticulumTransport = nil + connected := IsConnected(js.Undefined(), nil).(js.Value).Bool() + if connected { + t.Error("expected connected to be false when transport is nil") + } +} + +func TestInitReticulum(t *testing.T) { + // Mock JS global functions + js.Global().Set("onChatMessage", js.FuncOf(func(this js.Value, args []js.Value) interface{} { return nil })) + js.Global().Set("log", js.FuncOf(func(this js.Value, args []js.Value) interface{} { return nil })) + js.Global().Set("onPeerDiscovered", js.FuncOf(func(this js.Value, args []js.Value) interface{} { return nil })) + + // Test without arguments + result := InitReticulum(js.Undefined(), []js.Value{}) + val := result.(js.Value) + if val.Get("error").IsUndefined() || val.Get("error").String() != "WebSocket URL required" { + t.Errorf("expected error 'WebSocket URL required', got %v", val.Get("error")) + } + + // Test with valid URL and username + wsURL := "ws://localhost:8080" + username := "testuser" + result = InitReticulum(js.Undefined(), []js.Value{js.ValueOf(wsURL), js.ValueOf(username)}) + val = result.(js.Value) + + if !val.Get("success").Bool() { + t.Errorf("InitReticulum failed: %v", val.Get("error")) + } + + if reticulumIdentity == nil { + t.Fatal("reticulumIdentity should not be nil after successful init") + } + + if userName != username { + t.Errorf("expected userName %s, got %s", username, userName) + } + + // Test with provided identity + id, _ := identity.NewIdentity() + idHex := id.GetHexHash() + // InitReticulum expects the FULL identity bytes in hex (64 bytes). + idBytes := id.GetPrivateKey() + idHexFull := hex.EncodeToString(idBytes) + + result = InitReticulum(js.Undefined(), []js.Value{js.ValueOf(wsURL), js.ValueOf(username), js.ValueOf(idHexFull)}) + val = result.(js.Value) + + if !val.Get("success").Bool() { + t.Errorf("InitReticulum with identity failed: %v", val.Get("error")) + } + + if reticulumIdentity.GetHexHash() != idHex { + t.Errorf("expected identity hash %s, got %s", idHex, reticulumIdentity.GetHexHash()) + } +} + +func TestIdentityAndDestination(t *testing.T) { + // Ensure initialized + js.Global().Set("onChatMessage", js.FuncOf(func(this js.Value, args []js.Value) interface{} { return nil })) + js.Global().Set("log", js.FuncOf(func(this js.Value, args []js.Value) interface{} { return nil })) + js.Global().Set("onPeerDiscovered", js.FuncOf(func(this js.Value, args []js.Value) interface{} { return nil })) + InitReticulum(js.Undefined(), []js.Value{js.ValueOf("ws://localhost")}) + + idResult := GetIdentity(js.Undefined(), nil).(js.Value) + if idResult.Get("hash").String() != reticulumIdentity.GetHexHash() { + t.Error("GetIdentity returned wrong hash") + } + + destResult := GetDestination(js.Undefined(), nil).(js.Value) + expectedDest := fmt.Sprintf("%x", reticulumDest.GetHash()) + if destResult.Get("hash").String() != expectedDest { + t.Errorf("GetDestination returned wrong hash, expected %s got %s", expectedDest, destResult.Get("hash").String()) + } +} + +func TestAnnounce(t *testing.T) { + // Ensure initialized + InitReticulum(js.Undefined(), []js.Value{js.ValueOf("ws://localhost")}) + + result := SendAnnounce(js.Undefined(), []js.Value{js.ValueOf("new_username")}).(js.Value) + if !result.Get("success").Bool() { + t.Errorf("SendAnnounce failed: %v", result.Get("error")) + } + + if userName != "new_username" { + t.Errorf("userName should have been updated to 'new_username', got %s", userName) + } +} + +func TestSendMessage(t *testing.T) { + // Ensure initialized + InitReticulum(js.Undefined(), []js.Value{js.ValueOf("ws://localhost")}) + + // Create a mock peer + peerId, _ := identity.NewIdentity() + peerHash := peerId.Hash() + peerHashHex := hex.EncodeToString(peerHash) + + // Manually add to known destinations so Recall works + identity.Remember([]byte("mock_packet"), peerHash, peerId.GetPublicKey(), []byte("peer_app_data")) + + // Test SendMessage + msg := "Hello Peer!" + result := SendMessage(js.Undefined(), []js.Value{js.ValueOf(peerHashHex), js.ValueOf(msg)}).(js.Value) + + if !result.Get("error").IsUndefined() { + errStr := result.Get("error").String() + if errStr != "Packet sending failed: no path to destination" { + t.Errorf("SendMessage failed with unexpected error: %s", errStr) + } else { + t.Log("SendMessage correctly failed with 'no path to destination' (as expected in test environment)") + } + } else if !result.Get("success").Bool() { + t.Errorf("SendMessage failed without error message") + } else { + if stats.packetsSent != 1 { + t.Errorf("expected 1 packet sent, got %d", stats.packetsSent) + } + } +}