Koha/t/db_dependent/api/v1/advanced_editor_macros.t
Tomas Cohen Arazi 7a4cf3569d
Bug 25502: Adapt Advanced macros routes to current guidelines
The original development started before the changes we introduced in the guidelines in late 2019, and the major code changes that took place in January 2020.

- Attribute mapping logic is now on the Koha::Object-level (the patches implement that, but are not using it)
- Related to the above, some helper methods like to_api and to_model are kept, the same for the mappings in the controller, they should all go away
- Related to the above, set_from_api and new_from_api should be used instead of using helper to_api and to_model methods in the controller
- $c->objects->search doesn't use the to_model and to_api params
- Response status codes need to be changed, at least for DELETE operations

Those are fixed by this patch.

To test:
1. Apply this patch
2. Run:
   $ kshell
  k$ prove t/db_dependent/api/v1/advanced_editor_macros.t
=> SUCCESS: Tests pass!
3. Sign off :-D

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
2020-05-19 15:21:16 +01:00

476 lines
18 KiB
Perl

#!/usr/bin/env perl
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
use Test::More tests => 5;
use Test::Mojo;
use Test::Warn;
use t::lib::TestBuilder;
use t::lib::Mocks;
use Koha::AdvancedEditorMacros;
use Koha::Database;
my $schema = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
my $t = Test::Mojo->new('Koha::REST::V1');
$schema->storage->txn_begin;
subtest 'list() tests' => sub {
plan tests => 8;
my $patron_1 = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 9 }
});
my $patron_2 = $builder->build_object({
class => 'Koha::Patrons',
});
my $password = 'thePassword123';
$patron_1->set_password({ password => $password, skip_validation => 1 });
my $userid = $patron_1->userid;
# Create test context
my $macro_1 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test1',
macro => 'delete 100',
borrowernumber => $patron_1->borrowernumber,
shared => 0,
}
});
my $macro_2 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test2',
macro => 'delete 100',
borrowernumber => $patron_1->borrowernumber,
shared => 1,
}
});
my $macro_3 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test3',
macro => 'delete 100',
borrowernumber => $patron_2->borrowernumber,
shared => 0,
}
});
my $macro_4 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test4',
macro => 'delete 100',
borrowernumber => $patron_2->borrowernumber,
shared => 1,
}
});
my $macros_index = Koha::AdvancedEditorMacros->search({ -or => { shared => 1, borrowernumber => $patron_1->borrowernumber } })->count-1;
## Authorized user tests
# Make sure we are returned with the correct amount of macros
$t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros" )
->status_is( 200, 'SWAGGER3.2.2' )
->json_has('/' . $macros_index . '/macro_id')
->json_hasnt('/' . ($macros_index + 1) . '/macro_id');
subtest 'query parameters' => sub {
plan tests => 15;
$t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?name=" . $macro_2->name)
->status_is(200)
->json_is( [ $macro_2->to_api ] );
$t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?name=" . $macro_3->name)
->status_is(200)
->json_is( [ ] );
$t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?macro_text=delete%20100")
->status_is(200)
->json_is( [ $macro_1->to_api, $macro_2->to_api, $macro_4->to_api ] );
$t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?patron_id=" . $patron_1->borrowernumber)
->status_is(200)
->json_is( [ $macro_1->to_api, $macro_2->to_api ] );
$t->get_ok("//$userid:$password@/api/v1/advanced_editor/macros?shared=1")
->status_is(200)
->json_is( [ $macro_2->to_api, $macro_4->to_api ] );
};
# Warn on unsupported query parameter
$t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros?macro_blah=blah" )
->status_is(400)
->json_is( [{ path => '/query/macro_blah', message => 'Malformed query string'}] );
};
subtest 'get() tests' => sub {
plan tests => 15;
my $patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 1 }
});
my $password = 'thePassword123';
$patron->set_password({ password => $password, skip_validation => 1 });
my $userid = $patron->userid;
my $macro_1 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
shared => 1,
}
});
my $macro_2 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
shared => 0,
}
});
my $macro_3 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
borrowernumber => $patron->borrowernumber,
shared => 0,
}
});
$t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $macro_1->id )
->status_is( 403, 'Cannot get a shared macro via regular endpoint' )
->json_is( '/error' => 'This macro is shared, you must access it via advanced_editor/macros/shared' );
$t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/shared/" . $macro_1->id )
->status_is( 200, 'Can get a shared macro via shared endpoint' )
->json_is( $macro_1->to_api );
$t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $macro_2->id )
->status_is( 403, 'Cannot access another users macro' )
->json_is( '/error' => 'You do not have permission to access this macro' );
$t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $macro_3->id )
->status_is( 200, 'Can get your own private macro' )
->json_is( $macro_3->to_api );
my $non_existent_code = $macro_1->id;
$macro_1->delete;
$t->get_ok( "//$userid:$password@/api/v1/advanced_editor/macros/" . $non_existent_code )
->status_is(404)
->json_is( '/error' => 'Macro not found' );
};
subtest 'add() tests' => sub {
plan tests => 24;
my $authorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'advanced_editor',
},
});
my $password = 'thePassword123';
$authorized_patron->set_password({ password => $password, skip_validation => 1 });
my $auth_userid = $authorized_patron->userid;
my $unauthorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
my $unauth_userid = $unauthorized_patron->userid;
my $macro = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { shared => 0 }
});
my $macro_values = $macro->to_api;
delete $macro_values->{macro_id};
$macro->delete;
# Unauthorized attempt to write
$t->post_ok( "//$unauth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
->status_is(403);
# Authorized attempt to write invalid data
my $macro_with_invalid_field = { %$macro_values };
$macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
$t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_with_invalid_field )
->status_is(400)
->json_is(
"/errors" => [
{
message => "Properties not allowed: big_mac_ro.",
path => "/body"
}
]
);
# Authorized attempt to write
$t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
->status_is( 201, 'SWAGGER3.2.1' )
->json_has( '/macro_id', 'We generated a new id' )
->json_is( '/name' => $macro_values->{name}, 'The name matches what we supplied' )
->json_is( '/macro_text' => $macro_values->{macro_text}, 'The text matches what we supplied' )
->json_is( '/patron_id' => $macro_values->{patron_id}, 'The borrower matches the borrower who submitted' )
->json_is( '/shared' => Mojo::JSON->false, 'The macro is not shared' )
->header_like( Location => qr|^\/api\/v1\/advanced_editor/macros\/d*|, 'Correct location' );
# save the library_id
my $macro_id = 999;
# Authorized attempt to create with existing id
$macro_values->{macro_id} = $macro_id;
$t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
->status_is(400)
->json_is( '/errors' => [
{
message => "Read-only.",
path => "/body/macro_id"
}
]
);
$macro_values->{shared} = Mojo::JSON->true;
delete $macro_values->{macro_id};
# Unauthorized attempt to write a shared macro on private endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
->status_is(403);
# Unauthorized attempt to write a private macro on shared endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared" => json => $macro_values )
->status_is(403);
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'create_shared_macros',
},
});
# Authorized attempt to write a shared macro on private endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros" => json => $macro_values )
->status_is(403);
# Authorized attempt to write a shared macro on shared endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared" => json => $macro_values )
->status_is(201);
};
subtest 'update() tests' => sub {
plan tests => 32;
my $authorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'advanced_editor',
},
});
my $password = 'thePassword123';
$authorized_patron->set_password({ password => $password, skip_validation => 1 });
my $auth_userid = $authorized_patron->userid;
my $unauthorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
my $unauth_userid = $unauthorized_patron->userid;
my $macro = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
});
my $macro_2 = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
});
my $macro_id = $macro->id;
my $macro_2_id = $macro_2->id;
my $macro_values = $macro->to_api;
delete $macro_values->{macro_id};
# Unauthorized attempt to update
$t->put_ok( "//$unauth_userid:$password@/api/v1/advanced_editor/macros/$macro_id"
=> json => { name => 'New unauthorized name change' } )
->status_is(403);
# Attempt partial update on a PUT
my $macro_with_missing_field = {
name => "Call it macro-roni",
};
$t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_with_missing_field )
->status_is(400)
->json_has( "/errors" =>
[ { message => "Missing property.", path => "/body/macro_text" } ]
);
my $macro_update = {
name => "Macro-update",
macro_text => "delete 100",
patron_id => $authorized_patron->borrowernumber,
shared => Mojo::JSON->false,
};
my $test = $t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_update )
->status_is(200, 'Authorized user can update a macro')
->json_is( '/macro_id' => $macro_id, 'We get the id back' )
->json_is( '/name' => $macro_update->{name}, 'We get the name back' )
->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get the text back' )
->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get the patron_id back' )
->json_is( '/shared' => $macro_update->{shared}, 'It should still not be shared' );
# Now try to make the macro shared
$macro_update->{shared} = Mojo::JSON->true;
$t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_id" => json => $macro_update )
->status_is(403, 'Cannot make your macro shared on private endpoint');
$t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_id" => json => $macro_update )
->status_is(403, 'Cannot make your macro shared without permission');
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'create_shared_macros',
},
});
$t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_update )
->status_is(403, 'Cannot make your macro shared on the private endpoint');
$t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_id" => json => $macro_update )
->status_is(200, 'Can update macro to shared with permission')
->json_is( '/macro_id' => $macro_id, 'We get back the id' )
->json_is( '/name' => $macro_update->{name}, 'We get back the name' )
->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get back the text' )
->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get back our patron id' )
->json_is( '/shared' => Mojo::JSON->true, 'It is shared' );
# Authorized attempt to write invalid data
my $macro_with_invalid_field = { %$macro_update };
$macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
$t->put_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id" => json => $macro_with_invalid_field )
->status_is(400)
->json_is(
"/errors" => [
{
message => "Properties not allowed: big_mac_ro.",
path => "/body"
}
]
);
my $non_existent_macro = $builder->build_object({class => 'Koha::AdvancedEditorMacros'});
my $non_existent_code = $non_existent_macro->id;
$non_existent_macro->delete;
$t->put_ok("//$auth_userid:$password@/api/v1/advanced_editor/macros/$non_existent_code" => json => $macro_update)
->status_is(404);
$t->put_ok("//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id" => json => $macro_update)
->status_is(403, "Cannot update other borrowers private macro");
};
subtest 'delete() tests' => sub {
plan tests => 12;
my $authorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'advanced_editor',
},
});
my $password = 'thePassword123';
$authorized_patron->set_password({ password => $password, skip_validation => 1 });
my $auth_userid = $authorized_patron->userid;
my $unauthorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
my $unauth_userid = $unauthorized_patron->userid;
my $macro = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
});
my $macro_2 = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
});
my $macro_id = $macro->id;
my $macro_2_id = $macro_2->id;
# Unauthorized attempt to delete
$t->delete_ok( "//$unauth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id")
->status_is(403, "Cannot delete macro without permission");
$t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_id")
->status_is( 204, 'Can delete macro with permission');
$t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id")
->status_is(403, 'Cannot delete other users macro with permission');
$macro_2->shared(1)->store();
$t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_2_id")
->status_is(403, 'Cannot delete other users shared macro without permission');
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'delete_shared_macros',
},
});
$t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/$macro_2_id")
->status_is(403, 'Cannot delete other users shared macro with permission on private endpoint');
$t->delete_ok( "//$auth_userid:$password@/api/v1/advanced_editor/macros/shared/$macro_2_id")
->status_is(204, 'Can delete other users shared macro with permission');
};
$schema->storage->txn_rollback;