Files
netbird/client/ios/NetBirdSDK/filedrop_test.go
T
Zoltán Papp f7da113ac7 [client] Port the file drop bindings to iOS
The iOS SDK package gets the same platform handle the Android bindings
carry: transfers, receiving policy, and per-sender rules on one profile,
attached to the engine when the client starts. Payloads are path-based
rather than stream-based, since the app group container is readable from
both processes, and the platform sink is dropped for the same reason:
delivery lands on the filesystem directly. The default destination is
seeded in the constructor because the app process opens handles of its
own while the extension is down.
2026-08-27 12:43:54 +02:00

183 lines
5.1 KiB
Go

//go:build ios
package NetBirdSDK
import (
"errors"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/netbirdio/netbird/client/internal/filedrop"
)
func TestPayloadFileReadsFromPath(t *testing.T) {
path := filepath.Join(t.TempDir(), "greeting.txt")
if err := os.WriteFile(path, []byte("hello world"), 0o600); err != nil {
t.Fatalf("write payload: %v", err)
}
payloads := NewFileDropPayloads()
if err := payloads.AddFile("greeting.txt", 11, "text/plain", path); err != nil {
t.Fatalf("AddFile: %v", err)
}
if payloads.Length() != 1 {
t.Fatalf("expected 1 payload, got %d", payloads.Length())
}
stream, err := payloads.items[0].Open(0)
if err != nil {
t.Fatalf("Open: %v", err)
}
got, err := io.ReadAll(stream)
if err != nil {
t.Fatalf("ReadAll: %v", err)
}
if string(got) != "hello world" {
t.Fatalf("got %q, want %q", got, "hello world")
}
if err := stream.Close(); err != nil {
t.Fatalf("Close: %v", err)
}
}
func TestPayloadFileHonoursOffset(t *testing.T) {
path := filepath.Join(t.TempDir(), "greeting.txt")
if err := os.WriteFile(path, []byte("hello world"), 0o600); err != nil {
t.Fatalf("write payload: %v", err)
}
payloads := NewFileDropPayloads()
if err := payloads.AddFile("greeting.txt", 11, "", path); err != nil {
t.Fatalf("AddFile: %v", err)
}
stream, err := payloads.items[0].Open(6)
if err != nil {
t.Fatalf("Open: %v", err)
}
defer stream.Close()
got, err := io.ReadAll(stream)
if err != nil {
t.Fatalf("ReadAll: %v", err)
}
if string(got) != "world" {
t.Fatalf("got %q, want %q", got, "world")
}
}
func TestPayloadRejectsMissingPathAndOversizedText(t *testing.T) {
payloads := NewFileDropPayloads()
if err := payloads.AddFile("no-path.bin", 1, "", ""); err == nil {
t.Fatal("expected an error for a file without a path")
}
if err := payloads.AddFile("", 1, "", "/tmp/x"); err == nil {
t.Fatal("expected an error for an empty file name")
}
if err := payloads.AddText("big", strings.Repeat("x", filedrop.MaxInlineTextSize+1)); err == nil {
t.Fatal("expected an error for oversized text")
}
if payloads.Length() != 0 {
t.Fatalf("expected no payloads, got %d", payloads.Length())
}
}
func TestFileDropPersistsSettingsPerProfile(t *testing.T) {
configDir := t.TempDir()
writeTestProfile(t, configDir, "aaaaaaaabbbbbbbbccccccccdddddddd")
writeTestProfile(t, configDir, "11111111222222223333333344444444")
first, err := NewFileDrop(configDir, "aaaaaaaabbbbbbbbccccccccdddddddd")
if err != nil {
t.Fatalf("NewFileDrop: %v", err)
}
defer first.Close()
if err := first.SetMode(FileDropModeAutoAccept); err != nil {
t.Fatalf("SetMode: %v", err)
}
if err := first.SetPeerRule("peer-key", FileDropRuleBlock); err != nil {
t.Fatalf("SetPeerRule: %v", err)
}
second, err := NewFileDrop(configDir, "11111111222222223333333344444444")
if err != nil {
t.Fatalf("NewFileDrop: %v", err)
}
defer second.Close()
if got := second.Mode(); got != FileDropModeAsk {
t.Fatalf("second profile mode = %d, want the default %d", got, FileDropModeAsk)
}
if got := second.PeerRule("peer-key"); got != FileDropRuleDefault {
t.Fatalf("second profile rule = %d, want %d", got, FileDropRuleDefault)
}
reopened, err := NewFileDrop(configDir, "aaaaaaaabbbbbbbbccccccccdddddddd")
if err != nil {
t.Fatalf("NewFileDrop: %v", err)
}
defer reopened.Close()
if got := reopened.Mode(); got != FileDropModeAutoAccept {
t.Fatalf("reopened mode = %d, want %d", got, FileDropModeAutoAccept)
}
if got := reopened.PeerRule("peer-key"); got != FileDropRuleBlock {
t.Fatalf("reopened rule = %d, want %d", got, FileDropRuleBlock)
}
}
func TestFileDropSeedsDefaultDestination(t *testing.T) {
configDir := t.TempDir()
writeTestProfile(t, configDir, "aaaaaaaabbbbbbbbccccccccdddddddd")
fd, err := NewFileDrop(configDir, "aaaaaaaabbbbbbbbccccccccdddddddd")
if err != nil {
t.Fatalf("NewFileDrop: %v", err)
}
defer fd.Close()
want := filepath.Join(configDir, filedropDataSubdir, "aaaaaaaabbbbbbbbccccccccdddddddd", "incoming")
if got := fd.DestinationDir(); got != want {
t.Fatalf("destination = %q, want %q", got, want)
}
}
func TestFileDropSendWithoutTunnelFails(t *testing.T) {
configDir := t.TempDir()
writeTestProfile(t, configDir, "aaaaaaaabbbbbbbbccccccccdddddddd")
fd, err := NewFileDrop(configDir, "aaaaaaaabbbbbbbbccccccccdddddddd")
if err != nil {
t.Fatalf("NewFileDrop: %v", err)
}
defer fd.Close()
payloads := NewFileDropPayloads()
if err := payloads.AddText("note", "hi"); err != nil {
t.Fatalf("AddText: %v", err)
}
if _, err := fd.Send("peer-key", "peer", "100.64.0.2", payloads); !errors.Is(err, filedrop.ErrNotConnected) {
t.Fatalf("Send error = %v, want %v", err, filedrop.ErrNotConnected)
}
if _, err := fd.Send("peer-key", "peer", "100.64.0.2", NewFileDropPayloads()); err == nil {
t.Fatal("expected an error when there is nothing to send")
}
if _, err := fd.Send("peer-key", "peer", "not-an-ip", payloads); err == nil {
t.Fatal("expected an error for an unparseable peer address")
}
}
func writeTestProfile(t *testing.T, configDir, id string) {
t.Helper()
if _, err := NewProfileManager(configDir).impl.ProfilePrefs(id); err != nil {
t.Fatalf("resolve prefs for %s: %v", id, err)
}
}