Files
grlx-lsp/internal/recipe/recipe_test.go
Tai Groot d5a9585c58 feat: grlx LSP server in Go
LSP server for grlx recipe files (.grlx) providing:

- Completion for ingredients, methods, properties, requisite types,
  and step ID references
- Diagnostics for unknown ingredients/methods, missing required
  properties, unknown properties, and invalid requisite types
- Hover documentation for all ingredients and methods with property
  tables
- Full schema for all 6 grlx ingredients (cmd, file, group, pkg,
  service, user) with accurate properties from the grlx source
2026-03-06 09:14:10 +00:00

188 lines
3.9 KiB
Go

package recipe
import "testing"
func TestParseSimpleRecipe(t *testing.T) {
data := []byte(`
include:
- apache
- .dev
steps:
install nginx:
pkg.installed:
- name: nginx
start nginx:
service.running:
- name: nginx
- requisites:
- require: install nginx
`)
r := Parse(data)
if len(r.Errors) > 0 {
t.Fatalf("unexpected parse errors: %v", r.Errors)
}
if len(r.Includes) != 2 {
t.Fatalf("expected 2 includes, got %d", len(r.Includes))
}
if r.Includes[0].Value != "apache" {
t.Errorf("include[0] = %q, want %q", r.Includes[0].Value, "apache")
}
if r.Includes[1].Value != ".dev" {
t.Errorf("include[1] = %q, want %q", r.Includes[1].Value, ".dev")
}
if len(r.Steps) != 2 {
t.Fatalf("expected 2 steps, got %d", len(r.Steps))
}
s := r.Steps[0]
if s.ID != "install nginx" {
t.Errorf("step[0].ID = %q, want %q", s.ID, "install nginx")
}
if s.Ingredient != "pkg" {
t.Errorf("step[0].Ingredient = %q, want %q", s.Ingredient, "pkg")
}
if s.Method != "installed" {
t.Errorf("step[0].Method = %q, want %q", s.Method, "installed")
}
if len(s.Properties) != 1 || s.Properties[0].Key != "name" {
t.Errorf("step[0] expected name property, got %v", s.Properties)
}
}
func TestParseInvalidYAML(t *testing.T) {
data := []byte(`{{{invalid`)
r := Parse(data)
if len(r.Errors) == 0 {
t.Error("expected parse errors for invalid YAML")
}
}
func TestParseUnknownTopLevel(t *testing.T) {
data := []byte(`
include:
- foo
bogus_key: bar
steps: {}
`)
r := Parse(data)
found := false
for _, e := range r.Errors {
if e.Message == "unknown top-level key: bogus_key" {
found = true
}
}
if !found {
t.Error("expected error about unknown top-level key")
}
}
func TestParseBadMethodKey(t *testing.T) {
data := []byte(`
steps:
bad step:
nomethod:
- name: foo
`)
r := Parse(data)
found := false
for _, e := range r.Errors {
if e.Message == "step key must be in the form ingredient.method, got: nomethod" {
found = true
}
}
if !found {
t.Errorf("expected error about bad method key, got: %v", r.Errors)
}
}
func TestStepIDs(t *testing.T) {
data := []byte(`
steps:
step one:
file.exists:
- name: /tmp/a
step two:
file.absent:
- name: /tmp/b
`)
r := Parse(data)
ids := r.StepIDs()
if len(ids) != 2 {
t.Fatalf("expected 2 step IDs, got %d", len(ids))
}
idSet := make(map[string]bool)
for _, id := range ids {
idSet[id] = true
}
if !idSet["step one"] || !idSet["step two"] {
t.Errorf("missing expected step IDs: %v", ids)
}
}
func TestParseRequisites(t *testing.T) {
data := []byte(`
steps:
first step:
file.exists:
- name: /tmp/a
second step:
file.exists:
- name: /tmp/b
- requisites:
- require: first step
- onchanges:
- first step
`)
r := Parse(data)
if len(r.Errors) > 0 {
t.Fatalf("unexpected parse errors: %v", r.Errors)
}
if len(r.Steps) != 2 {
t.Fatalf("expected 2 steps, got %d", len(r.Steps))
}
s := r.Steps[1]
if len(s.Requisites) != 2 {
t.Fatalf("expected 2 requisites, got %d", len(s.Requisites))
}
if s.Requisites[0].Condition != "require" {
t.Errorf("requisite[0].Condition = %q, want %q", s.Requisites[0].Condition, "require")
}
if len(s.Requisites[0].StepIDs) != 1 || s.Requisites[0].StepIDs[0] != "first step" {
t.Errorf("requisite[0].StepIDs = %v, want [first step]", s.Requisites[0].StepIDs)
}
}
func TestParseEmptyRecipe(t *testing.T) {
r := Parse([]byte(""))
if r == nil {
t.Fatal("expected non-nil recipe for empty input")
}
}
func TestParseGoTemplate(t *testing.T) {
// Recipes can contain Go template syntax — the parser should not crash.
// Template directives may cause YAML errors, but the parser should handle gracefully.
data := []byte(`
steps:
install golang:
archive.extracted:
- name: /usr/local/go
`)
r := Parse(data)
if len(r.Steps) != 1 {
t.Fatalf("expected 1 step, got %d", len(r.Steps))
}
}