6b52e83bdc
A two-pane local/remote file browser over SSH, split out of dial so file transfer lives in its own tool. The two share ~/.ssh/config as their base. - internal/sshconf: ~/.ssh/config parsing, for the saved-host list. - internal/sshx: dialling and auth — agent, keys, password, host-key verification, ProxyJump. - internal/vfs: the FS interface both sides are driven through, implemented over the local disk and over SFTP. - internal/xfer: recursive copy engine with progress reporting, cancellation, conflict policy, and resume of partial transfers. - internal/ui: the Fyne GUI — connect window (saved hosts plus quick connect) and the two-pane browser session. Build with `make build`; the Makefile locates the Go toolchain at ~/.local/go, which is deliberately kept off PATH. Cross-compiling needs a per-target C toolchain because Fyne is cgo, so `make dist` builds the native binary only — the alternatives are documented in the Makefile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69 lines
1.8 KiB
Go
69 lines
1.8 KiB
Go
package sshx
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/pkg/sftp"
|
|
)
|
|
|
|
// TestLiveConnect exercises the real auth + SFTP path against a live host.
|
|
// Gated on DIAL_LIVE_HOST so the normal test suite never needs a server.
|
|
// DIAL_LIVE_HOST=192.168.192.201 go test ./internal/sshx -run TestLiveConnect -v
|
|
func TestLiveConnect(t *testing.T) {
|
|
alias := os.Getenv("DIAL_LIVE_HOST")
|
|
if alias == "" {
|
|
t.Skip("set DIAL_LIVE_HOST to run the live connection test")
|
|
}
|
|
opts := Options{Logf: func(format string, args ...any) { t.Logf(format, args...) }}
|
|
client, err := Connect(alias, opts)
|
|
if err != nil {
|
|
t.Fatalf("Connect(%q): %v", alias, err)
|
|
}
|
|
defer client.Close()
|
|
|
|
sf, err := sftp.NewClient(client)
|
|
if err != nil {
|
|
t.Fatalf("sftp.NewClient: %v", err)
|
|
}
|
|
defer sf.Close()
|
|
|
|
entries, err := sf.ReadDir(".")
|
|
if err != nil {
|
|
t.Fatalf("ReadDir(.): %v", err)
|
|
}
|
|
for _, e := range entries {
|
|
t.Logf(" %s\t%d bytes", e.Name(), e.Size())
|
|
}
|
|
t.Logf("SFTP OK: %d entries in remote home", len(entries))
|
|
|
|
// Round-trip: write a file, read it back, verify contents, clean up.
|
|
const remote = "dial-live-test.txt"
|
|
want := []byte("dial round-trip @ live test\n")
|
|
w, err := sf.Create(remote)
|
|
if err != nil {
|
|
t.Fatalf("Create(%s): %v", remote, err)
|
|
}
|
|
if _, err := w.Write(want); err != nil {
|
|
t.Fatalf("Write: %v", err)
|
|
}
|
|
w.Close()
|
|
|
|
r, err := sf.Open(remote)
|
|
if err != nil {
|
|
t.Fatalf("Open(%s): %v", remote, err)
|
|
}
|
|
got := make([]byte, len(want))
|
|
if _, err := r.Read(got); err != nil {
|
|
t.Fatalf("Read: %v", err)
|
|
}
|
|
r.Close()
|
|
if string(got) != string(want) {
|
|
t.Fatalf("round-trip mismatch: got %q want %q", got, want)
|
|
}
|
|
if err := sf.Remove(remote); err != nil {
|
|
t.Fatalf("Remove(%s): %v", remote, err)
|
|
}
|
|
t.Logf("round-trip OK: wrote %d bytes, read back identical, removed", len(want))
|
|
}
|