package emitter import ( "strings" "testing" "git.cloudinit.dev/coreci/orca/internal/jobspec" ) func TestSocketEmitter_RenderSocketLines_NoPorts(t *testing.T) { spec := &jobspec.WorkloadSpec{ Kind: "Job", Name: "backup", Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/rsync"}, } lines := (SocketEmitter{}).RenderSocketLines(spec) if len(lines) != 0 { t.Errorf("got %d lines, want 0 for no ports: %v", len(lines), lines) } } func TestSocketEmitter_RenderSocketLines_NilSpec(t *testing.T) { lines := (SocketEmitter{}).RenderSocketLines(nil) if lines != nil { t.Errorf("nil spec should return nil, got %v", lines) } } func TestSocketEmitter_RenderSocketLines_SinglePort(t *testing.T) { spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "web", Ports: []jobspec.PortSpec{{Name: "http", Port: 8080}}, } lines := (SocketEmitter{}).RenderSocketLines(spec) // Expect: RuntimeDirectory + comment. No TCP bind (default socket). wantRT := "RuntimeDirectory=orca/alloc-web" if !contains(lines, wantRT) { t.Errorf("lines %v missing %q", lines, wantRT) } wantSock := "# socket: /run/orca/alloc-web/port-http.sock" if !contains(lines, wantSock) { t.Errorf("lines %v missing %q", lines, wantSock) } for _, l := range lines { if strings.HasPrefix(l, "ExecStartPre=") { t.Errorf("socket bind should not emit ExecStartPre (no TCP opt-in): %s", l) } } } func TestSocketEmitter_RenderSocketLines_MultiplePorts(t *testing.T) { spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "api", Ports: []jobspec.PortSpec{ {Name: "http", Port: 8080}, {Name: "grpc", Port: 9090}, }, } lines := (SocketEmitter{}).RenderSocketLines(spec) // Two RuntimeDirectory lines (one per port). count := 0 for _, l := range lines { if l == "RuntimeDirectory=orca/alloc-api" { count++ } } if count != 2 { t.Errorf("RuntimeDirectory count = %d, want 2 (one per port)", count) } if !contains(lines, "# socket: /run/orca/alloc-api/port-http.sock") { t.Errorf("missing http socket comment") } if !contains(lines, "# socket: /run/orca/alloc-api/port-grpc.sock") { t.Errorf("missing grpc socket comment") } } func TestSocketEmitter_RenderSocketLines_TCPBind127(t *testing.T) { // service.bind = 127.0.0.1 → TCP opt-in → ExecStartPre marker per port. spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "web", Ports: []jobspec.PortSpec{{Name: "http", Port: 8080}}, Service: &jobspec.ServiceBlock{Bind: "127.0.0.1"}, } lines := (SocketEmitter{}).RenderSocketLines(spec) found := false for _, l := range lines { if strings.HasPrefix(l, "ExecStartPre=/bin/echo orca: bind 127.0.0.1 port http (tcp, R-007 opt-in)") { found = true } } if !found { t.Errorf("missing TCP bind ExecStartPre marker; lines: %v", lines) } } func TestSocketEmitter_RenderSocketLines_TCPBindIPv6(t *testing.T) { // Non-127.0.0.1 bind is accepted by schema but not the documented // opt-in; the marker is only emitted for 127.0.0.1. The // RuntimeDirectory lines are still emitted (the directory exists // regardless of bind mode — sockets or TCP). spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "web", Ports: []jobspec.PortSpec{{Name: "http", Port: 8080}}, Service: &jobspec.ServiceBlock{Bind: "::1"}, } lines := (SocketEmitter{}).RenderSocketLines(spec) for _, l := range lines { if strings.HasPrefix(l, "ExecStartPre=") { t.Errorf("::1 bind should NOT emit TCP marker (only 127.0.0.1 is documented opt-in): %s", l) } } if !contains(lines, "RuntimeDirectory=orca/alloc-web") { t.Errorf("RuntimeDirectory should still be emitted for ::1 bind") } } func TestSocketEmitter_RenderSocketLines_EmptyBindSocket(t *testing.T) { // Empty bind → default socket → no TCP marker, but RuntimeDirectory emitted. spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "web", Ports: []jobspec.PortSpec{{Name: "http", Port: 8080}}, Service: &jobspec.ServiceBlock{Bind: ""}, } lines := (SocketEmitter{}).RenderSocketLines(spec) for _, l := range lines { if strings.HasPrefix(l, "ExecStartPre=") { t.Errorf("empty bind should NOT emit TCP marker: %s", l) } } if !contains(lines, "RuntimeDirectory=orca/alloc-web") { t.Errorf("RuntimeDirectory missing for empty bind") } } func TestSocketEmitter_RenderSocketLines_NilService(t *testing.T) { // No service block → default socket → no TCP marker, but RuntimeDirectory emitted. spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "web", Ports: []jobspec.PortSpec{{Name: "http", Port: 8080}}, } lines := (SocketEmitter{}).RenderSocketLines(spec) for _, l := range lines { if strings.HasPrefix(l, "ExecStartPre=") { t.Errorf("nil service should NOT emit TCP marker: %s", l) } } if !contains(lines, "RuntimeDirectory=orca/alloc-web") { t.Errorf("RuntimeDirectory missing for nil service") } } func TestSocketEmitter_SocketPath(t *testing.T) { got := SocketPath("alloc-123", "http") want := "/run/orca/alloc-alloc-123/port-http.sock" if got != want { t.Errorf("SocketPath = %q, want %q", got, want) } } func TestSocketEmitter_AllocIDPlaceholderNilSpec(t *testing.T) { if got := allocIDForSocket(nil); got != "" { t.Errorf("allocIDForSocket(nil) = %q, want ", got) } } func TestSocketEmitter_AllocIDPlaceholderEmptyName(t *testing.T) { spec := &jobspec.WorkloadSpec{Name: " "} if got := allocIDForSocket(spec); got != "" { t.Errorf("allocIDForSocket(empty name) = %q, want ", got) } } func TestSocketEmitter_AllocIDPlaceholderNamedSpec(t *testing.T) { spec := &jobspec.WorkloadSpec{Name: "web"} if got := allocIDForSocket(spec); got != "web" { t.Errorf("allocIDForSocket(web) = %q, want web", got) } } func TestSocketEmitter_SocketPathPlaceholder(t *testing.T) { got := SocketPath("", "grpc") want := "/run/orca/alloc-/port-grpc.sock" if got != want { t.Errorf("SocketPath = %q, want %q", got, want) } } func TestSystemdEmitter_IntegratesSocketLines(t *testing.T) { // End-to-end: the systemd unit for a Service with ports contains // the RuntimeDirectory line emitted by the SocketEmitter. spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "web", Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/httpd"}, Ports: []jobspec.PortSpec{{Name: "http", Port: 8080}}, } files, err := SystemdEmitter{}.Render(spec, &Node{}) if err != nil { t.Fatalf("Render: %v", err) } c := files[0].Content if !strings.Contains(c, "RuntimeDirectory=orca/alloc-web\n") { t.Errorf("unit missing RuntimeDirectory line\n%s", c) } if !strings.Contains(c, "# socket: /run/orca/alloc-web/port-http.sock\n") { t.Errorf("unit missing socket path comment\n%s", c) } } func TestSystemdEmitter_IntegratesSocketLinesTCPBind(t *testing.T) { // When service.bind = 127.0.0.1, the unit contains the ExecStartPre // TCP-bind marker. spec := &jobspec.WorkloadSpec{ Kind: "Service", Name: "web", Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/httpd"}, Ports: []jobspec.PortSpec{{Name: "http", Port: 8080}}, Service: &jobspec.ServiceBlock{Bind: "127.0.0.1"}, } files, err := SystemdEmitter{}.Render(spec, &Node{}) if err != nil { t.Fatalf("Render: %v", err) } c := files[0].Content if !strings.Contains(c, "ExecStartPre=/bin/echo orca: bind 127.0.0.1 port http (tcp, R-007 opt-in)\n") { t.Errorf("unit missing TCP bind ExecStartPre marker\n%s", c) } } func TestSystemdEmitter_NoSocketLinesForPortlessSpec(t *testing.T) { // A Job with no ports → no RuntimeDirectory line in the unit. spec := &jobspec.WorkloadSpec{ Kind: "Job", Name: "backup", Runtime: &jobspec.RuntimeBlock{OneOf: "process", Command: "/bin/rsync"}, } files, err := SystemdEmitter{}.Render(spec, &Node{}) if err != nil { t.Fatalf("Render: %v", err) } c := files[0].Content if strings.Contains(c, "RuntimeDirectory=") { t.Errorf("portless spec should not emit RuntimeDirectory\n%s", c) } if strings.Contains(c, "# socket:") { t.Errorf("portless spec should not emit socket comment\n%s", c) } } // contains reports whether the slice contains the string s. func contains(lines []string, s string) bool { for _, l := range lines { if l == s { return true } } return false }