esync

Directory watching and remote syncing
Log | Files | Refs | README | LICENSE

integration_test.go (4017B)


      1 package main
      2 
      3 import (
      4 	"os"
      5 	"path/filepath"
      6 	"testing"
      7 	"time"
      8 
      9 	"github.com/louloulibs/esync/internal/config"
     10 	"github.com/louloulibs/esync/internal/syncer"
     11 	"github.com/louloulibs/esync/internal/watcher"
     12 )
     13 
     14 // TestLocalSyncIntegration verifies that the Syncer can rsync files between
     15 // two local directories. This test requires rsync to be installed.
     16 func TestLocalSyncIntegration(t *testing.T) {
     17 	// Ensure rsync is available
     18 	if _, err := os.Stat("/usr/bin/rsync"); err != nil {
     19 		if _, err2 := os.Stat("/opt/homebrew/bin/rsync"); err2 != nil {
     20 			t.Skip("rsync not found, skipping integration test")
     21 		}
     22 	}
     23 
     24 	// 1. Create two temp dirs (src, dst)
     25 	src := t.TempDir()
     26 	dst := t.TempDir()
     27 
     28 	// 2. Write a test file to src
     29 	testContent := "hello from integration test\n"
     30 	testFile := filepath.Join(src, "testfile.txt")
     31 	if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
     32 		t.Fatalf("failed to write test file: %v", err)
     33 	}
     34 
     35 	// 3. Create a Config pointing src -> dst with archive=true, progress=true
     36 	cfg := &config.Config{
     37 		Sync: config.SyncSection{
     38 			Local:  src,
     39 			Remote: dst,
     40 		},
     41 		Settings: config.Settings{
     42 			Rsync: config.RsyncSettings{
     43 				Archive:  true,
     44 				Progress: true,
     45 			},
     46 		},
     47 	}
     48 
     49 	// 4. Create a Syncer, run it
     50 	s := syncer.New(cfg)
     51 	result, err := s.Run()
     52 
     53 	// 5. Verify: no error, result.Success is true
     54 	if err != nil {
     55 		t.Fatalf("syncer.Run() returned error: %v", err)
     56 	}
     57 	if !result.Success {
     58 		t.Fatalf("expected result.Success=true, got false; error: %s", result.ErrorMessage)
     59 	}
     60 
     61 	// 6. Verify: the file exists in dst with correct contents
     62 	dstFile := filepath.Join(dst, "testfile.txt")
     63 	got, err := os.ReadFile(dstFile)
     64 	if err != nil {
     65 		t.Fatalf("failed to read synced file %s: %v", dstFile, err)
     66 	}
     67 	if string(got) != testContent {
     68 		t.Errorf("synced file content mismatch:\n  got:  %q\n  want: %q", string(got), testContent)
     69 	}
     70 }
     71 
     72 // TestWatcherTriggersSync verifies that the watcher detects a new file and
     73 // triggers a sync from src to dst. This test requires rsync to be installed.
     74 func TestWatcherTriggersSync(t *testing.T) {
     75 	// Ensure rsync is available
     76 	if _, err := os.Stat("/usr/bin/rsync"); err != nil {
     77 		if _, err2 := os.Stat("/opt/homebrew/bin/rsync"); err2 != nil {
     78 			t.Skip("rsync not found, skipping integration test")
     79 		}
     80 	}
     81 
     82 	// 1. Create two temp dirs (src, dst)
     83 	src := t.TempDir()
     84 	dst := t.TempDir()
     85 
     86 	// 2. Create Config and Syncer
     87 	cfg := &config.Config{
     88 		Sync: config.SyncSection{
     89 			Local:  src,
     90 			Remote: dst,
     91 		},
     92 		Settings: config.Settings{
     93 			Rsync: config.RsyncSettings{
     94 				Archive:  true,
     95 				Progress: true,
     96 			},
     97 		},
     98 	}
     99 	s := syncer.New(cfg)
    100 
    101 	// 3. Create watcher with handler that runs syncer and signals a channel
    102 	synced := make(chan struct{}, 1)
    103 	handler := func() {
    104 		_, _ = s.Run()
    105 		select {
    106 		case synced <- struct{}{}:
    107 		default:
    108 		}
    109 	}
    110 
    111 	// Use short debounce (100ms) for fast tests
    112 	w, err := watcher.New(src, 100, nil, nil, handler)
    113 	if err != nil {
    114 		t.Fatalf("watcher.New() failed: %v", err)
    115 	}
    116 
    117 	// 4. Start watcher
    118 	if err := w.Start(); err != nil {
    119 		t.Fatalf("watcher.Start() failed: %v", err)
    120 	}
    121 	defer w.Stop()
    122 
    123 	// 5. Wait 200ms for watcher to settle
    124 	time.Sleep(200 * time.Millisecond)
    125 
    126 	// 6. Write a file to src
    127 	testContent := "watcher triggered sync\n"
    128 	testFile := filepath.Join(src, "watched.txt")
    129 	if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
    130 		t.Fatalf("failed to write test file: %v", err)
    131 	}
    132 
    133 	// 7. Wait for signal (with 5s timeout)
    134 	select {
    135 	case <-synced:
    136 		// success - sync was triggered
    137 	case <-time.After(5 * time.Second):
    138 		t.Fatal("timed out waiting for watcher to trigger sync")
    139 	}
    140 
    141 	// 8. Verify: file was synced to dst with correct contents
    142 	dstFile := filepath.Join(dst, "watched.txt")
    143 	got, err := os.ReadFile(dstFile)
    144 	if err != nil {
    145 		t.Fatalf("failed to read synced file %s: %v", dstFile, err)
    146 	}
    147 	if string(got) != testContent {
    148 		t.Errorf("synced file content mismatch:\n  got:  %q\n  want: %q", string(got), testContent)
    149 	}
    150 }