mirror of
https://git.proxmox.com/git/pve-common
synced 2025-04-28 12:10:17 +00:00
section config: add tests for separated property lists
more or less a copy from the normal section config test, but now with properties defined multiple times as well as conflicting options Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> [ TL: improve consistency with property-isolation terminology ] Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
7887b1cb0e
commit
7d6cc4f14f
@ -7,6 +7,7 @@ TESTS = lock_file.test \
|
||||
section_config_test.test \
|
||||
api_parameter_test.test \
|
||||
is_deeply_test.test \
|
||||
section_config_property_isolation_test.pl \
|
||||
|
||||
all:
|
||||
|
||||
|
489
test/section_config_property_isolation_test.pl
Executable file
489
test/section_config_property_isolation_test.pl
Executable file
@ -0,0 +1,489 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use lib '../src';
|
||||
|
||||
package Conf;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Test::More;
|
||||
|
||||
use base qw(PVE::SectionConfig);
|
||||
|
||||
my $defaultData = {
|
||||
propertyList => {
|
||||
type => { description => "Section type." },
|
||||
id => {
|
||||
description => "ID",
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
maxLength => 64,
|
||||
},
|
||||
common => {
|
||||
type => 'string',
|
||||
description => 'common value',
|
||||
maxLength => 512,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
sub private {
|
||||
return $defaultData;
|
||||
}
|
||||
|
||||
sub expect_success {
|
||||
my ($class, $filename, $expected, $raw, $allow_unknown) = @_;
|
||||
|
||||
my $res = $class->parse_config($filename, $raw, $allow_unknown);
|
||||
delete $res->{digest};
|
||||
|
||||
is_deeply($res, $expected, $filename);
|
||||
|
||||
my $written = $class->write_config($filename, $res, $allow_unknown);
|
||||
my $res2 = $class->parse_config($filename, $written, $allow_unknown);
|
||||
delete $res2->{digest};
|
||||
|
||||
is_deeply($res, $res2, "$filename - verify rewritten data");
|
||||
}
|
||||
|
||||
sub expect_fail {
|
||||
my ($class, $filename, $expected, $raw) = @_;
|
||||
|
||||
eval { $class->parse_config($filename, $raw) };
|
||||
die "test '$filename' succeeded unexpectedly\n" if !$@;
|
||||
ok(1, "$filename should fail to parse");
|
||||
}
|
||||
|
||||
package Conf::One;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'Conf';
|
||||
|
||||
sub type {
|
||||
return 'one';
|
||||
}
|
||||
|
||||
sub properties {
|
||||
return {
|
||||
field1 => {
|
||||
description => 'Field One',
|
||||
type => 'integer',
|
||||
minimum => 3,
|
||||
maximum => 9,
|
||||
},
|
||||
field2 => {
|
||||
description => 'Field Two',
|
||||
type => 'integer',
|
||||
minimum => 10,
|
||||
maximum => 19,
|
||||
},
|
||||
another => {
|
||||
description => 'Another field',
|
||||
type => 'string',
|
||||
optional => 1,
|
||||
},
|
||||
arrayfield => {
|
||||
description => "Array Field with property string",
|
||||
optional => 1,
|
||||
type => 'array',
|
||||
items => {
|
||||
type => 'string',
|
||||
description => 'a property string',
|
||||
format => {
|
||||
subfield1 => {
|
||||
type => 'string',
|
||||
description => 'first subfield'
|
||||
},
|
||||
subfield2 => {
|
||||
type => 'integer',
|
||||
minimum => 0,
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
sub options {
|
||||
return {
|
||||
common => { optional => 1 },
|
||||
};
|
||||
}
|
||||
|
||||
package Conf::Two;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'Conf';
|
||||
|
||||
sub type {
|
||||
return 'two';
|
||||
}
|
||||
|
||||
sub properties {
|
||||
return {
|
||||
field2 => {
|
||||
description => 'Field Two but different',
|
||||
type => 'integer',
|
||||
minimum => 3,
|
||||
maximum => 9,
|
||||
},
|
||||
another => {
|
||||
description => 'Another field',
|
||||
type => 'string',
|
||||
},
|
||||
arrayfield => {
|
||||
optional => 1,
|
||||
description => "Array Field with property string",
|
||||
type => 'array',
|
||||
items => {
|
||||
type => 'string',
|
||||
description => 'a property string',
|
||||
format => {
|
||||
subfield1 => {
|
||||
type => 'string',
|
||||
description => 'first subfield'
|
||||
},
|
||||
subfield2 => {
|
||||
type => 'integer',
|
||||
minimum => 0,
|
||||
optional => 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
sub options {
|
||||
return {
|
||||
common => { optional => 1 },
|
||||
};
|
||||
}
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Test::More;
|
||||
|
||||
Conf::One->register();
|
||||
Conf::Two->register();
|
||||
Conf->init(1);
|
||||
|
||||
# FIXME: allow development debug warnings?!
|
||||
local $SIG{__WARN__} = sub { die @_; };
|
||||
|
||||
my sub enum {
|
||||
my $n = 1;
|
||||
return { map { $_ => $n++ } @_ };
|
||||
}
|
||||
|
||||
Conf->expect_success(
|
||||
'property-isolation-test1',
|
||||
{
|
||||
ids => {
|
||||
t1 => {
|
||||
type => 'one',
|
||||
common => 'foo',
|
||||
field1 => 3,
|
||||
field2 => 10,
|
||||
arrayfield => [ 'subfield1=test' ],
|
||||
},
|
||||
t2 => {
|
||||
type => 'one',
|
||||
common => 'foo2',
|
||||
field1 => 4,
|
||||
field2 => 15,
|
||||
another => 'more-text',
|
||||
},
|
||||
t3 => {
|
||||
type => 'two',
|
||||
field2 => 5,
|
||||
another => 'even more text',
|
||||
},
|
||||
},
|
||||
order => { t1 => 1, t2 => 2, t3 => 3 },
|
||||
},
|
||||
<<"EOF");
|
||||
one: t1
|
||||
common foo
|
||||
field1 3
|
||||
field2 10
|
||||
arrayfield subfield1=test
|
||||
|
||||
one: t2
|
||||
common foo2
|
||||
field1 4
|
||||
field2 15
|
||||
another more-text
|
||||
|
||||
two: t3
|
||||
field2 5
|
||||
another even more text
|
||||
EOF
|
||||
|
||||
my $with_unknown_data = {
|
||||
ids => {
|
||||
t1 => {
|
||||
type => 'one',
|
||||
common => 'foo',
|
||||
field1 => 3,
|
||||
field2 => 10,
|
||||
},
|
||||
t2 => {
|
||||
type => 'one',
|
||||
common => 'foo2',
|
||||
field1 => 4,
|
||||
field2 => 15,
|
||||
another => 'more-text',
|
||||
},
|
||||
t3 => {
|
||||
type => 'two',
|
||||
field2 => 5,
|
||||
another => 'even more text',
|
||||
arrayfield => [
|
||||
'subfield1=test,subfield2=2',
|
||||
'subfield1=test2',
|
||||
],
|
||||
},
|
||||
invalid => {
|
||||
type => 'bad',
|
||||
common => 'omg',
|
||||
unknownfield => 'shouldnotbehere',
|
||||
unknownarray => ['entry1', 'entry2'],
|
||||
},
|
||||
},
|
||||
order => enum(qw(t1 t2 invalid t3)),
|
||||
};
|
||||
my $with_unknown_text = <<"EOF";
|
||||
one: t1
|
||||
common foo
|
||||
field1 3
|
||||
field2 10
|
||||
|
||||
one: t2
|
||||
common foo2
|
||||
field1 4
|
||||
field2 15
|
||||
another more-text
|
||||
|
||||
bad: invalid
|
||||
common omg
|
||||
unknownfield shouldnotbehere
|
||||
unknownarray entry1
|
||||
unknownarray entry2
|
||||
|
||||
two: t3
|
||||
field2 5
|
||||
another even more text
|
||||
arrayfield subfield1=test,subfield2=2
|
||||
arrayfield subfield1=test2
|
||||
EOF
|
||||
|
||||
my $wrong_field_schema_data = {
|
||||
ids => {
|
||||
t1 => {
|
||||
type => 'one',
|
||||
common => 'foo',
|
||||
field1 => 3,
|
||||
field2 => 5, # this should fail
|
||||
},
|
||||
},
|
||||
order => enum(qw(t1)),
|
||||
};
|
||||
|
||||
my $wrong_field_schema_text = <<"EOF";
|
||||
one: t1
|
||||
common foo
|
||||
field1 3
|
||||
field2 5
|
||||
EOF
|
||||
|
||||
Conf->expect_fail('property-isolation-wrong-field-schema', $wrong_field_schema_data, $wrong_field_schema_text);
|
||||
Conf->expect_fail('property-isolation-unknown-forbidden', $with_unknown_data, $with_unknown_text);
|
||||
Conf->expect_success('property-isolation-unknown-allowed', $with_unknown_data, $with_unknown_text, 1);
|
||||
|
||||
# schema tests
|
||||
my $create_schema = Conf->createSchema();
|
||||
my $expected_create_schema = {
|
||||
additionalProperties => 0,
|
||||
type => 'object',
|
||||
properties => {
|
||||
id => {
|
||||
description => "ID",
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
maxLength => 64,
|
||||
},
|
||||
type => {
|
||||
description => 'Section type.',
|
||||
enum => [ 'one', 'two' ],
|
||||
type => 'string'
|
||||
},
|
||||
common => {
|
||||
maxLength => 512,
|
||||
optional => 1,
|
||||
type => 'string',
|
||||
description => 'common value'
|
||||
},
|
||||
field1 => {
|
||||
type => 'integer',
|
||||
'type-property' => 'type',
|
||||
'instance-types' => [ 'one' ],
|
||||
maximum => 9,
|
||||
optional => 1,
|
||||
minimum => 3,
|
||||
description => 'Field One'
|
||||
},
|
||||
field2 => {
|
||||
oneOf => [
|
||||
{
|
||||
description => 'Field Two',
|
||||
optional => 1,
|
||||
minimum => 10,
|
||||
'instance-types' => [ 'one' ],
|
||||
type => 'integer',
|
||||
maximum => 19
|
||||
},
|
||||
{
|
||||
optional => 1,
|
||||
minimum => 3,
|
||||
description => 'Field Two but different',
|
||||
type => 'integer',
|
||||
'instance-types' => [ 'two' ],
|
||||
maximum => 9
|
||||
}
|
||||
],
|
||||
'type-property' => 'type'
|
||||
},
|
||||
arrayfield => {
|
||||
items => {
|
||||
type => 'string',
|
||||
format => {
|
||||
subfield1 => {
|
||||
description => 'first subfield',
|
||||
type => 'string'
|
||||
},
|
||||
subfield2 => {
|
||||
minimum => 0,
|
||||
type => 'integer',
|
||||
optional => 1
|
||||
}
|
||||
},
|
||||
description => 'a property string'
|
||||
},
|
||||
description => 'Array Field with property string',
|
||||
type => 'array',
|
||||
optional => 1
|
||||
},
|
||||
another => {
|
||||
optional => 1,
|
||||
type => 'string',
|
||||
description => 'Another field'
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
is_deeply($create_schema, $expected_create_schema, "property-isolation create schema test");
|
||||
|
||||
my $update_schema = Conf->updateSchema();
|
||||
my $expected_update_schema = {
|
||||
additionalProperties => 0,
|
||||
type => 'object',
|
||||
properties => {
|
||||
id => {
|
||||
description => "ID",
|
||||
type => 'string',
|
||||
format => 'pve-configid',
|
||||
maxLength => 64,
|
||||
},
|
||||
type => {
|
||||
type => 'string',
|
||||
enum => [ 'one', 'two' ],
|
||||
description => 'Section type.'
|
||||
},
|
||||
digest => {
|
||||
optional => 1,
|
||||
type => 'string',
|
||||
description => 'Prevent changes if current configuration file has a different digest. This can be used to prevent concurrent modifications.',
|
||||
maxLength => 64
|
||||
},
|
||||
delete => {
|
||||
description => 'A list of settings you want to delete.',
|
||||
maxLength => 4096,
|
||||
format => 'pve-configid-list',
|
||||
optional => 1,
|
||||
type => 'string'
|
||||
},
|
||||
common => {
|
||||
maxLength => 512,
|
||||
description => 'common value',
|
||||
type => 'string',
|
||||
optional => 1
|
||||
},
|
||||
field1 => {
|
||||
description => 'Field One',
|
||||
maximum => 9,
|
||||
'instance-types' => [ 'one' ],
|
||||
'type-property' => 'type',
|
||||
minimum => 3,
|
||||
optional => 1,
|
||||
type => 'integer'
|
||||
},
|
||||
field2 => {
|
||||
'type-property' => 'type',
|
||||
oneOf => [
|
||||
{
|
||||
type => 'integer',
|
||||
minimum => 10,
|
||||
optional => 1,
|
||||
maximum => 19,
|
||||
'instance-types' => [ 'one' ],
|
||||
description => 'Field Two'
|
||||
},
|
||||
{
|
||||
description => 'Field Two but different',
|
||||
maximum => 9,
|
||||
'instance-types' => [ 'two' ],
|
||||
minimum => 3,
|
||||
optional => 1,
|
||||
type => 'integer'
|
||||
}
|
||||
]
|
||||
},
|
||||
arrayfield => {
|
||||
type => 'array',
|
||||
optional => 1,
|
||||
items => {
|
||||
description => 'a property string',
|
||||
type => 'string',
|
||||
format => {
|
||||
subfield2 => {
|
||||
type => 'integer',
|
||||
minimum => 0,
|
||||
optional => 1
|
||||
},
|
||||
subfield1 => {
|
||||
description => 'first subfield',
|
||||
type => 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
description => 'Array Field with property string'
|
||||
},
|
||||
another => {
|
||||
description => 'Another field',
|
||||
optional => 1,
|
||||
type => 'string'
|
||||
},
|
||||
}
|
||||
};
|
||||
is_deeply($update_schema, $expected_update_schema, "property-isolation update schema test");
|
||||
|
||||
done_testing();
|
||||
|
||||
1;
|
Loading…
Reference in New Issue
Block a user