package main import ( "os" "os/exec" "path/filepath" "strings" "testing" "git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/cache" "git.zb-server.de/Sebi/ssh-netbox-wrapper/internal/config" ) // TestNetsshHelper runs main() with the arguments in NETSSH_TEST_ARGS. func TestNetsshHelper(t *testing.T) { if args := os.Getenv("NETSSH_TEST_ARGS"); args != "" { os.Args = append([]string{"netssh"}, strings.Split(args, "\x1f")...) main() os.Exit(0) } } // prepareTransferTest writes a config whose only host is cached; NetBox is // unreachable, so every result must come from cache, shortcuts, and options. func prepareTransferTest(t *testing.T) { t.Helper() dir := t.TempDir() t.Setenv("HOME", t.TempDir()) t.Setenv("XDG_CONFIG_HOME", dir) t.Setenv("XDG_CACHE_HOME", t.TempDir()) t.Setenv("NETSSH_NETBOX_URL", "") t.Setenv("NETSSH_NETBOX_TOKEN", "") t.Setenv("NETSSH_CACHE_PATH", filepath.Join(t.TempDir(), "hosts.json")) t.Setenv("NETSSH_SHELL_ALIAS", "") data := `netbox: url: http://127.0.0.1:1 token: t ssh: default_user: ops default_port: 2222 extra_args: ["-A"] forwards: web: {local: "8080:localhost:80"} rules: - match: {tag: dmz} proxy_jump: jh.example.com forwards: socks: {dynamic: "1080"} shortcuts: domains: [.hoster.de] rewrites: - pattern: '^([a-z]+)[0-9]+-' replace: '$1' ` if err := os.WriteFile(filepath.Join(dir, "netssh.yaml"), []byte(data), 0o600); err != nil { t.Fatal(err) } cfg, err := config.Load() if err != nil { t.Fatal(err) } c := cache.New(cfg.CachePath(), 3600) c.Upsert(cache.Entry{Name: "fsn1-bx1.hoster.de", IP: "10.0.0.9", Kind: "device", Tags: []string{"dmz"}}) if err := c.Save(); err != nil { t.Fatal(err) } } func runNetssh(t *testing.T, args ...string) (string, error) { t.Helper() cmd := exec.Command(os.Args[0], "-test.run=^TestNetsshHelper$") cmd.Env = append(os.Environ(), "NETSSH_TEST_ARGS="+strings.Join(args, "\x1f")) out, err := cmd.CombinedOutput() return strings.TrimSpace(string(out)), err } func TestTransferAndTunnelCommands(t *testing.T) { prepareTransferTest(t) for _, tc := range []struct { args []string want string }{ // sftp: -P and -o User, rule jump host, path kept, no ssh-only extra_args. {[]string{"sftp", "--dry-run", "fsnbx1:/var/log"}, "sftp -P 2222 -o User=ops -J jh.example.com 10.0.0.9:/var/log"}, {[]string{"sftp", "--dry-run", "-P", "22", "root@fsnbx1"}, "sftp -P 22 -J jh.example.com root@10.0.0.9"}, {[]string{"scp", "--dry-run", "-r", "a.txt", "fsn1-bx1.hoster.de:/tmp/"}, "scp -P 2222 -o User=ops -r -J jh.example.com a.txt 10.0.0.9:/tmp/"}, {[]string{"scp", "--dry-run", "--no-jump", "fsnbx1:/etc/hosts", "."}, "scp -P 2222 -o User=ops -o ProxyJump=none 10.0.0.9:/etc/hosts ."}, // Forwards come from the global options and the matching rule. {[]string{"tunnel", "--dry-run", "fsnbx1", "web", "socks", "5432:db:5432"}, "ssh -A -p 2222 -l ops -J jh.example.com -L 8080:localhost:80 -D 1080 -L 5432:db:5432 -o ExitOnForwardFailure=yes -N 10.0.0.9"}, {[]string{"--dry-run", "--forward", "web", "fsnbx1"}, "ssh -A -p 2222 -l ops -J jh.example.com -L 8080:localhost:80 10.0.0.9"}, } { out, err := runNetssh(t, tc.args...) if err != nil || out != tc.want { t.Errorf("%v:\n got %s (%v)\n want %s", tc.args, out, err, tc.want) } } for _, tc := range []struct { args []string want string }{ {[]string{"tunnel", "--dry-run", "fsnbx1"}, "forwards for fsn1-bx1.hoster.de: socks, web"}, {[]string{"tunnel", "--dry-run", "fsnbx1", "db"}, `unknown forward "db"; available: socks, web`}, {[]string{"sftp", "--forward", "web", "fsnbx1"}, "only available for ssh"}, {[]string{"scp", "fsnbx1:a", "other:b"}, "not supported"}, } { out, err := runNetssh(t, tc.args...) if err == nil || !strings.Contains(out, tc.want) { t.Errorf("%v: got %s (%v), want error containing %q", tc.args, out, err, tc.want) } } } func TestTransferCompletion(t *testing.T) { prepareTransferTest(t) for _, tc := range []struct { args []string want string }{ {[]string{"__complete", "sftp", "fsnb"}, "fsnbx1\n"}, {[]string{"__complete", "scp", "a.txt", "fsnb"}, "fsnbx1:\n"}, {[]string{"__complete", "tunnel", "fsnbx1", ""}, "socks\nweb\n"}, {[]string{"__complete", "tunnel", "fsnbx1", "web", ""}, "socks\n"}, {[]string{"__complete", "--forward", ""}, "socks\nweb\n"}, } { out, err := runNetssh(t, tc.args...) if err != nil || !strings.HasPrefix(out+"\n", tc.want) { t.Errorf("%v: got %q (%v), want prefix %q", tc.args, out, err, tc.want) } } }