public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Elias Huhsovitz" <e.huhsovitz@proxmox.com>
To: "Daniel Kral" <d.kral@proxmox.com>, <pve-devel@lists.proxmox.com>
Subject: Re: [PATCH access-control 2/2] test: api: add tests for ACL modification endpoint
Date: Thu, 23 Jul 2026 11:00:35 +0200	[thread overview]
Message-ID: <DK5TTET754VG.59P9HQBZSXYQ@proxmox.com> (raw)
In-Reply-To: <DK4CNI1DDPZT.3IP5R1XO0CIWP@proxmox.com>

Thank you for the review!
I added my thoughts down below.

On Tue Jul 21, 2026 at 5:20 PM CEST, Daniel Kral wrote:
> Thanks for adding some test cases for this!
>
> See below for some comments inline.
>
> The patch also needs a run of `make tidy`.
>
> On Mon Jul 20, 2026 at 3:45 PM CEST, Elias Huhsovitz wrote:
>> The `PUT /access/acl` endpoint previously lacked automated test
>> coverage. Introduce `api-update-acl-test.pl` to verify the ACL
>> modification logic.
>>
>> The tests cover the following scenarios:
>> - Adding, modifying, and deleting ACLs for existing users, groups, and
>>   tokens.
>> - Rejecting requests to add ACLs for non-existent entities.
>> - Enforcing privilege boundaries by rejecting calls lacking the
>>   `Permissions.Modify` privilege.
>> - Deleting ACLs for non-existent entities.
>>
>> Signed-off-by: Elias Huhsovitz <e.huhsovitz@proxmox.com>
>
> It might be nice to have these test cases precede the fixing patch so
> that the change in logic is directly visible and verifiable in the
> fixing patch through the change in the test outcomes.
>

I agree, this is much better. I will do this in the future.

>> ---
>>  src/test/api-tests.pl           |   2 +-
>>  src/test/api-update-acl-test.pl | 301 ++++++++++++++++++++++++++++++++
>>  2 files changed, 302 insertions(+), 1 deletion(-)
>>  create mode 100644 src/test/api-update-acl-test.pl
>>
>> diff --git a/src/test/api-tests.pl b/src/test/api-tests.pl
>> index a1a987a..e9c400a 100755
>> --- a/src/test/api-tests.pl
>> +++ b/src/test/api-tests.pl
>> @@ -7,6 +7,6 @@ use TAP::Harness;
>>  
>>  my $harness = TAP::Harness->new({ verbosity => -1 });
>>  
>> -my $result = $harness->runtests('api-get-permissions-test.pl');
>> +my $result = $harness->runtests('api-get-permissions-test.pl', 'api-update-acl-test.pl');
>>  
>>  exit -1 if $result->{failed};
>> diff --git a/src/test/api-update-acl-test.pl b/src/test/api-update-acl-test.pl
>> new file mode 100644
>> index 0000000..0157533
>> --- /dev/null
>> +++ b/src/test/api-update-acl-test.pl
>> @@ -0,0 +1,301 @@
>> +#!/usr/bin/env perl
>> +use v5.36;
>
> nit: missing empty newline between shebang and first use statement
>
>> +
>> +use lib qw(..);
>> +
>> +use Test::More;
>> +use Test::MockModule;
>> +
>> +use PVE::AccessControl;
>> +use PVE::RPCEnvironment;
>> +use PVE::API2::ACL;
>> +
>> +# This test suite verifies the `PUT /access/acl` API endpoint.
>> +#
>> +# It mocks the cluster filesystem and the RPC environment to isolate
>> +# the API logic from external dependencies. The tests pass mock
>> +# configuration hashes directly to the API handler and verify the
>> +# resulting state changes and error messages.
>> +#
>> +# The `get_base_cfg` function generates a clean, default configuration
>> +# hash. The `before_each` helper resets the configuration and all mock
>> +# behaviors before each test to isolate the test cases.
>> +
>> +my $current_cfg;
>> +
>> +# --- MOCKS ---
>> +
>> +my $cluster_module = Test::MockModule->new('PVE::Cluster');
>> +$cluster_module->noop('cfs_update');
>> +$cluster_module->mock('cfs_read_file', sub ($filename) {
>> +    die "unknown file '$filename'\n" if $filename ne 'user.cfg';
>> +    return $current_cfg;
>> +});
>> +$cluster_module->mock('cfs_write_file', sub ($filename, $data, $force_utf8 = undef) {
>> +    die "unknown file '$filename'\n" if $filename ne 'user.cfg';
>> +    $current_cfg = $data;
>> +});
>> +
>> +# Re-assign sub-routines to our mocked versions if imported into PVE::API2::ACL.
>> +no warnings 'redefine';
>> +*PVE::API2::ACL::cfs_read_file = \&PVE::Cluster::cfs_read_file;
>> +*PVE::API2::ACL::cfs_write_file = \&PVE::Cluster::cfs_write_file;
>
> I tend to not redefine functions here at all.
>
> This could be rewritten as:
>
> my $api_acl_module = Test::MockModule->new('PVE::API2::ACL');
>
> $api_acl_module->noop('cfs_update');
> $api_acl_module->mock(
>     'cfs_read_file' => sub($filename) { ... },
>     'cfs_write_file' => sub($filename, $data, $force_utf8 = undef) { ... },
> );
>

Thanks, this came about due to some strange error messages perl gave me,
but this is much cleaner.

>> +
>> +# Mock lock_user_config to execute the callback immediately.
>> +my $acl_module = Test::MockModule->new('PVE::AccessControl');
>> +$acl_module->mock('lock_user_config', sub ($code, @args) {
>> +    return $code->(@args);
>> +});
>> +
>> +my $rpcenv_module = Test::MockModule->new('PVE::RPCEnvironment');
>> +
>> +my $rpcenv = PVE::RPCEnvironment->init('cli');
>> +
>> +my ($handler, $handler_info) = PVE::API2::ACL->find_handler('PUT', '');
>> +
>> +# --- HELPER FUNCTIONS ---
>> +
>> +sub get_base_cfg () {
>> +    return {
>> +        users  => {
>> +            'admin@pve' => {
>> +                enable => 1,
>> +                tokens => {
>> +                    'mytoken' => { privsep => 1 },
>> +                },
>> +            },
>> +        },
>> +        groups => { 'admin_group' => {} },
>> +        roles  => {
>> +            'PVEAdmin' => { 'Permissions.Modify' => 1 },
>> +            'NoAccess' => {},
>> +        },
>> +        acl_root => {},
>> +    };
>> +}
>> +
>> +sub before_each () {
>> +    $current_cfg = get_base_cfg();
>> +
>> +    $rpcenv_module->mock('permissions', sub {
>> +        return { 'Permissions.Modify' => 1 };
>> +    });
>> +    $rpcenv_module->mock('get_user', sub {
>> +        return 'admin@pve';
>> +    });
>
> get_user doesn't need to be mocked every time, if you use
>
>     $rpcenv->set_user('admin@pve');
>

Thansk I missed this functionality.

>> +}
>> +
>> +sub run_update_acl ($params) {
>> +    my $result = eval { $handler->handle($handler_info, $params) };
>> +    return ($result, $@);
>> +}
>
> Hm, I wonder whether the test cases would be a little more compact and
> reviewable by following the usual test case structure which is
>
> - defining an array full of hashes where each hash represents a test
>   case
> - a loop then goes through each test case
>
> Each test case could then make a deep comparison between the resulting
> $current_cfg->{acl_root}, which is reset for each test case entry.
>
> As a proposal for a possible test case entry could be:
>
>     {
>         update_acl_params => { ... },
>         # can be made empty for specific test cases, etc.
>         permissions => { 'Permissions.Modify' => 1 },
>         expected_acl_root => { ... },
>     }
>
> What do you think?

I also like this kind of style.

I used the following guide as a base: https://perlmaven.com/testing
and it mentioned the subtest keyword here: https://perlmaven.com/subtest

There subtest keyword is also used in some other repositories, such as 
- pve-container/src/test/snapshot-test.pm
- qemu-server/src/test/snapshot-test.pm
- pve-container/src/test/idmap-test.pm

So i was not sure what the preferred approach currently is.

I did it this way, since I am more familiar with JUnit5 as a testing 
framework. I don't view the additional verbosity as a bad thing in
this case, since it provides a bit more description and makes it easier
to modify existing tests in the future.

I appreciate the simplicity of the approach you suggested
and can implement this style in v2.




  reply	other threads:[~2026-07-23  9:00 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-20 13:45 [PATCH access-control 0/2] fix: #6510: Allow deleting ACLs for non-existent entities & add API tests Elias Huhsovitz
2026-07-20 13:45 ` [PATCH access-control 1/2] fix #6510: api: acl: allow deleting ACLs for non-existent entities Elias Huhsovitz
2026-07-21 14:40   ` Daniel Kral
2026-07-20 13:45 ` [PATCH access-control 2/2] test: api: add tests for ACL modification endpoint Elias Huhsovitz
2026-07-21 15:20   ` Daniel Kral
2026-07-23  9:00     ` Elias Huhsovitz [this message]
2026-07-23  9:28       ` Daniel Kral
2026-07-23 11:16 ` superseded: [PATCH access-control 0/2] fix: #6510: Allow deleting ACLs for non-existent entities & add API tests Elias Huhsovitz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DK5TTET754VG.59P9HQBZSXYQ@proxmox.com \
    --to=e.huhsovitz@proxmox.com \
    --cc=d.kral@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal