wraith/internal/connections/service_test.go
Vantz Stockwell e8ed0139b3 feat: connection + group CRUD with JSON tags and options
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 06:17:31 -04:00

235 lines
5.6 KiB
Go

package connections
import (
"path/filepath"
"testing"
"github.com/vstockwell/wraith/internal/db"
)
func strPtr(s string) *string { return &s }
func setupTestService(t *testing.T) *ConnectionService {
t.Helper()
dir := t.TempDir()
database, err := db.Open(filepath.Join(dir, "test.db"))
if err != nil {
t.Fatalf("db.Open() error: %v", err)
}
if err := db.Migrate(database); err != nil {
t.Fatalf("db.Migrate() error: %v", err)
}
t.Cleanup(func() { database.Close() })
return NewConnectionService(database)
}
func TestCreateGroup(t *testing.T) {
svc := setupTestService(t)
g, err := svc.CreateGroup("Servers", nil)
if err != nil {
t.Fatalf("CreateGroup() error: %v", err)
}
if g.ID == 0 {
t.Error("expected non-zero ID")
}
if g.Name != "Servers" {
t.Errorf("Name = %q, want %q", g.Name, "Servers")
}
if g.ParentID != nil {
t.Errorf("ParentID = %v, want nil", g.ParentID)
}
}
func TestCreateSubGroup(t *testing.T) {
svc := setupTestService(t)
parent, err := svc.CreateGroup("Servers", nil)
if err != nil {
t.Fatalf("CreateGroup(parent) error: %v", err)
}
child, err := svc.CreateGroup("Production", &parent.ID)
if err != nil {
t.Fatalf("CreateGroup(child) error: %v", err)
}
if child.ParentID == nil {
t.Fatal("expected non-nil ParentID")
}
if *child.ParentID != parent.ID {
t.Errorf("ParentID = %d, want %d", *child.ParentID, parent.ID)
}
}
func TestListGroups(t *testing.T) {
svc := setupTestService(t)
parent, err := svc.CreateGroup("Servers", nil)
if err != nil {
t.Fatalf("CreateGroup(parent) error: %v", err)
}
if _, err := svc.CreateGroup("Production", &parent.ID); err != nil {
t.Fatalf("CreateGroup(child) error: %v", err)
}
groups, err := svc.ListGroups()
if err != nil {
t.Fatalf("ListGroups() error: %v", err)
}
if len(groups) != 1 {
t.Fatalf("len(groups) = %d, want 1 (only root groups)", len(groups))
}
if groups[0].Name != "Servers" {
t.Errorf("groups[0].Name = %q, want %q", groups[0].Name, "Servers")
}
if len(groups[0].Children) != 1 {
t.Fatalf("len(children) = %d, want 1", len(groups[0].Children))
}
if groups[0].Children[0].Name != "Production" {
t.Errorf("child name = %q, want %q", groups[0].Children[0].Name, "Production")
}
}
func TestDeleteGroup(t *testing.T) {
svc := setupTestService(t)
g, err := svc.CreateGroup("ToDelete", nil)
if err != nil {
t.Fatalf("CreateGroup() error: %v", err)
}
if err := svc.DeleteGroup(g.ID); err != nil {
t.Fatalf("DeleteGroup() error: %v", err)
}
groups, err := svc.ListGroups()
if err != nil {
t.Fatalf("ListGroups() error: %v", err)
}
if len(groups) != 0 {
t.Errorf("len(groups) = %d, want 0 after delete", len(groups))
}
}
func TestCreateConnection(t *testing.T) {
svc := setupTestService(t)
conn, err := svc.CreateConnection(CreateConnectionInput{
Name: "Web Server",
Hostname: "10.0.0.1",
Port: 22,
Protocol: "ssh",
Tags: []string{"Prod", "Linux"},
Options: `{"keepAliveInterval": 60}`,
})
if err != nil {
t.Fatalf("CreateConnection() error: %v", err)
}
if conn.ID == 0 {
t.Error("expected non-zero ID")
}
if conn.Name != "Web Server" {
t.Errorf("Name = %q, want %q", conn.Name, "Web Server")
}
if len(conn.Tags) != 2 {
t.Fatalf("len(Tags) = %d, want 2", len(conn.Tags))
}
if conn.Tags[0] != "Prod" || conn.Tags[1] != "Linux" {
t.Errorf("Tags = %v, want [Prod Linux]", conn.Tags)
}
if conn.Options != `{"keepAliveInterval": 60}` {
t.Errorf("Options = %q, want JSON blob", conn.Options)
}
}
func TestListConnections(t *testing.T) {
svc := setupTestService(t)
if _, err := svc.CreateConnection(CreateConnectionInput{
Name: "Server A",
Hostname: "10.0.0.1",
Port: 22,
Protocol: "ssh",
}); err != nil {
t.Fatalf("CreateConnection(A) error: %v", err)
}
if _, err := svc.CreateConnection(CreateConnectionInput{
Name: "Server B",
Hostname: "10.0.0.2",
Port: 3389,
Protocol: "rdp",
}); err != nil {
t.Fatalf("CreateConnection(B) error: %v", err)
}
conns, err := svc.ListConnections()
if err != nil {
t.Fatalf("ListConnections() error: %v", err)
}
if len(conns) != 2 {
t.Fatalf("len(conns) = %d, want 2", len(conns))
}
}
func TestUpdateConnection(t *testing.T) {
svc := setupTestService(t)
conn, err := svc.CreateConnection(CreateConnectionInput{
Name: "Old Name",
Hostname: "10.0.0.1",
Port: 22,
Protocol: "ssh",
Tags: []string{"Dev"},
})
if err != nil {
t.Fatalf("CreateConnection() error: %v", err)
}
updated, err := svc.UpdateConnection(conn.ID, UpdateConnectionInput{
Name: strPtr("New Name"),
Tags: []string{"Prod", "Linux"},
})
if err != nil {
t.Fatalf("UpdateConnection() error: %v", err)
}
if updated.Name != "New Name" {
t.Errorf("Name = %q, want %q", updated.Name, "New Name")
}
if len(updated.Tags) != 2 {
t.Fatalf("len(Tags) = %d, want 2", len(updated.Tags))
}
if updated.Tags[0] != "Prod" {
t.Errorf("Tags[0] = %q, want %q", updated.Tags[0], "Prod")
}
// Hostname should remain unchanged
if updated.Hostname != "10.0.0.1" {
t.Errorf("Hostname = %q, want %q (unchanged)", updated.Hostname, "10.0.0.1")
}
}
func TestDeleteConnection(t *testing.T) {
svc := setupTestService(t)
conn, err := svc.CreateConnection(CreateConnectionInput{
Name: "ToDelete",
Hostname: "10.0.0.1",
Port: 22,
Protocol: "ssh",
})
if err != nil {
t.Fatalf("CreateConnection() error: %v", err)
}
if err := svc.DeleteConnection(conn.ID); err != nil {
t.Fatalf("DeleteConnection() error: %v", err)
}
conns, err := svc.ListConnections()
if err != nil {
t.Fatalf("ListConnections() error: %v", err)
}
if len(conns) != 0 {
t.Errorf("len(conns) = %d, want 0 after delete", len(conns))
}
}