|
1 | | -package create |
| 1 | +package create_test |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "strings" |
| 4 | + "context" |
5 | 5 | "testing" |
| 6 | + |
| 7 | + "github.com/containers/kubernetes-mcp-server/pkg/api" |
| 8 | + internalk8s "github.com/containers/kubernetes-mcp-server/pkg/kubernetes" |
| 9 | + "github.com/containers/kubernetes-mcp-server/pkg/toolsets/kubevirt/vm/create" |
6 | 10 | ) |
7 | 11 |
|
8 | | -// Test the YAML rendering directly without creating resources |
9 | | -func TestRenderVMYaml(t *testing.T) { |
| 12 | +type mockToolCallRequest struct { |
| 13 | + arguments map[string]interface{} |
| 14 | +} |
| 15 | + |
| 16 | +func (m *mockToolCallRequest) GetArguments() map[string]any { |
| 17 | + return m.arguments |
| 18 | +} |
| 19 | + |
| 20 | +func TestCreateParameterValidation(t *testing.T) { |
10 | 21 | tests := []struct { |
11 | | - name string |
12 | | - params vmParams |
13 | | - wantErr bool |
14 | | - checkFunc func(t *testing.T, result string) |
| 22 | + name string |
| 23 | + args map[string]interface{} |
| 24 | + wantErr bool |
| 25 | + errMsg string |
15 | 26 | }{ |
16 | 27 | { |
17 | | - name: "renders VM with basic settings", |
18 | | - params: vmParams{ |
19 | | - Namespace: "test-ns", |
20 | | - Name: "test-vm", |
21 | | - ContainerDisk: "quay.io/containerdisks/fedora:latest", |
22 | | - RunStrategy: "Halted", |
23 | | - }, |
24 | | - wantErr: false, |
25 | | - checkFunc: func(t *testing.T, result string) { |
26 | | - if !strings.Contains(result, "apiVersion: kubevirt.io/v1") { |
27 | | - t.Errorf("Expected apiVersion in YAML") |
28 | | - } |
29 | | - if !strings.Contains(result, "kind: VirtualMachine") { |
30 | | - t.Errorf("Expected kind VirtualMachine in YAML") |
31 | | - } |
32 | | - if !strings.Contains(result, "name: test-vm") { |
33 | | - t.Errorf("Expected VM name test-vm in YAML") |
34 | | - } |
35 | | - if !strings.Contains(result, "namespace: test-ns") { |
36 | | - t.Errorf("Expected namespace test-ns in YAML") |
37 | | - } |
38 | | - if !strings.Contains(result, "quay.io/containerdisks/fedora:latest") { |
39 | | - t.Errorf("Expected fedora container disk in result") |
40 | | - } |
41 | | - if !strings.Contains(result, "guest: 2Gi") { |
42 | | - t.Errorf("Expected guest: 2Gi in YAML manifest") |
43 | | - } |
44 | | - }, |
45 | | - }, |
46 | | - { |
47 | | - name: "renders VM with instancetype", |
48 | | - params: vmParams{ |
49 | | - Namespace: "test-ns", |
50 | | - Name: "test-vm", |
51 | | - ContainerDisk: "quay.io/containerdisks/ubuntu:24.04", |
52 | | - Instancetype: "u1.medium", |
53 | | - RunStrategy: "Halted", |
54 | | - }, |
55 | | - wantErr: false, |
56 | | - checkFunc: func(t *testing.T, result string) { |
57 | | - if !strings.Contains(result, "name: u1.medium") { |
58 | | - t.Errorf("Expected instance type in YAML manifest") |
59 | | - } |
60 | | - if !strings.Contains(result, "kind: VirtualMachineClusterInstancetype") { |
61 | | - t.Errorf("Expected VirtualMachineClusterInstancetype in YAML manifest") |
62 | | - } |
63 | | - // When instancetype is set, memory should not be in the YAML |
64 | | - if strings.Contains(result, "guest: 2Gi") { |
65 | | - t.Errorf("Should not have guest memory when instancetype is specified") |
66 | | - } |
| 28 | + name: "missing namespace parameter", |
| 29 | + args: map[string]interface{}{ |
| 30 | + "name": "test-vm", |
67 | 31 | }, |
| 32 | + wantErr: true, |
| 33 | + errMsg: "namespace parameter required", |
68 | 34 | }, |
69 | 35 | { |
70 | | - name: "renders VM with preference", |
71 | | - params: vmParams{ |
72 | | - Namespace: "test-ns", |
73 | | - Name: "test-vm", |
74 | | - ContainerDisk: "registry.redhat.io/rhel9/rhel-guest-image:latest", |
75 | | - Preference: "rhel.9", |
76 | | - RunStrategy: "Halted", |
77 | | - }, |
78 | | - wantErr: false, |
79 | | - checkFunc: func(t *testing.T, result string) { |
80 | | - if !strings.Contains(result, "name: rhel.9") { |
81 | | - t.Errorf("Expected preference in YAML manifest") |
82 | | - } |
83 | | - if !strings.Contains(result, "kind: VirtualMachineClusterPreference") { |
84 | | - t.Errorf("Expected VirtualMachineClusterPreference in YAML manifest") |
85 | | - } |
86 | | - }, |
87 | | - }, |
88 | | - { |
89 | | - name: "renders VM with custom container disk", |
90 | | - params: vmParams{ |
91 | | - Namespace: "test-ns", |
92 | | - Name: "test-vm", |
93 | | - ContainerDisk: "quay.io/myrepo/myimage:v1.0", |
94 | | - RunStrategy: "Halted", |
95 | | - }, |
96 | | - wantErr: false, |
97 | | - checkFunc: func(t *testing.T, result string) { |
98 | | - if !strings.Contains(result, "quay.io/myrepo/myimage:v1.0") { |
99 | | - t.Errorf("Expected custom container disk in YAML") |
100 | | - } |
| 36 | + name: "missing name parameter", |
| 37 | + args: map[string]interface{}{ |
| 38 | + "namespace": "test-ns", |
101 | 39 | }, |
| 40 | + wantErr: true, |
| 41 | + errMsg: "name parameter required", |
102 | 42 | }, |
103 | 43 | { |
104 | | - name: "renders VM with DataSource", |
105 | | - params: vmParams{ |
106 | | - Namespace: "test-ns", |
107 | | - Name: "test-vm", |
108 | | - UseDataSource: true, |
109 | | - DataSourceName: "fedora", |
110 | | - DataSourceNamespace: "openshift-virtualization-os-images", |
111 | | - RunStrategy: "Halted", |
112 | | - }, |
113 | | - wantErr: false, |
114 | | - checkFunc: func(t *testing.T, result string) { |
115 | | - if !strings.Contains(result, "dataVolumeTemplates") { |
116 | | - t.Errorf("Expected dataVolumeTemplates in YAML") |
117 | | - } |
118 | | - if !strings.Contains(result, "kind: DataSource") { |
119 | | - t.Errorf("Expected DataSource kind in YAML") |
120 | | - } |
121 | | - if !strings.Contains(result, "name: fedora") { |
122 | | - t.Errorf("Expected DataSource name in YAML") |
123 | | - } |
124 | | - if !strings.Contains(result, "openshift-virtualization-os-images") { |
125 | | - t.Errorf("Expected DataSource namespace in YAML") |
126 | | - } |
| 44 | + name: "invalid namespace type", |
| 45 | + args: map[string]interface{}{ |
| 46 | + "namespace": 123, |
| 47 | + "name": "test-vm", |
127 | 48 | }, |
| 49 | + wantErr: true, |
| 50 | + errMsg: "namespace parameter must be a string", |
128 | 51 | }, |
129 | 52 | { |
130 | | - name: "renders VM with autostart (runStrategy Always)", |
131 | | - params: vmParams{ |
132 | | - Namespace: "test-ns", |
133 | | - Name: "test-vm", |
134 | | - ContainerDisk: "quay.io/containerdisks/fedora:latest", |
135 | | - RunStrategy: "Always", |
136 | | - }, |
137 | | - wantErr: false, |
138 | | - checkFunc: func(t *testing.T, result string) { |
139 | | - if !strings.Contains(result, "runStrategy: Always") { |
140 | | - t.Errorf("Expected runStrategy: Always in YAML") |
141 | | - } |
| 53 | + name: "invalid name type", |
| 54 | + args: map[string]interface{}{ |
| 55 | + "namespace": "test-ns", |
| 56 | + "name": 456, |
142 | 57 | }, |
| 58 | + wantErr: true, |
| 59 | + errMsg: "name parameter must be a string", |
143 | 60 | }, |
144 | 61 | } |
145 | 62 |
|
| 63 | + // Get the tool through the public API |
| 64 | + tools := create.Tools() |
| 65 | + if len(tools) != 1 { |
| 66 | + t.Fatalf("Expected 1 tool, got %d", len(tools)) |
| 67 | + } |
| 68 | + vmCreateTool := tools[0] |
| 69 | + |
146 | 70 | for _, tt := range tests { |
147 | 71 | t.Run(tt.name, func(t *testing.T) { |
148 | | - result, err := renderVMYaml(tt.params) |
| 72 | + params := api.ToolHandlerParams{ |
| 73 | + Context: context.Background(), |
| 74 | + Kubernetes: &internalk8s.Kubernetes{}, |
| 75 | + ToolCallRequest: &mockToolCallRequest{arguments: tt.args}, |
| 76 | + } |
149 | 77 |
|
150 | | - if tt.wantErr { |
151 | | - if err == nil { |
152 | | - t.Error("Expected error, got nil") |
153 | | - } |
154 | | - } else { |
155 | | - if err != nil { |
156 | | - t.Errorf("Expected no error, got: %v", err) |
157 | | - } |
158 | | - if result == "" { |
159 | | - t.Error("Expected non-empty result") |
160 | | - } |
161 | | - if tt.checkFunc != nil { |
162 | | - tt.checkFunc(t, result) |
163 | | - } |
| 78 | + // Call through the public Handler interface |
| 79 | + result, err := vmCreateTool.Handler(params) |
| 80 | + if err != nil { |
| 81 | + t.Errorf("Handler() unexpected Go error: %v", err) |
| 82 | + return |
164 | 83 | } |
165 | | - }) |
166 | | - } |
167 | | -} |
168 | 84 |
|
169 | | -func TestResolveContainerDisk(t *testing.T) { |
170 | | - tests := []struct { |
171 | | - name string |
172 | | - input string |
173 | | - expected string |
174 | | - }{ |
175 | | - {"fedora", "fedora", "quay.io/containerdisks/fedora:latest"}, |
176 | | - {"ubuntu", "ubuntu", "quay.io/containerdisks/ubuntu:24.04"}, |
177 | | - {"rhel8", "rhel8", "registry.redhat.io/rhel8/rhel-guest-image:latest"}, |
178 | | - {"rhel9", "rhel9", "registry.redhat.io/rhel9/rhel-guest-image:latest"}, |
179 | | - {"rhel10", "rhel10", "registry.redhat.io/rhel10/rhel-guest-image:latest"}, |
180 | | - {"centos", "centos", "quay.io/containerdisks/centos-stream:9-latest"}, |
181 | | - {"centos-stream", "centos-stream", "quay.io/containerdisks/centos-stream:9-latest"}, |
182 | | - {"debian", "debian", "quay.io/containerdisks/debian:latest"}, |
183 | | - {"case insensitive", "FEDORA", "quay.io/containerdisks/fedora:latest"}, |
184 | | - {"with whitespace", " ubuntu ", "quay.io/containerdisks/ubuntu:24.04"}, |
185 | | - {"custom image", "quay.io/myrepo/myimage:v1", "quay.io/myrepo/myimage:v1"}, |
186 | | - {"with tag", "myimage:latest", "myimage:latest"}, |
187 | | - {"unknown OS", "customos", "customos"}, |
188 | | - } |
| 85 | + if result == nil { |
| 86 | + t.Error("Expected non-nil result") |
| 87 | + return |
| 88 | + } |
189 | 89 |
|
190 | | - for _, tt := range tests { |
191 | | - t.Run(tt.name, func(t *testing.T) { |
192 | | - result := resolveContainerDisk(tt.input) |
193 | | - if result != tt.expected { |
194 | | - t.Errorf("resolveContainerDisk(%s) = %s, want %s", tt.input, result, tt.expected) |
| 90 | + // For parameter validation errors, check the error message |
| 91 | + if tt.wantErr && tt.errMsg != "" { |
| 92 | + if result.Error == nil { |
| 93 | + t.Error("Expected error in result.Error, got nil") |
| 94 | + return |
| 95 | + } |
| 96 | + if result.Error.Error() != tt.errMsg { |
| 97 | + t.Errorf("Expected error message %q, got %q", tt.errMsg, result.Error.Error()) |
| 98 | + } |
195 | 99 | } |
196 | 100 | }) |
197 | 101 | } |
|
0 commit comments