- Fix go vet warnings across 4 files: update stale API calls in container/linuxkit_test.go, container/state_test.go, and devops/devops_test.go (removed io.Local arg from NewState/LoadState), rewrite container/templates_test.go for package-level function API - Add ansible/parser_test.go: 17 tests covering ParsePlaybook, ParseInventory, ParseTasks, GetHosts, GetHostVars, isModule, NormalizeModule (plays, vars, handlers, blocks, loops, roles, FQCN) - Add ansible/types_test.go: RoleRef/Task UnmarshalYAML, Inventory structure, Facts, TaskResult, KnownModules validation - Add ansible/executor_test.go: executor logic (getHosts, matchesTags, evaluateWhen, templateString, applyFilter, resolveLoop, templateArgs, handleNotify, normalizeConditions, helper functions) - Add infra/hetzner_test.go: HCloudClient/HRobotClient construction, do() round-trip via httptest, API error handling, JSON serialisation for HCloudServer, HCloudLoadBalancer, HRobotServer - Add infra/cloudns_test.go: doRaw() round-trip via httptest, zone/record JSON parsing, CRUD response validation, ACME challenge logic, auth param verification, error handling - Fix go.mod replace directive path (../go -> ../core) - All tests pass, go vet clean, go test -race clean Co-Authored-By: Virgil <virgil@lethean.io> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
777 lines
18 KiB
Go
777 lines
18 KiB
Go
package ansible
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// --- ParsePlaybook ---
|
|
|
|
func TestParsePlaybook_Good_SimplePlay(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: Configure webserver
|
|
hosts: webservers
|
|
become: true
|
|
tasks:
|
|
- name: Install nginx
|
|
apt:
|
|
name: nginx
|
|
state: present
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, plays, 1)
|
|
assert.Equal(t, "Configure webserver", plays[0].Name)
|
|
assert.Equal(t, "webservers", plays[0].Hosts)
|
|
assert.True(t, plays[0].Become)
|
|
require.Len(t, plays[0].Tasks, 1)
|
|
assert.Equal(t, "Install nginx", plays[0].Tasks[0].Name)
|
|
assert.Equal(t, "apt", plays[0].Tasks[0].Module)
|
|
assert.Equal(t, "nginx", plays[0].Tasks[0].Args["name"])
|
|
assert.Equal(t, "present", plays[0].Tasks[0].Args["state"])
|
|
}
|
|
|
|
func TestParsePlaybook_Good_MultiplePlays(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: Play one
|
|
hosts: all
|
|
tasks:
|
|
- name: Say hello
|
|
debug:
|
|
msg: "Hello"
|
|
|
|
- name: Play two
|
|
hosts: localhost
|
|
connection: local
|
|
tasks:
|
|
- name: Say goodbye
|
|
debug:
|
|
msg: "Goodbye"
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, plays, 2)
|
|
assert.Equal(t, "Play one", plays[0].Name)
|
|
assert.Equal(t, "all", plays[0].Hosts)
|
|
assert.Equal(t, "Play two", plays[1].Name)
|
|
assert.Equal(t, "localhost", plays[1].Hosts)
|
|
assert.Equal(t, "local", plays[1].Connection)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_WithVars(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: With vars
|
|
hosts: all
|
|
vars:
|
|
http_port: 8080
|
|
app_name: myapp
|
|
tasks:
|
|
- name: Print port
|
|
debug:
|
|
msg: "Port is {{ http_port }}"
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, plays, 1)
|
|
assert.Equal(t, 8080, plays[0].Vars["http_port"])
|
|
assert.Equal(t, "myapp", plays[0].Vars["app_name"])
|
|
}
|
|
|
|
func TestParsePlaybook_Good_PrePostTasks(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: Full lifecycle
|
|
hosts: all
|
|
pre_tasks:
|
|
- name: Pre task
|
|
debug:
|
|
msg: "pre"
|
|
tasks:
|
|
- name: Main task
|
|
debug:
|
|
msg: "main"
|
|
post_tasks:
|
|
- name: Post task
|
|
debug:
|
|
msg: "post"
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, plays, 1)
|
|
assert.Len(t, plays[0].PreTasks, 1)
|
|
assert.Len(t, plays[0].Tasks, 1)
|
|
assert.Len(t, plays[0].PostTasks, 1)
|
|
assert.Equal(t, "Pre task", plays[0].PreTasks[0].Name)
|
|
assert.Equal(t, "Main task", plays[0].Tasks[0].Name)
|
|
assert.Equal(t, "Post task", plays[0].PostTasks[0].Name)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_Handlers(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: With handlers
|
|
hosts: all
|
|
tasks:
|
|
- name: Install package
|
|
apt:
|
|
name: nginx
|
|
notify: restart nginx
|
|
handlers:
|
|
- name: restart nginx
|
|
service:
|
|
name: nginx
|
|
state: restarted
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, plays, 1)
|
|
assert.Len(t, plays[0].Handlers, 1)
|
|
assert.Equal(t, "restart nginx", plays[0].Handlers[0].Name)
|
|
assert.Equal(t, "service", plays[0].Handlers[0].Module)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_ShellFreeForm(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: Shell tasks
|
|
hosts: all
|
|
tasks:
|
|
- name: Run a command
|
|
shell: echo hello world
|
|
- name: Run raw command
|
|
command: ls -la /tmp
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, plays[0].Tasks, 2)
|
|
assert.Equal(t, "shell", plays[0].Tasks[0].Module)
|
|
assert.Equal(t, "echo hello world", plays[0].Tasks[0].Args["_raw_params"])
|
|
assert.Equal(t, "command", plays[0].Tasks[1].Module)
|
|
assert.Equal(t, "ls -la /tmp", plays[0].Tasks[1].Args["_raw_params"])
|
|
}
|
|
|
|
func TestParsePlaybook_Good_WithTags(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: Tagged play
|
|
hosts: all
|
|
tags:
|
|
- setup
|
|
tasks:
|
|
- name: Tagged task
|
|
debug:
|
|
msg: "tagged"
|
|
tags:
|
|
- debug
|
|
- always
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []string{"setup"}, plays[0].Tags)
|
|
assert.Equal(t, []string{"debug", "always"}, plays[0].Tasks[0].Tags)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_BlockRescueAlways(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: With blocks
|
|
hosts: all
|
|
tasks:
|
|
- name: Protected block
|
|
block:
|
|
- name: Try this
|
|
shell: echo try
|
|
rescue:
|
|
- name: Handle error
|
|
debug:
|
|
msg: "rescued"
|
|
always:
|
|
- name: Always runs
|
|
debug:
|
|
msg: "always"
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
task := plays[0].Tasks[0]
|
|
assert.Len(t, task.Block, 1)
|
|
assert.Len(t, task.Rescue, 1)
|
|
assert.Len(t, task.Always, 1)
|
|
assert.Equal(t, "Try this", task.Block[0].Name)
|
|
assert.Equal(t, "Handle error", task.Rescue[0].Name)
|
|
assert.Equal(t, "Always runs", task.Always[0].Name)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_WithLoop(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: Loop test
|
|
hosts: all
|
|
tasks:
|
|
- name: Install packages
|
|
apt:
|
|
name: "{{ item }}"
|
|
state: present
|
|
loop:
|
|
- vim
|
|
- curl
|
|
- git
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
task := plays[0].Tasks[0]
|
|
assert.Equal(t, "apt", task.Module)
|
|
items, ok := task.Loop.([]any)
|
|
require.True(t, ok)
|
|
assert.Len(t, items, 3)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_RoleRefs(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: With roles
|
|
hosts: all
|
|
roles:
|
|
- common
|
|
- role: webserver
|
|
vars:
|
|
http_port: 80
|
|
tags:
|
|
- web
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, plays[0].Roles, 2)
|
|
assert.Equal(t, "common", plays[0].Roles[0].Role)
|
|
assert.Equal(t, "webserver", plays[0].Roles[1].Role)
|
|
assert.Equal(t, 80, plays[0].Roles[1].Vars["http_port"])
|
|
assert.Equal(t, []string{"web"}, plays[0].Roles[1].Tags)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_FullyQualifiedModules(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: FQCN modules
|
|
hosts: all
|
|
tasks:
|
|
- name: Copy file
|
|
ansible.builtin.copy:
|
|
src: /tmp/foo
|
|
dest: /tmp/bar
|
|
- name: Run shell
|
|
ansible.builtin.shell: echo hello
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "ansible.builtin.copy", plays[0].Tasks[0].Module)
|
|
assert.Equal(t, "/tmp/foo", plays[0].Tasks[0].Args["src"])
|
|
assert.Equal(t, "ansible.builtin.shell", plays[0].Tasks[1].Module)
|
|
assert.Equal(t, "echo hello", plays[0].Tasks[1].Args["_raw_params"])
|
|
}
|
|
|
|
func TestParsePlaybook_Good_RegisterAndWhen(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: Conditional play
|
|
hosts: all
|
|
tasks:
|
|
- name: Check file
|
|
stat:
|
|
path: /etc/nginx/nginx.conf
|
|
register: nginx_conf
|
|
- name: Show result
|
|
debug:
|
|
msg: "File exists"
|
|
when: nginx_conf.stat.exists
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "nginx_conf", plays[0].Tasks[0].Register)
|
|
assert.NotNil(t, plays[0].Tasks[1].When)
|
|
}
|
|
|
|
func TestParsePlaybook_Good_EmptyPlaybook(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
require.NoError(t, os.WriteFile(path, []byte("---\n[]"), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
assert.Empty(t, plays)
|
|
}
|
|
|
|
func TestParsePlaybook_Bad_InvalidYAML(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "bad.yml")
|
|
|
|
require.NoError(t, os.WriteFile(path, []byte("{{invalid yaml}}"), 0644))
|
|
|
|
p := NewParser(dir)
|
|
_, err := p.ParsePlaybook(path)
|
|
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "parse playbook")
|
|
}
|
|
|
|
func TestParsePlaybook_Bad_FileNotFound(t *testing.T) {
|
|
p := NewParser(t.TempDir())
|
|
_, err := p.ParsePlaybook("/nonexistent/playbook.yml")
|
|
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "read playbook")
|
|
}
|
|
|
|
func TestParsePlaybook_Good_GatherFactsDisabled(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "playbook.yml")
|
|
|
|
yaml := `---
|
|
- name: No facts
|
|
hosts: all
|
|
gather_facts: false
|
|
tasks: []
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
plays, err := p.ParsePlaybook(path)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, plays[0].GatherFacts)
|
|
assert.False(t, *plays[0].GatherFacts)
|
|
}
|
|
|
|
// --- ParseInventory ---
|
|
|
|
func TestParseInventory_Good_SimpleInventory(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "inventory.yml")
|
|
|
|
yaml := `---
|
|
all:
|
|
hosts:
|
|
web1:
|
|
ansible_host: 192.168.1.10
|
|
web2:
|
|
ansible_host: 192.168.1.11
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
inv, err := p.ParseInventory(path)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, inv.All)
|
|
assert.Len(t, inv.All.Hosts, 2)
|
|
assert.Equal(t, "192.168.1.10", inv.All.Hosts["web1"].AnsibleHost)
|
|
assert.Equal(t, "192.168.1.11", inv.All.Hosts["web2"].AnsibleHost)
|
|
}
|
|
|
|
func TestParseInventory_Good_WithGroups(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "inventory.yml")
|
|
|
|
yaml := `---
|
|
all:
|
|
children:
|
|
webservers:
|
|
hosts:
|
|
web1:
|
|
ansible_host: 10.0.0.1
|
|
web2:
|
|
ansible_host: 10.0.0.2
|
|
databases:
|
|
hosts:
|
|
db1:
|
|
ansible_host: 10.0.1.1
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
inv, err := p.ParseInventory(path)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, inv.All.Children["webservers"])
|
|
assert.Len(t, inv.All.Children["webservers"].Hosts, 2)
|
|
require.NotNil(t, inv.All.Children["databases"])
|
|
assert.Len(t, inv.All.Children["databases"].Hosts, 1)
|
|
}
|
|
|
|
func TestParseInventory_Good_WithVars(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "inventory.yml")
|
|
|
|
yaml := `---
|
|
all:
|
|
vars:
|
|
ansible_user: admin
|
|
children:
|
|
production:
|
|
vars:
|
|
env: prod
|
|
hosts:
|
|
prod1:
|
|
ansible_host: 10.0.0.1
|
|
ansible_port: 2222
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
inv, err := p.ParseInventory(path)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "admin", inv.All.Vars["ansible_user"])
|
|
assert.Equal(t, "prod", inv.All.Children["production"].Vars["env"])
|
|
assert.Equal(t, 2222, inv.All.Children["production"].Hosts["prod1"].AnsiblePort)
|
|
}
|
|
|
|
func TestParseInventory_Bad_InvalidYAML(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "bad.yml")
|
|
|
|
require.NoError(t, os.WriteFile(path, []byte("{{{bad"), 0644))
|
|
|
|
p := NewParser(dir)
|
|
_, err := p.ParseInventory(path)
|
|
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "parse inventory")
|
|
}
|
|
|
|
func TestParseInventory_Bad_FileNotFound(t *testing.T) {
|
|
p := NewParser(t.TempDir())
|
|
_, err := p.ParseInventory("/nonexistent/inventory.yml")
|
|
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "read inventory")
|
|
}
|
|
|
|
// --- ParseTasks ---
|
|
|
|
func TestParseTasks_Good_TaskFile(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "tasks.yml")
|
|
|
|
yaml := `---
|
|
- name: First task
|
|
shell: echo first
|
|
- name: Second task
|
|
copy:
|
|
src: /tmp/a
|
|
dest: /tmp/b
|
|
`
|
|
require.NoError(t, os.WriteFile(path, []byte(yaml), 0644))
|
|
|
|
p := NewParser(dir)
|
|
tasks, err := p.ParseTasks(path)
|
|
|
|
require.NoError(t, err)
|
|
require.Len(t, tasks, 2)
|
|
assert.Equal(t, "shell", tasks[0].Module)
|
|
assert.Equal(t, "echo first", tasks[0].Args["_raw_params"])
|
|
assert.Equal(t, "copy", tasks[1].Module)
|
|
assert.Equal(t, "/tmp/a", tasks[1].Args["src"])
|
|
}
|
|
|
|
func TestParseTasks_Bad_InvalidYAML(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "bad.yml")
|
|
|
|
require.NoError(t, os.WriteFile(path, []byte("not: [valid: tasks"), 0644))
|
|
|
|
p := NewParser(dir)
|
|
_, err := p.ParseTasks(path)
|
|
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
// --- GetHosts ---
|
|
|
|
func TestGetHosts_Good_AllPattern(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Hosts: map[string]*Host{
|
|
"host1": {},
|
|
"host2": {},
|
|
},
|
|
},
|
|
}
|
|
|
|
hosts := GetHosts(inv, "all")
|
|
assert.Len(t, hosts, 2)
|
|
assert.Contains(t, hosts, "host1")
|
|
assert.Contains(t, hosts, "host2")
|
|
}
|
|
|
|
func TestGetHosts_Good_LocalhostPattern(t *testing.T) {
|
|
inv := &Inventory{All: &InventoryGroup{}}
|
|
hosts := GetHosts(inv, "localhost")
|
|
assert.Equal(t, []string{"localhost"}, hosts)
|
|
}
|
|
|
|
func TestGetHosts_Good_GroupPattern(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Children: map[string]*InventoryGroup{
|
|
"web": {
|
|
Hosts: map[string]*Host{
|
|
"web1": {},
|
|
"web2": {},
|
|
},
|
|
},
|
|
"db": {
|
|
Hosts: map[string]*Host{
|
|
"db1": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
hosts := GetHosts(inv, "web")
|
|
assert.Len(t, hosts, 2)
|
|
assert.Contains(t, hosts, "web1")
|
|
assert.Contains(t, hosts, "web2")
|
|
}
|
|
|
|
func TestGetHosts_Good_SpecificHost(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Children: map[string]*InventoryGroup{
|
|
"servers": {
|
|
Hosts: map[string]*Host{
|
|
"myhost": {},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
hosts := GetHosts(inv, "myhost")
|
|
assert.Equal(t, []string{"myhost"}, hosts)
|
|
}
|
|
|
|
func TestGetHosts_Good_AllIncludesChildren(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Hosts: map[string]*Host{"top": {}},
|
|
Children: map[string]*InventoryGroup{
|
|
"group1": {
|
|
Hosts: map[string]*Host{"child1": {}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
hosts := GetHosts(inv, "all")
|
|
assert.Len(t, hosts, 2)
|
|
assert.Contains(t, hosts, "top")
|
|
assert.Contains(t, hosts, "child1")
|
|
}
|
|
|
|
func TestGetHosts_Bad_NoMatch(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Hosts: map[string]*Host{"host1": {}},
|
|
},
|
|
}
|
|
|
|
hosts := GetHosts(inv, "nonexistent")
|
|
assert.Empty(t, hosts)
|
|
}
|
|
|
|
func TestGetHosts_Bad_NilGroup(t *testing.T) {
|
|
inv := &Inventory{All: nil}
|
|
hosts := GetHosts(inv, "all")
|
|
assert.Empty(t, hosts)
|
|
}
|
|
|
|
// --- GetHostVars ---
|
|
|
|
func TestGetHostVars_Good_DirectHost(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Vars: map[string]any{"global_var": "global"},
|
|
Hosts: map[string]*Host{
|
|
"myhost": {
|
|
AnsibleHost: "10.0.0.1",
|
|
AnsiblePort: 2222,
|
|
AnsibleUser: "deploy",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
vars := GetHostVars(inv, "myhost")
|
|
assert.Equal(t, "10.0.0.1", vars["ansible_host"])
|
|
assert.Equal(t, 2222, vars["ansible_port"])
|
|
assert.Equal(t, "deploy", vars["ansible_user"])
|
|
assert.Equal(t, "global", vars["global_var"])
|
|
}
|
|
|
|
func TestGetHostVars_Good_InheritedGroupVars(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Vars: map[string]any{"level": "all"},
|
|
Children: map[string]*InventoryGroup{
|
|
"production": {
|
|
Vars: map[string]any{"env": "prod", "level": "group"},
|
|
Hosts: map[string]*Host{
|
|
"prod1": {
|
|
AnsibleHost: "10.0.0.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
vars := GetHostVars(inv, "prod1")
|
|
assert.Equal(t, "10.0.0.1", vars["ansible_host"])
|
|
assert.Equal(t, "prod", vars["env"])
|
|
}
|
|
|
|
func TestGetHostVars_Good_HostNotFound(t *testing.T) {
|
|
inv := &Inventory{
|
|
All: &InventoryGroup{
|
|
Hosts: map[string]*Host{"other": {}},
|
|
},
|
|
}
|
|
|
|
vars := GetHostVars(inv, "nonexistent")
|
|
assert.Empty(t, vars)
|
|
}
|
|
|
|
// --- isModule ---
|
|
|
|
func TestIsModule_Good_KnownModules(t *testing.T) {
|
|
assert.True(t, isModule("shell"))
|
|
assert.True(t, isModule("command"))
|
|
assert.True(t, isModule("copy"))
|
|
assert.True(t, isModule("file"))
|
|
assert.True(t, isModule("apt"))
|
|
assert.True(t, isModule("service"))
|
|
assert.True(t, isModule("systemd"))
|
|
assert.True(t, isModule("debug"))
|
|
assert.True(t, isModule("set_fact"))
|
|
}
|
|
|
|
func TestIsModule_Good_FQCN(t *testing.T) {
|
|
assert.True(t, isModule("ansible.builtin.shell"))
|
|
assert.True(t, isModule("ansible.builtin.copy"))
|
|
assert.True(t, isModule("ansible.builtin.apt"))
|
|
}
|
|
|
|
func TestIsModule_Good_DottedUnknown(t *testing.T) {
|
|
// Any key with dots is considered a module
|
|
assert.True(t, isModule("community.general.ufw"))
|
|
assert.True(t, isModule("ansible.posix.authorized_key"))
|
|
}
|
|
|
|
func TestIsModule_Bad_NotAModule(t *testing.T) {
|
|
assert.False(t, isModule("some_random_key"))
|
|
assert.False(t, isModule("foobar"))
|
|
}
|
|
|
|
// --- NormalizeModule ---
|
|
|
|
func TestNormalizeModule_Good(t *testing.T) {
|
|
assert.Equal(t, "ansible.builtin.shell", NormalizeModule("shell"))
|
|
assert.Equal(t, "ansible.builtin.copy", NormalizeModule("copy"))
|
|
assert.Equal(t, "ansible.builtin.apt", NormalizeModule("apt"))
|
|
}
|
|
|
|
func TestNormalizeModule_Good_AlreadyFQCN(t *testing.T) {
|
|
assert.Equal(t, "ansible.builtin.shell", NormalizeModule("ansible.builtin.shell"))
|
|
assert.Equal(t, "community.general.ufw", NormalizeModule("community.general.ufw"))
|
|
}
|
|
|
|
// --- NewParser ---
|
|
|
|
func TestNewParser_Good(t *testing.T) {
|
|
p := NewParser("/some/path")
|
|
assert.NotNil(t, p)
|
|
assert.Equal(t, "/some/path", p.basePath)
|
|
assert.NotNil(t, p.vars)
|
|
}
|