package store import ( "path/filepath" "testing" ) func TestLocalRemoteLinkRoundTrip(t *testing.T) { dir := t.TempDir() st, err := New(filepath.Join(dir, "test.db")) if err != nil { t.Fatal(err) } defer st.Close() if err := st.UpsertLocal(Local{Name: "web", IP: "127.0.0.1", Port: 8080, Protocol: "tcp"}); err != nil { t.Fatal(err) } if err := st.UpsertRemote(Remote{Name: "srv-a", IP: "1.2.3.4", Port: 7000, Token: "tok", Enabled: true}); err != nil { t.Fatal(err) } if err := st.ReplaceLinks([]Link{{Local: "web", Remote: "srv-a", RemotePort: 8080}}); err != nil { t.Fatal(err) } locals, _ := st.ListLocals() if len(locals) != 1 || locals[0].Name != "web" { t.Fatalf("locals = %+v", locals) } remotes, _ := st.ListRemotes() if len(remotes) != 1 || remotes[0].Token != "tok" { t.Fatalf("remotes = %+v", remotes) } fwds, err := st.LinksForRemote("srv-a") if err != nil || len(fwds) != 1 { t.Fatalf("forwards = %+v err=%v", fwds, err) } if fwds[0].LocalPort != 8080 || fwds[0].RemotePort != 8080 { t.Fatalf("forward = %+v", fwds[0]) } } func TestReplaceLinksClearsOld(t *testing.T) { dir := t.TempDir() st, _ := New(filepath.Join(dir, "test.db")) defer st.Close() _ = st.UpsertLocal(Local{Name: "a", IP: "127.0.0.1", Port: 1, Protocol: "tcp"}) _ = st.UpsertLocal(Local{Name: "b", IP: "127.0.0.1", Port: 2, Protocol: "tcp"}) _ = st.UpsertRemote(Remote{Name: "r", IP: "1.2.3.4", Port: 7000, Enabled: true}) if err := st.ReplaceLinks([]Link{{Local: "a", Remote: "r", RemotePort: 1}}); err != nil { t.Fatal(err) } if err := st.ReplaceLinks([]Link{{Local: "b", Remote: "r", RemotePort: 2}}); err != nil { t.Fatal(err) } links, _ := st.ListLinks() if len(links) != 1 || links[0].Local != "b" { t.Fatalf("links = %+v", links) } } // `usr` is a reserved SQL keyword in several dialects, so the remotes column is // named `usr`. This guards the mapping in both directions, including the // migrate() path that adds the column to pre-existing databases. func TestRemoteUserRoundTrip(t *testing.T) { st, err := New(filepath.Join(t.TempDir(), "test.db")) if err != nil { t.Fatal(err) } defer st.Close() // A remote written without user (existing installs) must read back empty. if err := st.UpsertRemote(Remote{Name: "plain", IP: "1.2.3.4", Port: 7000, Enabled: true}); err != nil { t.Fatal(err) } if got, ok := st.GetRemote("plain"); !ok || got.User != "" { t.Fatalf("plain remote user = %q ok=%v, want empty", got.User, ok) } want := Remote{Name: "panel", IP: "103.40.13.126", Port: 59999, Token: "tk", User: "e8f52adfaf0e5c95", Enabled: true} if err := st.UpsertRemote(want); err != nil { t.Fatal(err) } got, ok := st.GetRemote("panel") if !ok || got.User != want.User { t.Fatalf("user not persisted: %+v", got) } list, err := st.ListRemotes() if err != nil { t.Fatal(err) } var found bool for _, r := range list { if r.Name == "panel" { found = true if r.User != want.User { t.Fatalf("ListRemotes user = %q, want %q", r.User, want.User) } } } if !found { t.Fatal("panel remote missing from ListRemotes") } // Updating in place must not drop the column, and clearing it must work. want.Token = "tk2" if err := st.UpsertRemote(want); err != nil { t.Fatal(err) } if got, _ := st.GetRemote("panel"); got.User != want.User || got.Token != "tk2" { t.Fatalf("upsert lost fields: %+v", got) } want.User = "" if err := st.UpsertRemote(want); err != nil { t.Fatal(err) } if got, _ := st.GetRemote("panel"); got.User != "" { t.Fatalf("user not cleared: %q", got.User) } }