mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 10:46:58 +00:00
458 lines
11 KiB
Go
458 lines
11 KiB
Go
package process
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/deployments"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestNewManager(t *testing.T) {
|
|
logger := zap.NewNop()
|
|
m := NewManager(logger)
|
|
|
|
if m == nil {
|
|
t.Fatal("NewManager returned nil")
|
|
}
|
|
if m.logger == nil {
|
|
t.Error("expected logger to be set")
|
|
}
|
|
if m.processes == nil {
|
|
t.Error("expected processes map to be initialized")
|
|
}
|
|
}
|
|
|
|
func TestGetServiceName(t *testing.T) {
|
|
m := NewManager(zap.NewNop())
|
|
|
|
tests := []struct {
|
|
name string
|
|
namespace string
|
|
deplName string
|
|
want string
|
|
}{
|
|
{
|
|
name: "simple names",
|
|
namespace: "alice",
|
|
deplName: "myapp",
|
|
want: "orama-deploy-alice-myapp",
|
|
},
|
|
{
|
|
name: "dots replaced with dashes",
|
|
namespace: "alice.eth",
|
|
deplName: "my.app",
|
|
want: "orama-deploy-alice-eth-my-app",
|
|
},
|
|
{
|
|
name: "multiple dots",
|
|
namespace: "a.b.c",
|
|
deplName: "x.y.z",
|
|
want: "orama-deploy-a-b-c-x-y-z",
|
|
},
|
|
{
|
|
name: "no dots unchanged",
|
|
namespace: "production",
|
|
deplName: "api-server",
|
|
want: "orama-deploy-production-api-server",
|
|
},
|
|
{
|
|
name: "empty strings",
|
|
namespace: "",
|
|
deplName: "",
|
|
want: "orama-deploy--",
|
|
},
|
|
{
|
|
name: "single character names",
|
|
namespace: "a",
|
|
deplName: "b",
|
|
want: "orama-deploy-a-b",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
d := &deployments.Deployment{
|
|
Namespace: tt.namespace,
|
|
Name: tt.deplName,
|
|
}
|
|
got := m.getServiceName(d)
|
|
if got != tt.want {
|
|
t.Errorf("getServiceName() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetStartCommand(t *testing.T) {
|
|
m := NewManager(zap.NewNop())
|
|
// On macOS (test environment), useSystemd will be false, so node/npm use short paths.
|
|
// We explicitly set it to test both modes.
|
|
|
|
workDir := "/opt/orama/deployments/alice/myapp"
|
|
|
|
tests := []struct {
|
|
name string
|
|
useSystemd bool
|
|
deplType deployments.DeploymentType
|
|
env map[string]string
|
|
want string
|
|
}{
|
|
{
|
|
name: "nextjs without systemd",
|
|
useSystemd: false,
|
|
deplType: deployments.DeploymentTypeNextJS,
|
|
want: "node server.js",
|
|
},
|
|
{
|
|
name: "nextjs with systemd",
|
|
useSystemd: true,
|
|
deplType: deployments.DeploymentTypeNextJS,
|
|
want: "/usr/bin/node server.js",
|
|
},
|
|
{
|
|
name: "nodejs backend default entry point",
|
|
useSystemd: false,
|
|
deplType: deployments.DeploymentTypeNodeJSBackend,
|
|
want: "node index.js",
|
|
},
|
|
{
|
|
name: "nodejs backend with systemd default entry point",
|
|
useSystemd: true,
|
|
deplType: deployments.DeploymentTypeNodeJSBackend,
|
|
want: "/usr/bin/node index.js",
|
|
},
|
|
{
|
|
name: "nodejs backend with custom entry point",
|
|
useSystemd: false,
|
|
deplType: deployments.DeploymentTypeNodeJSBackend,
|
|
env: map[string]string{"ENTRY_POINT": "src/server.js"},
|
|
want: "node src/server.js",
|
|
},
|
|
{
|
|
name: "nodejs backend with npm:start entry point",
|
|
useSystemd: false,
|
|
deplType: deployments.DeploymentTypeNodeJSBackend,
|
|
env: map[string]string{"ENTRY_POINT": "npm:start"},
|
|
want: "npm start",
|
|
},
|
|
{
|
|
name: "nodejs backend with npm:start systemd",
|
|
useSystemd: true,
|
|
deplType: deployments.DeploymentTypeNodeJSBackend,
|
|
env: map[string]string{"ENTRY_POINT": "npm:start"},
|
|
want: "/usr/bin/npm start",
|
|
},
|
|
{
|
|
name: "go backend",
|
|
useSystemd: false,
|
|
deplType: deployments.DeploymentTypeGoBackend,
|
|
want: filepath.Join(workDir, "app"),
|
|
},
|
|
{
|
|
name: "static type falls to default",
|
|
useSystemd: false,
|
|
deplType: deployments.DeploymentTypeStatic,
|
|
want: "echo 'Unknown deployment type'",
|
|
},
|
|
{
|
|
name: "unknown type falls to default",
|
|
useSystemd: false,
|
|
deplType: deployments.DeploymentType("something-else"),
|
|
want: "echo 'Unknown deployment type'",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m.useSystemd = tt.useSystemd
|
|
d := &deployments.Deployment{
|
|
Type: tt.deplType,
|
|
Environment: tt.env,
|
|
}
|
|
got := m.getStartCommand(d, workDir)
|
|
if got != tt.want {
|
|
t.Errorf("getStartCommand() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMapRestartPolicy(t *testing.T) {
|
|
m := NewManager(zap.NewNop())
|
|
|
|
tests := []struct {
|
|
name string
|
|
policy deployments.RestartPolicy
|
|
want string
|
|
}{
|
|
{
|
|
name: "always",
|
|
policy: deployments.RestartPolicyAlways,
|
|
want: "always",
|
|
},
|
|
{
|
|
name: "on-failure",
|
|
policy: deployments.RestartPolicyOnFailure,
|
|
want: "on-failure",
|
|
},
|
|
{
|
|
name: "never maps to no",
|
|
policy: deployments.RestartPolicyNever,
|
|
want: "no",
|
|
},
|
|
{
|
|
name: "empty string defaults to on-failure",
|
|
policy: deployments.RestartPolicy(""),
|
|
want: "on-failure",
|
|
},
|
|
{
|
|
name: "unknown policy defaults to on-failure",
|
|
policy: deployments.RestartPolicy("unknown"),
|
|
want: "on-failure",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := m.mapRestartPolicy(tt.policy)
|
|
if got != tt.want {
|
|
t.Errorf("mapRestartPolicy(%q) = %q, want %q", tt.policy, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSystemctlShow(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
want map[string]string
|
|
}{
|
|
{
|
|
name: "typical output",
|
|
input: "ActiveState=active\nSubState=running\nMainPID=1234",
|
|
want: map[string]string{
|
|
"ActiveState": "active",
|
|
"SubState": "running",
|
|
"MainPID": "1234",
|
|
},
|
|
},
|
|
{
|
|
name: "empty output",
|
|
input: "",
|
|
want: map[string]string{},
|
|
},
|
|
{
|
|
name: "lines without equals sign are skipped",
|
|
input: "ActiveState=active\nno-equals-here\nMainPID=5678",
|
|
want: map[string]string{
|
|
"ActiveState": "active",
|
|
"MainPID": "5678",
|
|
},
|
|
},
|
|
{
|
|
name: "value containing equals sign",
|
|
input: "Description=My App=Extra",
|
|
want: map[string]string{
|
|
"Description": "My App=Extra",
|
|
},
|
|
},
|
|
{
|
|
name: "empty value",
|
|
input: "MainPID=\nActiveState=active",
|
|
want: map[string]string{
|
|
"MainPID": "",
|
|
"ActiveState": "active",
|
|
},
|
|
},
|
|
{
|
|
name: "value with whitespace is trimmed",
|
|
input: "ActiveState= active \nMainPID= 1234 ",
|
|
want: map[string]string{
|
|
"ActiveState": "active",
|
|
"MainPID": "1234",
|
|
},
|
|
},
|
|
{
|
|
name: "trailing newline",
|
|
input: "ActiveState=active\n",
|
|
want: map[string]string{
|
|
"ActiveState": "active",
|
|
},
|
|
},
|
|
{
|
|
name: "timestamp value with spaces",
|
|
input: "ActiveEnterTimestamp=Mon 2026-01-29 10:00:00 UTC",
|
|
want: map[string]string{
|
|
"ActiveEnterTimestamp": "Mon 2026-01-29 10:00:00 UTC",
|
|
},
|
|
},
|
|
{
|
|
name: "line with only equals sign is skipped",
|
|
input: "=value",
|
|
want: map[string]string{},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := parseSystemctlShow(tt.input)
|
|
if len(got) != len(tt.want) {
|
|
t.Errorf("parseSystemctlShow() returned %d entries, want %d\ngot: %v\nwant: %v",
|
|
len(got), len(tt.want), got, tt.want)
|
|
return
|
|
}
|
|
for k, wantV := range tt.want {
|
|
gotV, ok := got[k]
|
|
if !ok {
|
|
t.Errorf("missing key %q in result", k)
|
|
continue
|
|
}
|
|
if gotV != wantV {
|
|
t.Errorf("key %q: got %q, want %q", k, gotV, wantV)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSystemdTimestamp(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
wantErr bool
|
|
check func(t *testing.T, got time.Time)
|
|
}{
|
|
{
|
|
name: "day-prefixed format",
|
|
input: "Mon 2026-01-29 10:00:00 UTC",
|
|
wantErr: false,
|
|
check: func(t *testing.T, got time.Time) {
|
|
if got.Year() != 2026 || got.Month() != time.January || got.Day() != 29 {
|
|
t.Errorf("wrong date: got %v", got)
|
|
}
|
|
if got.Hour() != 10 || got.Minute() != 0 || got.Second() != 0 {
|
|
t.Errorf("wrong time: got %v", got)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "without day prefix",
|
|
input: "2026-01-29 10:00:00 UTC",
|
|
wantErr: false,
|
|
check: func(t *testing.T, got time.Time) {
|
|
if got.Year() != 2026 || got.Month() != time.January || got.Day() != 29 {
|
|
t.Errorf("wrong date: got %v", got)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "different day and timezone",
|
|
input: "Fri 2025-12-05 14:30:45 EST",
|
|
wantErr: false,
|
|
check: func(t *testing.T, got time.Time) {
|
|
if got.Year() != 2025 || got.Month() != time.December || got.Day() != 5 {
|
|
t.Errorf("wrong date: got %v", got)
|
|
}
|
|
if got.Hour() != 14 || got.Minute() != 30 || got.Second() != 45 {
|
|
t.Errorf("wrong time: got %v", got)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "empty string returns error",
|
|
input: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid format returns error",
|
|
input: "not-a-timestamp",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "ISO format not supported",
|
|
input: "2026-01-29T10:00:00Z",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := parseSystemdTimestamp(tt.input)
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Errorf("parseSystemdTimestamp(%q) expected error, got nil (time: %v)", tt.input, got)
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("parseSystemdTimestamp(%q) unexpected error: %v", tt.input, err)
|
|
}
|
|
if tt.check != nil {
|
|
tt.check(t, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDirSize(t *testing.T) {
|
|
t.Run("directory with known-size files", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
// Create files with known sizes
|
|
if err := os.WriteFile(filepath.Join(dir, "file1.txt"), make([]byte, 100), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, "file2.txt"), make([]byte, 200), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create a subdirectory with a file
|
|
subDir := filepath.Join(dir, "subdir")
|
|
if err := os.MkdirAll(subDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(subDir, "file3.txt"), make([]byte, 300), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got := dirSize(dir)
|
|
want := int64(600)
|
|
if got != want {
|
|
t.Errorf("dirSize() = %d, want %d", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("empty directory", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
got := dirSize(dir)
|
|
if got != 0 {
|
|
t.Errorf("dirSize() on empty dir = %d, want 0", got)
|
|
}
|
|
})
|
|
|
|
t.Run("non-existent directory", func(t *testing.T) {
|
|
got := dirSize("/nonexistent/path/that/does/not/exist")
|
|
if got != 0 {
|
|
t.Errorf("dirSize() on non-existent dir = %d, want 0", got)
|
|
}
|
|
})
|
|
|
|
t.Run("single file", func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, "only.txt"), make([]byte, 512), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got := dirSize(dir)
|
|
want := int64(512)
|
|
if got != want {
|
|
t.Errorf("dirSize() = %d, want %d", got, want)
|
|
}
|
|
})
|
|
}
|