161 lines
4.4 KiB
Go
161 lines
4.4 KiB
Go
package application
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mmmy/snapgo/internal/domain"
|
|
)
|
|
|
|
type fakeFTPUploader struct {
|
|
path string
|
|
data []byte
|
|
err error
|
|
}
|
|
|
|
func (f *fakeFTPUploader) Upload(_ context.Context, remoteRelPath string, data []byte) error {
|
|
f.path = remoteRelPath
|
|
f.data = append([]byte(nil), data...)
|
|
return f.err
|
|
}
|
|
|
|
func TestCaptureAndFTPServiceUploadsAndCopiesFTPPath(t *testing.T) {
|
|
uploader := &fakeFTPUploader{}
|
|
clip := &fakeClipboard{}
|
|
notifier := &fakeNotifier{}
|
|
svc := &CaptureAndFTPService{
|
|
Uploader: uploader,
|
|
Clipboard: clip,
|
|
Notifier: notifier,
|
|
Cfg: domain.FTPConfig{
|
|
Protocol: domain.FTPProtocolFTP,
|
|
PathPrefix: "snapgo/",
|
|
},
|
|
}
|
|
|
|
if err := svc.ExecuteWithBytes(context.Background(), []byte("png")); err != nil {
|
|
t.Fatalf("upload FTP screenshot: %v", err)
|
|
}
|
|
if string(uploader.data) != "png" {
|
|
t.Fatalf("expected PNG bytes, got %q", string(uploader.data))
|
|
}
|
|
if !strings.HasPrefix(uploader.path, "snapgo/") || !strings.HasSuffix(uploader.path, ".png") {
|
|
t.Fatalf("unexpected remote path %q", uploader.path)
|
|
}
|
|
if clip.text != "/"+uploader.path {
|
|
t.Fatalf("expected FTP root path copied, got %q", clip.text)
|
|
}
|
|
if notifier.success != clip.text {
|
|
t.Fatalf("expected success notification %q, got %q", clip.text, notifier.success)
|
|
}
|
|
}
|
|
|
|
func TestCaptureAndFTPServiceCopiesConfiguredRootPath(t *testing.T) {
|
|
uploader := &fakeFTPUploader{}
|
|
clip := &fakeClipboard{}
|
|
svc := &CaptureAndFTPService{
|
|
Uploader: uploader,
|
|
Clipboard: clip,
|
|
Cfg: domain.FTPConfig{
|
|
Protocol: domain.FTPProtocolSFTP,
|
|
RootDirectory: "~/ftp/",
|
|
PathPrefix: "images",
|
|
},
|
|
}
|
|
|
|
if err := svc.ExecuteWithBytes(context.Background(), []byte("png")); err != nil {
|
|
t.Fatalf("upload SFTP screenshot: %v", err)
|
|
}
|
|
if clip.text != "~/ftp/"+uploader.path {
|
|
t.Fatalf("expected configured SFTP root path copied, got %q", clip.text)
|
|
}
|
|
}
|
|
|
|
func TestFTPShareTextJoinsConfiguredRootAndRemoteDirectory(t *testing.T) {
|
|
const relPath = "snapgo/2026/07/20260713-110413-18lnbx.png"
|
|
const want = "~/ftp/snapgo/2026/07/20260713-110413-18lnbx.png"
|
|
if got := ftpShareText(domain.FTPProtocolSFTP, "~/ftp/", relPath); got != want {
|
|
t.Fatalf("ftpShareText() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFTPShareTextUsesProtocolDefaultWhenRootDirectoryIsEmpty(t *testing.T) {
|
|
const relPath = "snapgo/2026/07/image.png"
|
|
tests := []struct {
|
|
name string
|
|
protocol string
|
|
want string
|
|
}{
|
|
{name: "FTP", protocol: domain.FTPProtocolFTP, want: "/" + relPath},
|
|
{name: "SFTP", protocol: domain.FTPProtocolSFTP, want: "~/" + relPath},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := ftpShareText(tt.protocol, "", relPath); got != tt.want {
|
|
t.Fatalf("ftpShareText() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCaptureAndFTPServiceRejectsTraversalBeforeUpload(t *testing.T) {
|
|
uploader := &fakeFTPUploader{}
|
|
notifier := &fakeNotifier{}
|
|
svc := &CaptureAndFTPService{
|
|
Uploader: uploader,
|
|
Notifier: notifier,
|
|
Cfg: domain.FTPConfig{
|
|
Protocol: domain.FTPProtocolSFTP,
|
|
PathPrefix: "../../outside",
|
|
},
|
|
}
|
|
|
|
err := svc.ExecuteWithBytes(context.Background(), []byte("png"))
|
|
if err == nil {
|
|
t.Fatal("expected traversal prefix to fail")
|
|
}
|
|
if uploader.path != "" {
|
|
t.Fatalf("uploader should not run, got path %q", uploader.path)
|
|
}
|
|
if notifier.failure == "" {
|
|
t.Fatal("expected failure notification")
|
|
}
|
|
}
|
|
|
|
func TestCaptureAndFTPServiceSurfacesUploadFailure(t *testing.T) {
|
|
want := errors.New("server unavailable")
|
|
uploader := &fakeFTPUploader{err: want}
|
|
clip := &fakeClipboard{}
|
|
notifier := &fakeNotifier{}
|
|
svc := &CaptureAndFTPService{
|
|
Uploader: uploader,
|
|
Clipboard: clip,
|
|
Notifier: notifier,
|
|
Cfg: domain.FTPConfig{Protocol: domain.FTPProtocolFTP},
|
|
}
|
|
|
|
err := svc.ExecuteWithBytes(context.Background(), []byte("png"))
|
|
if !errors.Is(err, want) {
|
|
t.Fatalf("expected %v, got %v", want, err)
|
|
}
|
|
if clip.text != "" {
|
|
t.Fatalf("clipboard should remain empty, got %q", clip.text)
|
|
}
|
|
if !strings.Contains(notifier.failure, "server unavailable") {
|
|
t.Fatalf("unexpected failure notification %q", notifier.failure)
|
|
}
|
|
}
|
|
|
|
func TestBuildFTPRemotePathNormalizesLoginRootMarkers(t *testing.T) {
|
|
remotePath, err := buildFTPRemotePath(" ~////snapgo/ ")
|
|
if err != nil {
|
|
t.Fatalf("build remote path: %v", err)
|
|
}
|
|
if strings.HasPrefix(remotePath, "/") || !strings.HasPrefix(remotePath, "snapgo/") {
|
|
t.Fatalf("expected relative snapgo path, got %q", remotePath)
|
|
}
|
|
}
|