//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) } } }