master
cpp 293 lines 11.4 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WSLCE2EVolumeListTests.cpp
8
9 Abstract:
10
11 This file contains end-to-end tests for WSLC.
12 --*/
13
14 #include "precomp.h"
15 #include "windows/Common.h"
16 #include "VolumeModel.h"
17 #include "WSLCExecutor.h"
18 #include "WSLCE2EHelpers.h"
19
20 namespace WSLCE2ETests {
21 using namespace wsl::shared;
22 using namespace wsl::shared::string;
23 using namespace wsl::windows::wslc::models;
24
25 class WSLCE2EVolumeListTests
26 {
27 WSLC_TEST_CLASS(WSLCE2EVolumeListTests)
28
29 TEST_METHOD_SETUP(MethodSetup)
30 {
31 EnsureVolumeDoesNotExist(TestVolumeName);
32 EnsureVolumeDoesNotExist(TestVolumeName2);
33 for (const auto& name : FilterTestVolumeNames)
34 {
35 EnsureVolumeDoesNotExist(name);
36 }
37 return true;
38 }
39
40 TEST_CLASS_CLEANUP(ClassCleanup)
41 {
42 EnsureVolumeDoesNotExist(TestVolumeName);
43 EnsureVolumeDoesNotExist(TestVolumeName2);
44 for (const auto& name : FilterTestVolumeNames)
45 {
46 EnsureVolumeDoesNotExist(name);
47 }
48 return true;
49 }
50
51 WSLC_TEST_METHOD(WSLCE2E_Volume_List_HelpCommand)
52 {
53 auto result = RunWslc(L"volume list --help");
54 result.Verify({.Stderr = L"", .ExitCode = 0});
55 VERIFY_IS_FALSE(result.Stdout.value().empty());
56 }
57
58 WSLC_TEST_METHOD(WSLCE2E_Volume_List_InvalidFormatOption)
59 {
60 auto result = RunWslc(L"volume list --format invalid");
61 result.Verify({.Stdout = L"", .ExitCode = 1});
62 VERIFY_IS_TRUE(result.StderrContainsSubstring(
63 L"Invalid format value: invalid is not a recognized format type. Supported format types are: json, table."));
64 }
65
66 WSLC_TEST_METHOD(WSLCE2E_Volume_List_QuietOption_OutputsNamesOnly)
67 {
68 auto result = RunWslc(std::format(L"volume create {}", TestVolumeName));
69 result.Verify({.Stderr = L"", .ExitCode = 0});
70 result = RunWslc(std::format(L"volume create {}", TestVolumeName2));
71 result.Verify({.Stderr = L"", .ExitCode = 0});
72
73 result = RunWslc(L"volume list --quiet");
74 result.Verify({.Stderr = L"", .ExitCode = 0});
75
76 auto lines = result.GetStdoutLines();
77 VERIFY_ARE_NOT_EQUAL(lines.end(), std::find(lines.begin(), lines.end(), TestVolumeName));
78 VERIFY_ARE_NOT_EQUAL(lines.end(), std::find(lines.begin(), lines.end(), TestVolumeName2));
79 }
80
81 WSLC_TEST_METHOD(WSLCE2E_Volume_List_JsonFormat)
82 {
83 auto result = RunWslc(std::format(L"volume create {}", TestVolumeName));
84 result.Verify({.Stderr = L"", .ExitCode = 0});
85 result = RunWslc(std::format(L"volume create {}", TestVolumeName2));
86 result.Verify({.Stderr = L"", .ExitCode = 0});
87
88 result = RunWslc(L"volume list --format json");
89 result.Verify({.Stderr = L"", .ExitCode = 0});
90
91 auto volumes = ParseNdjsonOutputAs<VolumeListOutput>(result);
92 VERIFY_ARE_EQUAL(2U, volumes.size());
93
94 std::vector<std::string> names;
95 names.reserve(volumes.size());
96 for (const auto& volume : volumes)
97 {
98 names.push_back(volume.Name);
99 }
100
101 VERIFY_ARE_NOT_EQUAL(names.end(), std::find(names.begin(), names.end(), WideToMultiByte(TestVolumeName)));
102 VERIFY_ARE_NOT_EQUAL(names.end(), std::find(names.begin(), names.end(), WideToMultiByte(TestVolumeName2)));
103 }
104
105 WSLC_TEST_METHOD(WSLCE2E_Volume_List_ReportsFullFieldSet)
106 {
107 auto result = RunWslc(std::format(L"volume create --label env=prod {}", TestVolumeName));
108 result.Verify({.Stderr = L"", .ExitCode = 0});
109
110 result = RunWslc(L"volume list --format json");
111 result.Verify({.Stderr = L"", .ExitCode = 0});
112
113 const auto entries = ParseNdjsonOutput(result);
114 VERIFY_ARE_EQUAL(1u, entries.size());
115 const auto& volume = entries[0];
116
117 VERIFY_ARE_EQUAL(10u, volume.size());
118 VERIFY_ARE_EQUAL("N/A", volume["Availability"].get<std::string>());
119 VERIFY_ARE_EQUAL("guest", volume["Driver"].get<std::string>());
120 VERIFY_ARE_EQUAL("N/A", volume["Group"].get<std::string>());
121 VERIFY_ARE_EQUAL("env=prod", volume["Labels"].get<std::string>());
122 VERIFY_ARE_EQUAL("N/A", volume["Links"].get<std::string>());
123 VERIFY_IS_FALSE(volume["Mountpoint"].get<std::string>().empty());
124 VERIFY_ARE_EQUAL(WideToMultiByte(TestVolumeName), volume["Name"].get<std::string>());
125 VERIFY_ARE_EQUAL("local", volume["Scope"].get<std::string>());
126 VERIFY_ARE_EQUAL("N/A", volume["Size"].get<std::string>());
127 VERIFY_ARE_EQUAL("N/A", volume["Status"].get<std::string>());
128 }
129
130 WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_MalformedValue)
131 {
132 const auto result = RunWslc(L"volume list --filter label");
133 result.Verify({.Stdout = L"", .ExitCode = 1});
134 VERIFY_IS_TRUE(result.StderrContainsSubstring(Localization::WSLCCLI_InvalidFilterError(L"label")));
135 }
136
137 WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_InvalidKey)
138 {
139 // Filter keys are validated by the Docker daemon, which rejects unknown keys.
140 const auto result = RunWslc(L"volume list --filter color=blue");
141 VERIFY_ARE_EQUAL(1, result.ExitCode);
142 VERIFY_IS_TRUE(result.Stderr.has_value());
143 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, result.Stderr->find(L"invalid filter 'color'"));
144 }
145
146 WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_InvalidDanglingValue)
147 {
148 // Dangling values are validated by the Docker daemon, which rejects non-boolean values.
149 const auto result = RunWslc(L"volume list --filter dangling=maybe");
150 VERIFY_ARE_EQUAL(1, result.ExitCode);
151 VERIFY_IS_TRUE(result.Stderr.has_value());
152 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, result.Stderr->find(L"invalid filter 'dangling=[maybe]'"));
153 }
154
155 WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_Driver)
156 {
157 const std::wstring alpha = L"wslc-flt-vlist-driver-alpha";
158 const std::wstring beta = L"wslc-flt-vlist-driver-beta";
159 auto cleanup = wil::scope_exit([&]() {
160 EnsureVolumeDoesNotExist(alpha);
161 EnsureVolumeDoesNotExist(beta);
162 });
163
164 auto result = RunWslc(std::format(L"volume create --driver vhd --opt SizeBytes={} {}", DefaultVolumeSizeBytes, alpha));
165 result.Verify({.Stderr = L"", .ExitCode = 0});
166 result = RunWslc(std::format(L"volume create --driver guest {}", beta));
167 result.Verify({.Stderr = L"", .ExitCode = 0});
168
169 {
170 const auto names = GetFilteredVolumeNames(L"--filter driver=vhd");
171 VERIFY_IS_TRUE(names.contains(WideToMultiByte(alpha)));
172 VERIFY_IS_FALSE(names.contains(WideToMultiByte(beta)));
173 }
174
175 {
176 const auto names = GetFilteredVolumeNames(L"--filter driver=guest");
177 VERIFY_IS_FALSE(names.contains(WideToMultiByte(alpha)));
178 VERIFY_IS_TRUE(names.contains(WideToMultiByte(beta)));
179 }
180
181 // Docker's own driver names (e.g. "local") never match wslc-managed volumes.
182 {
183 const auto names = GetFilteredVolumeNames(L"--filter driver=local");
184 VERIFY_IS_FALSE(names.contains(WideToMultiByte(alpha)));
185 VERIFY_IS_FALSE(names.contains(WideToMultiByte(beta)));
186 }
187 }
188
189 WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_Label)
190 {
191 const std::wstring alpha = L"wslc-flt-vlist-label-alpha";
192 const std::wstring beta = L"wslc-flt-vlist-label-beta";
193 const std::wstring scopeKey = L"wslc.e2e.list_filter_label";
194 const std::wstring scopeValue = L"1";
195
196 auto cleanup = wil::scope_exit([&]() {
197 EnsureVolumeDoesNotExist(alpha);
198 EnsureVolumeDoesNotExist(beta);
199 });
200
201 auto result = RunWslc(std::format(L"volume create --label {}={} --label env=prod {}", scopeKey, scopeValue, alpha));
202 result.Verify({.Stderr = L"", .ExitCode = 0});
203
204 result = RunWslc(std::format(L"volume create --label {}={} {}", scopeKey, scopeValue, beta));
205 result.Verify({.Stderr = L"", .ExitCode = 0});
206
207 {
208 const auto names = GetFilteredVolumeNames(std::format(L"--filter label={}", scopeKey));
209 VERIFY_IS_TRUE(names.contains(WideToMultiByte(alpha)));
210 VERIFY_IS_TRUE(names.contains(WideToMultiByte(beta)));
211 }
212
213 {
214 const auto names = GetFilteredVolumeNames(std::format(L"--filter label={}={} --filter label=env=prod", scopeKey, scopeValue));
215 VERIFY_IS_TRUE(names.contains(WideToMultiByte(alpha)));
216 VERIFY_IS_FALSE(names.contains(WideToMultiByte(beta)));
217 }
218
219 {
220 const auto names = GetFilteredVolumeNames(std::format(L"--filter label={} --filter label=env=prod", scopeKey));
221 VERIFY_IS_TRUE(names.contains(WideToMultiByte(alpha)));
222 VERIFY_IS_FALSE(names.contains(WideToMultiByte(beta)));
223 }
224 }
225
226 WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_JsonEmptyIsExactlyEmpty)
227 {
228 const std::wstring alpha = L"wslc-flt-vlist-empty-alpha";
229 auto cleanup = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(alpha); });
230
231 auto result = RunWslc(std::format(L"volume create {}", alpha));
232 result.Verify({.Stderr = L"", .ExitCode = 0});
233
234 // NDJSON with zero rows must be exactly empty stdout — not "[]", not "\n".
235 result = RunWslc(L"volume list --format json --filter name=wslc-flt-vlist-no-such-volume-zzz");
236 result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
237 }
238
239 WSLC_TEST_METHOD(WSLCE2E_Volume_List_Filter_Name)
240 {
241 const std::wstring alpha = L"wslc-flt-vlist-name-alpha";
242 const std::wstring beta = L"wslc-flt-vlist-name-beta";
243 auto cleanup = wil::scope_exit([&]() {
244 EnsureVolumeDoesNotExist(alpha);
245 EnsureVolumeDoesNotExist(beta);
246 });
247
248 auto result = RunWslc(std::format(L"volume create {}", alpha));
249 result.Verify({.Stderr = L"", .ExitCode = 0});
250 result = RunWslc(std::format(L"volume create {}", beta));
251 result.Verify({.Stderr = L"", .ExitCode = 0});
252
253 {
254 const auto names = GetFilteredVolumeNames(L"--filter name=wslc-flt-vlist-name-");
255 VERIFY_IS_TRUE(names.contains(WideToMultiByte(alpha)));
256 VERIFY_IS_TRUE(names.contains(WideToMultiByte(beta)));
257 }
258
259 {
260 const auto names = GetFilteredVolumeNames(L"--filter name=name-alpha");
261 VERIFY_IS_TRUE(names.contains(WideToMultiByte(alpha)));
262 VERIFY_IS_FALSE(names.contains(WideToMultiByte(beta)));
263 }
264 }
265
266 private:
267 static std::set<std::string> GetFilteredVolumeNames(const std::wstring& filterArgs)
268 {
269 auto result = RunWslc(std::format(L"volume list --format json {}", filterArgs));
270 result.Verify({.Stderr = L"", .ExitCode = 0});
271 const auto volumes = ParseNdjsonOutputAs<VolumeListOutput>(result);
272 std::set<std::string> names;
273 for (const auto& v : volumes)
274 {
275 names.insert(v.Name);
276 }
277 return names;
278 }
279
280 const std::wstring TestVolumeName = L"wslc-e2e-volume-list";
281 const std::wstring TestVolumeName2 = L"wslc-e2e-volume-list-2";
282 const int DefaultVolumeSizeBytes = 3 * 1024 * 1024;
283 const std::vector<std::wstring> FilterTestVolumeNames = {
284 L"wslc-flt-vlist-driver-alpha",
285 L"wslc-flt-vlist-driver-beta",
286 L"wslc-flt-vlist-label-alpha",
287 L"wslc-flt-vlist-label-beta",
288 L"wslc-flt-vlist-empty-alpha",
289 L"wslc-flt-vlist-name-alpha",
290 L"wslc-flt-vlist-name-beta",
291 };
292 };
293 } // namespace WSLCE2ETests