Git Repository

Stackyard

Stackyard scannt lokale Entwicklungsordner und bündelt Projekte, Git-Status, TODOs, Toolchains, Docker-Compose-Setups, Datei- und Launcher-Funktionen sowie Operations-Informationen in einer lokalen WebUI.

Projektseite ↗
HTTPShttps://zanvex.de/git/stackyard.git

internal/operations/operations_test.go

Zum Verzeichnis
package operations

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"stackyard/internal/project"
)

func TestLoadManifestAndActionWhitelist(t *testing.T) {
	root := t.TempDir()
	if err := os.MkdirAll(filepath.Join(root, ".stackyard"), 0o755); err != nil {
		t.Fatal(err)
	}
	data := []byte("provider: go\nactions:\n  - name: Tests\n    kind: go-test\n")
	if err := os.WriteFile(filepath.Join(root, ".stackyard", "project.yaml"), data, 0o644); err != nil {
		t.Fatal(err)
	}
	manifest := LoadManifest(root)
	if manifest.Provider != "go" || len(manifest.Actions) != 1 {
		t.Fatalf("unexpected manifest: %+v", manifest)
	}
	if ValidateAction("go-test") != nil || ValidateAction("shell") == nil {
		t.Fatal("unexpected action whitelist result")
	}
}

func TestCollectTodos(t *testing.T) {
	root := t.TempDir()
	if err := os.WriteFile(filepath.Join(root, "TODO.md"), []byte("- [ ] offen\n- [x] fertig\ntext"), 0o644); err != nil {
		t.Fatal(err)
	}
	items := CollectTodos([]project.Project{{ID: "p", Name: "Demo", Path: root}})
	if len(items) != 2 || items[0].Text != "offen" || !items[1].Done {
		t.Fatalf("unexpected todo items: %+v", items)
	}
}

func TestLoopbackURLValidation(t *testing.T) {
	if !isLoopbackURL("http://127.0.0.1:8080/health") || !isLoopbackURL("http://localhost:3000") {
		t.Fatal("expected loopback URLs to be accepted")
	}
	if isLoopbackURL("https://example.com/collect") || isLoopbackURL("file:///tmp/test") {
		t.Fatal("expected non-loopback URLs to be rejected")
	}
}

func TestBoundedLogKeepsNewestOutput(t *testing.T) {
	log := &boundedLog{max: 5}
	if _, err := log.Write([]byte("1234")); err != nil {
		t.Fatal(err)
	}
	if _, err := log.Write([]byte("567")); err != nil {
		t.Fatal(err)
	}
	if got := log.String(); got != "34567" {
		t.Fatalf("unexpected bounded log: %q", got)
	}
}

func TestProcessManagerRejectsOneShotAction(t *testing.T) {
	manager := NewProcessManager()
	if err := manager.Start("project", t.TempDir(), "go-test"); err == nil {
		t.Fatal("expected one-shot action to be rejected as a service")
	}
}

func TestProcessManagerCapturesServiceOutput(t *testing.T) {
	root := t.TempDir()
	if err := os.WriteFile(filepath.Join(root, "go.mod"), []byte("module process-test\n\ngo 1.24\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(root, "main.go"), []byte("package main\nimport \"fmt\"\nfunc main() { fmt.Print(\"service-ready\") }\n"), 0o644); err != nil {
		t.Fatal(err)
	}

	manager := NewProcessManager()
	if err := manager.Start("project", root, "go-run"); err != nil {
		t.Fatalf("start service: %v", err)
	}
	deadline := time.Now().Add(10 * time.Second)
	for time.Now().Before(deadline) {
		status := manager.Status("project", "go-run")
		if !status.Running {
			if status.Error != "" || !strings.Contains(status.Log, "service-ready") {
				t.Fatalf("unexpected final status: %+v", status)
			}
			return
		}
		time.Sleep(25 * time.Millisecond)
	}
	manager.StopAll()
	t.Fatal("service did not finish")
}