* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* openFuyao is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package parser
import (
"os"
"path/filepath"
"testing"
)
func writeTemp(t *testing.T, content string) string {
t.Helper()
f, err := os.CreateTemp(t.TempDir(), "*.yaml")
if err != nil {
t.Fatalf("failed to create temp file: %v", err)
}
if _, err := f.WriteString(content); err != nil {
t.Fatalf("failed to write temp file: %v", err)
}
f.Close()
return f.Name()
}
func TestParseNodes_Valid(t *testing.T) {
content := `
nodes:
- name: node1
ip: 192.168.1.1
port: 2222
user: root
password: secret
- name: node2
ip: 192.168.1.2
user: admin
keyFile: /home/admin/.ssh/id_rsa
`
path := writeTemp(t, content)
nodes, err := ParseNodes(path)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(nodes) != 2 {
t.Fatalf("expected 2 nodes, got %d", len(nodes))
}
if nodes[0].Name != "node1" || nodes[0].IP != "192.168.1.1" || nodes[0].Port != 2222 {
t.Errorf("node1 fields mismatch: %+v", nodes[0])
}
if nodes[1].Name != "node2" || nodes[1].KeyFile != "/home/admin/.ssh/id_rsa" {
t.Errorf("node2 fields mismatch: %+v", nodes[1])
}
}
func TestParseNodes_DefaultPort(t *testing.T) {
content := `
nodes:
- name: node1
ip: 10.0.0.1
user: root
password: pass
`
path := writeTemp(t, content)
nodes, err := ParseNodes(path)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodes[0].Port != 22 {
t.Errorf("expected default port 22, got %d", nodes[0].Port)
}
}
func TestParseNodes_FileNotFound(t *testing.T) {
_, err := ParseNodes(filepath.Join(t.TempDir(), "nonexistent.yaml"))
if err == nil {
t.Fatal("expected error for missing file, got nil")
}
}
func TestParseNodes_InvalidYAML(t *testing.T) {
path := writeTemp(t, "nodes: [invalid: yaml: :")
_, err := ParseNodes(path)
if err == nil {
t.Fatal("expected error for invalid YAML, got nil")
}
}
func TestParseNodes_EmptyNodes(t *testing.T) {
path := writeTemp(t, "nodes: []\n")
_, err := ParseNodes(path)
if err == nil {
t.Fatal("expected error for empty nodes list, got nil")
}
}
func TestParseNodes_MissingName(t *testing.T) {
content := `
nodes:
- ip: 10.0.0.1
user: root
password: pass
`
path := writeTemp(t, content)
_, err := ParseNodes(path)
if err == nil {
t.Fatal("expected error for missing name, got nil")
}
}
func TestParseNodes_MissingIP(t *testing.T) {
content := `
nodes:
- name: node1
user: root
password: pass
`
path := writeTemp(t, content)
_, err := ParseNodes(path)
if err == nil {
t.Fatal("expected error for missing ip, got nil")
}
}
func TestParseNodes_MissingUser(t *testing.T) {
content := `
nodes:
- name: node1
ip: 10.0.0.1
password: pass
`
path := writeTemp(t, content)
_, err := ParseNodes(path)
if err == nil {
t.Fatal("expected error for missing user, got nil")
}
}
func TestParseNodes_MissingAuth(t *testing.T) {
content := `
nodes:
- name: node1
ip: 10.0.0.1
user: root
`
path := writeTemp(t, content)
_, err := ParseNodes(path)
if err == nil {
t.Fatal("expected error when both password and keyFile are absent, got nil")
}
}
func TestParseNodes_KeyFileOnly(t *testing.T) {
content := `
nodes:
- name: node1
ip: 10.0.0.1
user: root
keyFile: /root/.ssh/id_rsa
`
path := writeTemp(t, content)
nodes, err := ParseNodes(path)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodes[0].KeyFile != "/root/.ssh/id_rsa" {
t.Errorf("unexpected keyFile: %s", nodes[0].KeyFile)
}
}
func TestParseValues_Valid(t *testing.T) {
content := `
inference-backend:
images:
inferenceEngine:
tag: v1.2.3
replicas: 3
`
path := writeTemp(t, content)
values, err := ParseValues(path)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if values == nil {
t.Fatal("expected non-nil values map")
}
if _, ok := values["inference-backend"]; !ok {
t.Error("expected inference-backend key in values")
}
}
func TestParseValues_FileNotFound(t *testing.T) {
_, err := ParseValues(filepath.Join(t.TempDir(), "nonexistent.yaml"))
if err == nil {
t.Fatal("expected error for missing file, got nil")
}
}
func TestParseValues_InvalidYAML(t *testing.T) {
path := writeTemp(t, "key: [bad: yaml: :")
_, err := ParseValues(path)
if err == nil {
t.Fatal("expected error for invalid YAML, got nil")
}
}
func TestParseValues_EmptyFile(t *testing.T) {
path := writeTemp(t, "")
_, err := ParseValues(path)
if err != nil {
t.Fatalf("unexpected error for empty file: %v", err)
}
}
func TestGetNestedString_SingleKey(t *testing.T) {
values := map[string]interface{}{
"tag": "v1.0.0",
}
got, err := GetNestedString(values, "tag")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != "v1.0.0" {
t.Errorf("expected v1.0.0, got %s", got)
}
}
func TestGetNestedString_NestedKey(t *testing.T) {
values := map[string]interface{}{
"inference-backend": map[string]interface{}{
"images": map[string]interface{}{
"inferenceEngine": map[string]interface{}{
"tag": "v2.3.4",
},
},
},
}
got, err := GetNestedString(values, "inference-backend.images.inferenceEngine.tag")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != "v2.3.4" {
t.Errorf("expected v2.3.4, got %s", got)
}
}
func TestGetNestedString_MissingKey(t *testing.T) {
values := map[string]interface{}{
"a": map[string]interface{}{},
}
_, err := GetNestedString(values, "a.missing")
if err == nil {
t.Fatal("expected error for missing key, got nil")
}
}
func TestGetNestedString_NonMapIntermediate(t *testing.T) {
values := map[string]interface{}{
"a": "not-a-map",
}
_, err := GetNestedString(values, "a.b")
if err == nil {
t.Fatal("expected error when intermediate node is not a map, got nil")
}
}
func TestGetNestedString_NonStringLeaf(t *testing.T) {
values := map[string]interface{}{
"count": 42,
}
_, err := GetNestedString(values, "count")
if err == nil {
t.Fatal("expected error when leaf value is not a string, got nil")
}
}
func TestGetNestedString_TopLevelMissing(t *testing.T) {
values := map[string]interface{}{}
_, err := GetNestedString(values, "nonexistent")
if err == nil {
t.Fatal("expected error for missing top-level key, got nil")
}
}