]> git.proxmox.com Git - pve-common.git/blob - test/section_config_property_isolation_test.pl
bump version to 8.2.1
[pve-common.git] / test / section_config_property_isolation_test.pl
1 #!/usr/bin/perl
2
3 use lib '../src';
4
5 package Conf;
6 use strict;
7 use warnings;
8
9 use Test::More;
10
11 use base qw(PVE::SectionConfig);
12
13 my $defaultData = {
14 propertyList => {
15 type => { description => "Section type." },
16 id => {
17 description => "ID",
18 type => 'string',
19 format => 'pve-configid',
20 maxLength => 64,
21 },
22 common => {
23 type => 'string',
24 description => 'common value',
25 maxLength => 512,
26 },
27 },
28 };
29
30 sub private {
31 return $defaultData;
32 }
33
34 sub expect_success {
35 my ($class, $filename, $expected, $raw, $allow_unknown) = @_;
36
37 my $res = $class->parse_config($filename, $raw, $allow_unknown);
38 delete $res->{digest};
39
40 is_deeply($res, $expected, $filename);
41
42 my $written = $class->write_config($filename, $res, $allow_unknown);
43 my $res2 = $class->parse_config($filename, $written, $allow_unknown);
44 delete $res2->{digest};
45
46 is_deeply($res, $res2, "$filename - verify rewritten data");
47 }
48
49 sub expect_fail {
50 my ($class, $filename, $expected, $raw) = @_;
51
52 eval { $class->parse_config($filename, $raw) };
53 die "test '$filename' succeeded unexpectedly\n" if !$@;
54 ok(1, "$filename should fail to parse");
55 }
56
57 package Conf::One;
58 use strict;
59 use warnings;
60
61 use base 'Conf';
62
63 sub type {
64 return 'one';
65 }
66
67 sub properties {
68 return {
69 field1 => {
70 description => 'Field One',
71 type => 'integer',
72 minimum => 3,
73 maximum => 9,
74 },
75 field2 => {
76 description => 'Field Two',
77 type => 'integer',
78 minimum => 10,
79 maximum => 19,
80 },
81 another => {
82 description => 'Another field',
83 type => 'string',
84 optional => 1,
85 },
86 arrayfield => {
87 description => "Array Field with property string",
88 optional => 1,
89 type => 'array',
90 items => {
91 type => 'string',
92 description => 'a property string',
93 format => {
94 subfield1 => {
95 type => 'string',
96 description => 'first subfield'
97 },
98 subfield2 => {
99 type => 'integer',
100 minimum => 0,
101 optional => 1,
102 },
103 },
104 },
105 },
106 };
107 }
108
109 sub options {
110 return {
111 common => { optional => 1 },
112 };
113 }
114
115 package Conf::Two;
116 use strict;
117 use warnings;
118
119 use base 'Conf';
120
121 sub type {
122 return 'two';
123 }
124
125 sub properties {
126 return {
127 field2 => {
128 description => 'Field Two but different',
129 type => 'integer',
130 minimum => 3,
131 maximum => 9,
132 },
133 another => {
134 description => 'Another field',
135 type => 'string',
136 },
137 arrayfield => {
138 optional => 1,
139 description => "Array Field with property string",
140 type => 'array',
141 items => {
142 type => 'string',
143 description => 'a property string',
144 format => {
145 subfield1 => {
146 type => 'string',
147 description => 'first subfield'
148 },
149 subfield2 => {
150 type => 'integer',
151 minimum => 0,
152 optional => 1,
153 },
154 },
155 },
156 },
157 };
158 }
159
160 sub options {
161 return {
162 common => { optional => 1 },
163 };
164 }
165
166 package main;
167
168 use strict;
169 use warnings;
170
171 use Test::More;
172
173 Conf::One->register();
174 Conf::Two->register();
175 Conf->init(property_isolation => 1);
176
177 # FIXME: allow development debug warnings?!
178 local $SIG{__WARN__} = sub { die @_; };
179
180 my sub enum {
181 my $n = 1;
182 return { map { $_ => $n++ } @_ };
183 }
184
185 Conf->expect_success(
186 'property-isolation-test1',
187 {
188 ids => {
189 t1 => {
190 type => 'one',
191 common => 'foo',
192 field1 => 3,
193 field2 => 10,
194 arrayfield => [ 'subfield1=test' ],
195 },
196 t2 => {
197 type => 'one',
198 common => 'foo2',
199 field1 => 4,
200 field2 => 15,
201 another => 'more-text',
202 },
203 t3 => {
204 type => 'two',
205 field2 => 5,
206 another => 'even more text',
207 },
208 },
209 order => { t1 => 1, t2 => 2, t3 => 3 },
210 },
211 <<"EOF");
212 one: t1
213 common foo
214 field1 3
215 field2 10
216 arrayfield subfield1=test
217
218 one: t2
219 common foo2
220 field1 4
221 field2 15
222 another more-text
223
224 two: t3
225 field2 5
226 another even more text
227 EOF
228
229 my $with_unknown_data = {
230 ids => {
231 t1 => {
232 type => 'one',
233 common => 'foo',
234 field1 => 3,
235 field2 => 10,
236 },
237 t2 => {
238 type => 'one',
239 common => 'foo2',
240 field1 => 4,
241 field2 => 15,
242 another => 'more-text',
243 },
244 t3 => {
245 type => 'two',
246 field2 => 5,
247 another => 'even more text',
248 arrayfield => [
249 'subfield1=test,subfield2=2',
250 'subfield1=test2',
251 ],
252 },
253 invalid => {
254 type => 'bad',
255 common => 'omg',
256 unknownfield => 'shouldnotbehere',
257 unknownarray => ['entry1', 'entry2'],
258 },
259 },
260 order => enum(qw(t1 t2 invalid t3)),
261 };
262 my $with_unknown_text = <<"EOF";
263 one: t1
264 common foo
265 field1 3
266 field2 10
267
268 one: t2
269 common foo2
270 field1 4
271 field2 15
272 another more-text
273
274 bad: invalid
275 common omg
276 unknownfield shouldnotbehere
277 unknownarray entry1
278 unknownarray entry2
279
280 two: t3
281 field2 5
282 another even more text
283 arrayfield subfield1=test,subfield2=2
284 arrayfield subfield1=test2
285 EOF
286
287 my $wrong_field_schema_data = {
288 ids => {
289 t1 => {
290 type => 'one',
291 common => 'foo',
292 field1 => 3,
293 field2 => 5, # this should fail
294 },
295 },
296 order => enum(qw(t1)),
297 };
298
299 my $wrong_field_schema_text = <<"EOF";
300 one: t1
301 common foo
302 field1 3
303 field2 5
304 EOF
305
306 Conf->expect_fail('property-isolation-wrong-field-schema', $wrong_field_schema_data, $wrong_field_schema_text);
307 Conf->expect_fail('property-isolation-unknown-forbidden', $with_unknown_data, $with_unknown_text);
308 Conf->expect_success('property-isolation-unknown-allowed', $with_unknown_data, $with_unknown_text, 1);
309
310 # schema tests
311 my $create_schema = Conf->createSchema();
312 my $expected_create_schema = {
313 additionalProperties => 0,
314 type => 'object',
315 properties => {
316 id => {
317 description => "ID",
318 type => 'string',
319 format => 'pve-configid',
320 maxLength => 64,
321 },
322 type => {
323 description => 'Section type.',
324 enum => [ 'one', 'two' ],
325 type => 'string'
326 },
327 common => {
328 maxLength => 512,
329 optional => 1,
330 type => 'string',
331 description => 'common value'
332 },
333 field1 => {
334 type => 'integer',
335 'type-property' => 'type',
336 'instance-types' => [ 'one' ],
337 maximum => 9,
338 optional => 1,
339 minimum => 3,
340 description => 'Field One'
341 },
342 field2 => {
343 oneOf => [
344 {
345 description => 'Field Two',
346 optional => 1,
347 minimum => 10,
348 'instance-types' => [ 'one' ],
349 type => 'integer',
350 maximum => 19
351 },
352 {
353 optional => 1,
354 minimum => 3,
355 description => 'Field Two but different',
356 type => 'integer',
357 'instance-types' => [ 'two' ],
358 maximum => 9
359 }
360 ],
361 'type-property' => 'type'
362 },
363 arrayfield => {
364 items => {
365 type => 'string',
366 format => {
367 subfield1 => {
368 description => 'first subfield',
369 type => 'string'
370 },
371 subfield2 => {
372 minimum => 0,
373 type => 'integer',
374 optional => 1
375 }
376 },
377 description => 'a property string'
378 },
379 description => 'Array Field with property string',
380 type => 'array',
381 optional => 1
382 },
383 another => {
384 optional => 1,
385 type => 'string',
386 description => 'Another field'
387 },
388 },
389 };
390
391 is_deeply($create_schema, $expected_create_schema, "property-isolation create schema test");
392
393 my $update_schema = Conf->updateSchema();
394 my $expected_update_schema = {
395 additionalProperties => 0,
396 type => 'object',
397 properties => {
398 id => {
399 description => "ID",
400 type => 'string',
401 format => 'pve-configid',
402 maxLength => 64,
403 },
404 type => {
405 type => 'string',
406 enum => [ 'one', 'two' ],
407 description => 'Section type.'
408 },
409 digest => {
410 optional => 1,
411 type => 'string',
412 description => 'Prevent changes if current configuration file has a different digest. This can be used to prevent concurrent modifications.',
413 maxLength => 64
414 },
415 delete => {
416 description => 'A list of settings you want to delete.',
417 maxLength => 4096,
418 format => 'pve-configid-list',
419 optional => 1,
420 type => 'string'
421 },
422 common => {
423 maxLength => 512,
424 description => 'common value',
425 type => 'string',
426 optional => 1
427 },
428 field1 => {
429 description => 'Field One',
430 maximum => 9,
431 'instance-types' => [ 'one' ],
432 'type-property' => 'type',
433 minimum => 3,
434 optional => 1,
435 type => 'integer'
436 },
437 field2 => {
438 'type-property' => 'type',
439 oneOf => [
440 {
441 type => 'integer',
442 minimum => 10,
443 optional => 1,
444 maximum => 19,
445 'instance-types' => [ 'one' ],
446 description => 'Field Two'
447 },
448 {
449 description => 'Field Two but different',
450 maximum => 9,
451 'instance-types' => [ 'two' ],
452 minimum => 3,
453 optional => 1,
454 type => 'integer'
455 }
456 ]
457 },
458 arrayfield => {
459 type => 'array',
460 optional => 1,
461 items => {
462 description => 'a property string',
463 type => 'string',
464 format => {
465 subfield2 => {
466 type => 'integer',
467 minimum => 0,
468 optional => 1
469 },
470 subfield1 => {
471 description => 'first subfield',
472 type => 'string'
473 }
474 }
475 },
476 description => 'Array Field with property string'
477 },
478 another => {
479 description => 'Another field',
480 optional => 1,
481 type => 'string'
482 },
483 }
484 };
485 is_deeply($update_schema, $expected_update_schema, "property-isolation update schema test");
486
487 done_testing();
488
489 1;