summaryrefslogtreecommitdiffhomepage
path: root/feature/taildrop/integration_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'feature/taildrop/integration_test.go')
-rw-r--r--feature/taildrop/integration_test.go170
1 files changed, 170 insertions, 0 deletions
diff --git a/feature/taildrop/integration_test.go b/feature/taildrop/integration_test.go
new file mode 100644
index 000000000..46768bb31
--- /dev/null
+++ b/feature/taildrop/integration_test.go
@@ -0,0 +1,170 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+package taildrop_test
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "testing"
+ "time"
+
+ "tailscale.com/client/local"
+ "tailscale.com/client/tailscale/apitype"
+ "tailscale.com/tailcfg"
+ "tailscale.com/tstest"
+ "tailscale.com/tstest/integration"
+ "tailscale.com/tstest/integration/testcontrol"
+)
+
+// TODO(bradfitz): add test where control doesn't send tailcfg.CapabilityFileSharing
+// and verify that we get the "file sharing not enabled by Tailscale admin" error.
+
+// TODO(bradfitz): add test between different users with the peercap to permit that?
+
+func TestTaildropIntegration(t *testing.T) {
+ tstest.Parallel(t)
+ controlOpt := integration.ConfigureControl(func(s *testcontrol.Server) {
+ s.AllNodesSameUser = true // required for Taildrop
+ })
+ env := integration.NewTestEnv(t, controlOpt)
+
+ // Create two nodes:
+ n1 := integration.NewTestNode(t, env)
+ d1 := n1.StartDaemon()
+
+ n2 := integration.NewTestNode(t, env)
+ d2 := n2.StartDaemon()
+
+ n1.AwaitListening()
+ t.Logf("n1 is listening")
+ n2.AwaitListening()
+ t.Logf("n2 is listening")
+ n1.MustUp()
+ t.Logf("n1 is up")
+ n2.MustUp()
+ t.Logf("n2 is up")
+ n1.AwaitRunning()
+ t.Logf("n1 is running")
+ n2.AwaitRunning()
+ t.Logf("n2 is running")
+
+ var peerStableID tailcfg.StableNodeID
+
+ if err := tstest.WaitFor(5*time.Second, func() error {
+ st := n1.MustStatus()
+ if len(st.Peer) == 0 {
+ return errors.New("no peers")
+ }
+ if len(st.Peer) > 1 {
+ return fmt.Errorf("got %d peers; want 1", len(st.Peer))
+ }
+ peer := st.Peer[st.Peers()[0]]
+ peerStableID = peer.ID
+ if peer.ID == st.Self.ID {
+ return errors.New("peer is self")
+ }
+
+ if len(st.TailscaleIPs) == 0 {
+ return errors.New("no Tailscale IPs")
+ }
+
+ return nil
+ }); err != nil {
+ t.Fatal(err)
+ }
+
+ const timeout = 30 * time.Second
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+
+ c1 := n1.LocalClient()
+ c2 := n2.LocalClient()
+
+ wantNoWaitingFiles := func(c *local.Client) {
+ t.Helper()
+ files, err := c.WaitingFiles(ctx)
+ if err != nil {
+ t.Fatalf("WaitingFiles: %v", err)
+ }
+ if len(files) != 0 {
+ t.Fatalf("WaitingFiles: got %d files; want 0", len(files))
+ }
+ }
+
+ // Verify c2 has no files.
+ wantNoWaitingFiles(c2)
+
+ gotFile := make(chan bool, 1)
+ go func() {
+ v, err := c2.AwaitWaitingFiles(t.Context(), timeout)
+ if err != nil {
+ return
+ }
+ if len(v) != 0 {
+ gotFile <- true
+ }
+ }()
+
+ fileContents := []byte("hello world this is a file")
+
+ n2ID := n2.MustStatus().Self.ID
+ t.Logf("n2 self.ID = %q; n1's peer[0].ID = %q", n2ID, peerStableID)
+ t.Logf("Doing PushFile ...")
+ err := c1.PushFile(ctx, n2.MustStatus().Self.ID, int64(len(fileContents)), "test.txt", bytes.NewReader(fileContents))
+ if err != nil {
+ t.Fatalf("PushFile from n1->n2: %v", err)
+ }
+ t.Logf("PushFile done")
+
+ select {
+ case <-gotFile:
+ t.Logf("n2 saw AwaitWaitingFiles wake up")
+ case <-ctx.Done():
+ t.Fatalf("n2 timeout waiting for AwaitWaitingFiles")
+ }
+
+ files, err := c2.WaitingFiles(ctx)
+ if err != nil {
+ t.Fatalf("c2.WaitingFiles: %v", err)
+ }
+ if len(files) != 1 {
+ t.Fatalf("c2.WaitingFiles: got %d files; want 1", len(files))
+ }
+ got := files[0]
+ want := apitype.WaitingFile{
+ Name: "test.txt",
+ Size: int64(len(fileContents)),
+ }
+ if got != want {
+ t.Fatalf("c2.WaitingFiles: got %+v; want %+v", got, want)
+ }
+
+ // Download the file.
+ rc, size, err := c2.GetWaitingFile(ctx, got.Name)
+ if err != nil {
+ t.Fatalf("c2.GetWaitingFile: %v", err)
+ }
+ if size != int64(len(fileContents)) {
+ t.Fatalf("c2.GetWaitingFile: got size %d; want %d", size, len(fileContents))
+ }
+ gotBytes, err := io.ReadAll(rc)
+ if err != nil {
+ t.Fatalf("c2.GetWaitingFile: %v", err)
+ }
+ if !bytes.Equal(gotBytes, fileContents) {
+ t.Fatalf("c2.GetWaitingFile: got %q; want %q", gotBytes, fileContents)
+ }
+
+ // Now delete it.
+ if err := c2.DeleteWaitingFile(ctx, got.Name); err != nil {
+ t.Fatalf("c2.DeleteWaitingFile: %v", err)
+ }
+ wantNoWaitingFiles(c2)
+
+ d1.MustCleanShutdown(t)
+ d2.MustCleanShutdown(t)
+}