App-Dochazka-REST

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

0.175  2014-09-25 09:29 CEST
- Resource.pm: encode response body in UTF-8 before sending it out on the line

0.176  2014-10-14 17:31 CEST
- Dispatch/: add some comments
- Resource.pm: add debug message; add allow_nonref; find bug #57 

0.177  2014-10-15 10:33 CEST
- start revamping path dispatch code in light of bug #57 (PUT request to
  non-existent resource returns HTTP code 200 and null entity body)
- realize that it makes no sense to return 404 on a PUT request, since the
  whole idea of PUT is to create a new resource
- the principal change is in Resource.pm->allowed_methods:
  - the definition of each resource (in config/dispatch) should contain list of allowed methods
  - when path is recognized, return the allowed methods from the resource definition
  - when the path is not recognized _AND_ the method is PUT, return 405 Method Not Allowed

0.178  2014-10-15 16:41 CEST
- the design change in 0.177 caused a lot of breakage: put the pieces back together (WIP)

0.179  2014-10-15 22:54 CEST

Changes  view on Meta::CPAN

  'employee/current/priv' resource
- Model/Shared.pm: add some log messages in course of debugging a problem with
  App::Dochazka::CLI

0.193  2014-10-21 08:35 CEST
- t/: fix accumulated brokenness in data model tests
- t/315-dispatch-employee-get.t: clean up comments and add a test case

0.194  2014-10-21 09:21 CEST
- Resource.pm: if method is GET and result is "No records found", return
  404 instead of 200 + status object
- t/: adapt tests to this change

0.195  2014-10-21 10:51 CEST
- Model/Employee.pm->noof_employees_by_priv: if $priv is not a valid
  privlevel, return 'OK' status + status code DISPATCH_NO_RECORDS_FOUND
  to trigger a 404 Not Found response; also, put 'count' property in the
  payload where it belongs
- t/315-dispatch-employee-get.t: adapt existing test case

0.196  2014-10-21 11:38 CEST
- config/dispatch_Top_Config.pm: make "echo" work with POST only; put
  resources in alphabetical order
- t/: remove 'echo' tests from top-level PUT and DELETE units

0.197  2014-10-21 11:47 CEST
- work on development-checklist

Changes  view on Meta::CPAN

- Util.pm: get rid of jury-rigged 'deep_copy' routine (use Storable::dclone
  instead!)

0.251  2014-11-06 09:42 CET
- Dispatch/Schedule.pm->_intervals_delete was reporting success even when
  nothing was actually deleted - fixed
- Model/Schedule.pm->schedule_all was crashing when no records found (because
  $counter was not initialized) - fixed
- t/104-employee.t: deprecate expurgate in favor of TO_JSON
- t/dispatch/intervals.t: when no schedule records are in the database,
  'schedule/all/...' will return 404 - fixed
- t/dispatch/schedule.t: add basic tests for 'schedule/all' and
  'schedule/all/disabled'

0.252  2014-11-06 16:43 CET
- Build.PL: require App::Dochazka 0.181 for new boilerplate code
- schedule_Config.pm: rename '/schedule/intervals/:sid' to 
  '/schedule/sid/:sid'
- dbinit_Config.pm: add a trigger to make immutable/unupdatable the 'schedule'
  field of the 'shedules' table 
- add a 'SQL_SCHEDULE_UPDATE' statement to support updates on the

Changes  view on Meta::CPAN

  dispatch target
- t/dispatch/interval.pm: add some missing boilerplate tests

0.290  2014-11-19 11:23 CET
- interval_Config.pm: 'interval/nick/:nick/:tsrange' resource was broken due to
  missing EOH - fixed
- t/dispatch/interval.t: $base for 'interval/nick/:nick/:tsrange' resource was
  set to wrong value - fixed

0.291  2014-11-19 15:19 CET
- HTTP_Message_en.conf, Resources.pm: add 404 status trigger
- interval_Config.pm: call the hypothetical error code DISPATCH_TOO_MANY_RECORDS_FOUND
- top_Config.pm: fix factual error in documentation 
- dispatch_Message_en.conf: add DISPATCH_TOO_MANY_RECORDS_FOUND definition 
- Dispatch/Shared.pm->history: trigger 404 when no records found
- Model/Employee.pm: return DISPATCH_NO_RECORDS_FOUND with status level 'NOTICE'
- t/dispatch/history.t: explore more possibilities for entering an invalid EID; 
  adapt to current state (status-triggered 404 errors)
- t/dispatch/interval.t: add basic tests for 'interval/self/:tsrange'

0.292  2014-11-19 16:40 CET
- dispatch/interval_Config.pm: enable _fetch_* dispatch targets
- sql/interval_Config.pm: add SQL_INTERVAL_SELECT_BY_EID_AND_TSRANGE
- Dispatch/Interval.pm: implement _fetch_* dispatch targets
- Model/Interval.pm: implement a fetch_by_eid_and_tsrange function
- Model/Shared.pm: load_multiple had no provision for binding parameters -
  fixed
- t/dispatch/interval.t: add some basic tests for

Changes  view on Meta::CPAN


0.315  2014-11-28 16:23 CET
- got Path::Router validations to work!!!
- config/dispatch/activity_Config.pm: added validations clauses
- config/dispatch/employee_Config.pm: added validations clauses
- config/dispatch_Message_en.conf: delete DISPATCH_AID_DOES_NOT_EXIST and
  DISPATCH_CODE_DOES_NOT_EXIST
- dbinit_Config.pm: change 'kosher_code' constraint on 'activities' table to
  match the validations clause
- Dispatch/Activity.pm: noticed that we were still returning custom codes (e.g.
  DISPATCH_AID_DOES_NOT_EXIST) instead of a simple 404 (405 on PUT) -- fixed
- t/dispatch/activity.t: fix tests broken by the above changes

0.316  2014-11-28 16:48 CET
- dispatch/employee_Config.pm: add validations clause to the
  'employee/count/:priv' resource definition
- Dispatch/Employee.pm: handle upper/mixed case priv strings gracefully
- Model/Employee.pm: noof_employees_by_priv was written long ago - update it to
  use best practices
- t/dispatch/employee.t: add 'employee/count/:priv' test cases

Changes  view on Meta::CPAN

- Test.pm: include location header in return status (a-la Web::MREST)
- t/dispatch/top.t: migrate unit

0.383  2015-02-11 16:43 CET
- migrate Employee resources (WIP)

0.384  2015-02-12 07:31 CET
- support 'employee/search/nick/:key'

0.385  2015-02-12 09:22 CET
- Dispatch.pm: make 404 explanation in 'handler_get_employee_search_nick' more
  descriptive
- move ACL.pm up one level in the directory structure, since the Dispatch/
  directory will soon be gone

0.386  2015-02-12 09:44 CET
- Dispatch.pm: in 'GET employee/search/nick/:key', add '%' characters to :key
  implicitly if none are provided (and note this in resource documentation)
- Model/Shared.pm: in load_multiple, mention search keys in the result

0.387  2015-02-12 10:13 CET
- Dispatch.pm: make 404 explanation more descriptive
- t/dispatch/employee.t: get unit running cleanly

0.388  2015-02-12 11:22 CET
- move Dispatch/Shared.pm up one directory level
- convert insert_employee and update_employee into ordinary functions and move
  them to Shared.pm - to be manageable, Dispatch.pm should contain resource
  handlers only

0.389  2015-02-12 14:40 CET
- Dispatch.pm, Shared.pm: migrating shared (non-handler) routines to Shared.pm

Changes  view on Meta::CPAN

- Docs/Resource.pm: re-generate POD by latest docgen.plx

0.402  2015-02-17 18:30 CET
- t/model/schedule.t: fix brokenness previously hidden by a stale module file
  in /usr/lib/perl5/site_perl

0.403  2015-02-18 11:59 CET
- use normalize_filespec to avoid $ENV{'HOME'}, which does not work on Windows
  systems

0.404  2015-02-18 17:59 CET
- dispatch_Messages_en.conf, Auth.pm: define and use DISPATCH_ACL_CHECK_FAILED message
- Dispatch.pm: remove extraneous 'my'

0.405  2015-02-20 15:24 CET
- ACL.pm: add 'acl_check_is_me' - generalized check for handlers that have
  acl_profile "passerby" but may only used to operate on one's own records
- ResourceDefs.pm, Dispatch.pm: use 'acl_check_is_me' in several handlers

0.406  2015-02-20 20:55 CET
- config/sql: add SQL_EMPLOYEE_SELECT_BY_SEC_ID

Changes  view on Meta::CPAN

- ResourceDefs.pm: change ACL of 'GET schedule/all' so inactives and actives
  can do it, too
- t/dispatch/schedule.t: add appropriate test cases

0.420  2015-04-04 21:18 CEST
- SQL: implement 'not_before_1892' function to enforce sane timestamp policy
  (no dates before 1892 in the database)
- t/: adapt tests; add test cases

0.421  2015-04-05 13:34 CEST
- REST/Shared.pm: in 'shared_get_privsched', return 404 if no priv/schedule
  assigned (instead of returning 200 OK with a null priv/schedule) 
- t/dispatch/schedule.t: adapt tests

0.422  2015-04-05 20:45 CEST
- dbinit_Config.pm: make schedule_at_timestamp return SID instead of the
  schedule JSON itself
- Model/Shared.pm: schedule_by_eid returns SID
- REST/Shared.pm: shared_get_privsched looks up the SID and returns the
  entire schedule
- t/dispatch/schedule.t: adapt tests affected by the above change

Changes  view on Meta::CPAN

- LDAP.pm: fix populate_employee() so it returns not_ok if nick is not in LDAP
- Dispatch.pm: implement handler_get_employee_ldap and handler_put_employee_ldap
- Auth.pm: fix DOCHAZKA_LDAP_AUTOCREATE_AS code path

0.472  2015-07-24 17:06 CEST
- doc: config/REST_Config.pm: tell users to not add a nick property to
  DOCHAZKA_LDAP_POPULATE_MATRIX
- t/: test POST employee/nick without required nick property
- Dispatch.pm: return 400 if POST employee/nick without nick property
  (fixes github issue #2) 
- Dispatch.pm: handler_put_employee_ldap() return 404 if nick not found in LDAP

0.473  2015-07-25 03:45 CEST
- REST_SiteConfig.pm: add config file for overriding Web::MREST core params
  since those are set first
- REST.pm: add a version() package method
- GET version now reports App::Dochazka::REST version instead of Web::MREST
  version (Github issue #3)
- t/sql/disabled_to_zero.t: fix issue where test was not cleaning up after itself
- Dispatch.pm: write a debug message if LDAP is enabled
- MANIFEST: refrain from packaging release script

config/dispatch_Message_en.conf  view on Meta::CPAN


DISPATCH_DATE_LIST_OR_TSRANGE
Request entity must contain either date_list or tsrange property (not both, not neither)

DOCHAZKA_MALFORMED_400
Status trigger for HTTP code 400 ("Malformed")

DOCHAZKA_FORBIDDEN_403
Status trigger for HTTP code 403 ("Forbidden")

DOCHAZKA_NOT_FOUND_404
Status trigger for HTTP code 404 ("Not Found")

DOCHAZKA_SESSION_TERMINATED
REST session terminated

DISPATCH_SCHEDULED_INTERVALS_CREATED
%s intervals created according to schedule

DISPATCH_SCHEDULED_NO_INTERVALS_CREATED
No intervals created

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN


    # the resource to be documented should be in the request body - if not, return 400
    my $docu_resource = $self->context->{'request_entity'};
    if ( $docu_resource ) {
        $log->debug( "handler_docu: request body is ->$docu_resource<-" );
    } else {
        $self->mrest_declare_status( 'code' => 400, 'explanation' => 'Missing request entity' );
        return $fail;
    }

    # the resource should be defined - if not, return 404
    my $def = $resources->{$docu_resource};
    $log->debug( "handler_docu: resource definition is " . Dumper( $def ) );
    if ( ref( $def ) ne 'HASH' ) {
        $self->mrest_declare_status( 'code' => 404, 
            'explanation' => "Could not find resource definition for $docu_resource" 
        );
        return $fail;
    }

    # all green - assemble the requested documentation
    my $method = $self->context->{'method'};
    my $resource_name = $self->context->{'resource_name'};
    my $pl = {
        'resource' => $docu_resource,

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

    my ( $self, $emp ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::_ldap_sync_pass1" ); 

    my $status = $emp->ldap_sync();
    $log->debug( "ldap_sync status: " . Dumper( $status ) );
    if ( $status->not_ok ) {
        if ( $status->code eq 'DOCHAZKA_LDAP_SYSTEM_USER_NOSYNC' ) {
            # system user - 403
            $status->{'http_code'} = 403;
        } else {
            $status->{'http_code'} = 404;
        }
        $self->mrest_declare_status( $status );
        return 0;
    }
    $self->context->{'stashed_employee_object'} = $emp;
    return 1;
}


=head3 handler_get_employee_ldap

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

    # second pass
    my $key = $self->context->{'mapping'}->{'key'};
    $key = "%$key%" unless $key =~ m/%/;
    my $status = $CELL->status_ok;
    $status = load_multiple(
        conn => $self->context->{'dbix_conn'},
        class => 'App::Dochazka::REST::Model::Employee',
        sql => $site->SQL_EMPLOYEE_SELECT_MULTIPLE_BY_NICK,
        keys => [ $key ],
    );
    # check for 404
    if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
        $self->mrest_declare_status( code => 404,
            explanation => "DISPATCH_SEARCH_EMPTY",
            args => [ 'employee', "nick LIKE $key" ],
        );
        return $fail;
    }
    return $status if $status->not_ok;

    # found some employee objects
    foreach my $emp ( @{ $status->payload } ) {
        $emp = $emp->TO_JSON;

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

            $emp->eid,
            $ts
        );
    } else {
        die "BGUDFUUFF! Improper prop ->$prop<- seen!";
    }
    # - process return value
    if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
        my $tsmsg = ( $ts ) ? $ts : 'now';
        $self->mrest_declare_status(
            code => 404,
            explanation => "No $prop history for $key $value as of $tsmsg",
        );
        return $fail;
    } elsif ( $status->not_ok ) {
        $self->mrest_declare_status(
            code => 500,
            explanation => $status->text,
        );
        return $fail;
    }

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

    my $emp = $self->context->{'stashed_employee_obj'};
    my $status = App::Dochazka::REST::Model::Shared::get_history( 
        $prop,
        $context->{'dbix_conn'},
        eid => $emp->eid,
        nick => $emp->nick, 
        tsrange => $tsrange, 
    );
    # - process return value
    if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
        $self->mrest_declare_status( code => 404, explanation => "No history for $key $value $tsrange" );
        return $fail;
    } elsif ( $status->not_ok ) {
        $self->mrest_declare_status( code => 500, explanation => $status->text );
        return $fail;
    }
    return $status;
}


=head3 handler_history_post

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

        $key = 'eid';
        $value = $entity->{eid};
    } elsif ( $entity->{nick} ) {
        $key = 'nick';
        $value = $entity->{nick};
    } elsif ( $entity->{sec_id} ) {
        $key = 'sec_id';
        $value = $entity->{sec_id};
    } else {
        $self->mrest_declare_status(
            code => 404,
            explanation => "DISPATCH_EMPLOYEE_CANNOT_BE_DETERMINED"
        );
        return;
    }
    map { delete $entity->{$_} } ( 'eid', 'nick', 'sec_id' );
    my $emp = shared_first_pass_lookup( $self, $key, $value );
    return unless $emp->isa( 'App::Dochazka::REST::Model::Employee' );
    return $emp;
}

lib/App/Dochazka/REST/Guide.pm  view on Meta::CPAN

full range of functionality may be available only to administrators. See These
operations are special cases. Their ACL profile is either 'inactive' or
'active', but a non-administrator employee may still get a 403 Forbidden error
on the operation if they are trying to do something, such as update an interval
belonging to a different employee, that is reserved for administrators.

=item * B<Test for resource existence>

The next test a request undergoes on its quest to become a response is the
test of resource existence. If the request is asking for a non-existent resource,
e.g. L<http://dochazka.site/employee/curent>, it cannot be fulfilled and a "404
Not Found" response will be sent.

For GET requests, this is ordinarily the last cog in the state machine: if the
test passes, a "200 OK" response is typically sent, along with a response body.
(There are exceptions to this rule, however - see L<the AUTHORIZATION
chapter|"AUTHORIZATION">.) Requests using other methods (POST, PUT, DELETE) are
subject to further processing as described below.

=back

lib/App/Dochazka/REST/Guide.pm  view on Meta::CPAN

C<PUT> and C<POST> requests may take a request body. If a request body is
expected or accepted, it must be a valid JSON string. (JSON is a simple way of
"stringifying" a data structure.)

=head2 HTTP response

The HTTP response returned by the REST server consists of:

=over

=item * Status code (e.g. 200, 400, 404, etc.)

=item * Headers

=item * Content body (or "response entity")

=back

=head3 Status

The HTTP standard stipulates a number of status codes. The server listens for

lib/App/Dochazka/REST/Guide.pm  view on Meta::CPAN

=item C<200> (OK)

The request was accepted and processed. Refer to the response body for the
result.

=item C<204> ()

This code is returned on C<DELETE> requests when either the record was
successfully deleted or the resource did not exist in the first place.

=item C<404> (Not Found)

The resource specification given in the URI could not be associated with a
known resource.

=item C<405> (Method Not Allowed)

The resource was recognized but it is not defined for this method.

=item C<401> (Not authorized)

lib/App/Dochazka/REST/Model/Employee.pm  view on Meta::CPAN

    $priv = lc $priv;

    if ( $priv eq 'total' ) {
        my $count = noof( $conn, 'employees' );
        return $CELL->status_ok( 
            'DISPATCH_COUNT_EMPLOYEES', 
            args => [ $count, $priv ], 
            payload => { count => $count } );
    }

    return $CELL->status_err( 'DOCHAZKA_NOT_FOUND_404' ) unless 
        $priv =~ m/^(passerby)|(inactive)|(active)|(admin)$/i;

    my $sql = $site->SQL_EMPLOYEE_COUNT_BY_PRIV_LEVEL;
    my ( $count ) = @{ select_single( conn => $conn, sql => $sql, keys => [ $priv ] )->payload };
    $log->debug( "select_single returned: $count" );
    $count += 0;
    $CELL->status_ok( 'DISPATCH_COUNT_EMPLOYEES', args => [ $count, $priv ], 
        payload => { 'priv' => $priv, 'count' => $count } );
}

lib/App/Dochazka/REST/Shared.pm  view on Meta::CPAN

        $thing = 'schedule';
        $status = App::Dochazka::REST::Model::Schedule->load_by_sid( $conn, $value );
    } elsif ( $key eq 'scode' ) {
        $thing = 'schedule';
        $status = App::Dochazka::REST::Model::Schedule->load_by_scode( $conn, $value );
    } else {
        die "shared_first_pass_lookup could not do anything with key $key!";
    }

    if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
        $d_obj->mrest_declare_status( code => 404,
            explanation => 'DISPATCH_SEARCH_EMPTY',
            args => [ $thing, "$key equals $value" ],
        );
        return;
    }
    if ( $status->not_ok ) {
        $d_obj->mrest_declare_status( code => 500, explanation => $status->code,
            args => $status->args 
        );
        return;

lib/App/Dochazka/REST/Shared.pm  view on Meta::CPAN

    #
    if (
            ! acl_check_is_my_report( $d_obj, ( lc $key ) => $value ) and
            ! acl_check_is_me( $d_obj, ( lc $key ) => $value )
       )
    {
        $d_obj->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
        return 0;
    }
    #
    # 404 check
    #
    my $emp = shared_first_pass_lookup( $d_obj, $key, $value );
    return 0 unless $emp;
    $d_obj->context->{'stashed_employee_object'} = $emp;
    return 1;
}


=head2 shared_get_employee

lib/App/Dochazka/REST/Shared.pm  view on Meta::CPAN

    # first pass
    if ( $pass == 1 ) {
        #
        # 403 (ACL) check - passerby can only look up him- or herself
        #
        if ( ! acl_check_is_me( $d_obj, ( lc $key ) => $value ) ) {
            $d_obj->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
            return 0;
        }
        # 
        # 404 check
        #
        my $emp = shared_first_pass_lookup( $d_obj, $key => $value );
        return 0 unless $emp;
        $d_obj->context->{'stashed_employee_object'} = $emp;
        return 1;
    }
    
    # second pass

    # - initialization

lib/App/Dochazka/REST/Shared.pm  view on Meta::CPAN

        if ( $return_value and $t eq 'schedule' ) {
            # $return_value is SID of the schedule, but we want the schedule itself
            my $status = App::Dochazka::REST::Model::Schedule->load_by_sid( $conn, $return_value );
            $return_value = $status->payload;
        }

        my @privsched = ( $t, $return_value );
        if ( $ts ) {
            if ( ! $return_value ) {
                $d_obj->mrest_declare_status( 
                    code => 404, 
                    explanation => "Employee $nick (EID $eid) has no $t assigned as of $ts" 
                );
                return $CELL->status_not_ok;
            }
            my $code;
            if ( 'PRIV' eq uc( $t ) ) {
                $code = 'DISPATCH_EMPLOYEE_PRIV_AS_AT';
            } elsif ( 'SCHEDULE' eq uc( $t ) ) {
                $code = 'DISPATCH_EMPLOYEE_SCHEDULE_AS_AT';
            } else {

lib/App/Dochazka/REST/Shared.pm  view on Meta::CPAN

                payload => {
                    eid => $eid += 0,  # "numify"
                    nick => $emp->nick,
                    timestamp => $ts,
                    @privsched,
                },
            );
        } else {
            if ( ! $return_value ) {
                $d_obj->mrest_declare_status( 
                    code => 404, 
                    explanation => "Employee $nick (EID $eid) has no $t assigned"
                );
                return $CELL->status_not_ok;
            }
            my $code;
            if ( 'PRIV' eq uc( $t ) ) {
                $code = 'DISPATCH_EMPLOYEE_PRIV';
            } elsif ( 'SCHEDULE' eq uc( $t ) ) {
                $code = 'DISPATCH_EMPLOYEE_SCHEDULE';
            } else {

t/dispatch/activity.t  view on Meta::CPAN

ok( defined $status->payload );
is( $status->payload->{'remark'}, 'puppy', "POST $base 6" );
is( $status->payload->{'long_desc'}, 'wop wop ng', "POST $base 7" );

note( 'non-existent AID and also out of range' );
$activity_obj = '{ "aid" : 3434342342342, "long_desc" : 3434341, "remark" : 34334342 }';
dbi_err( $test, 500, 'root', 'POST', $base, $activity_obj, qr/out of range for type integer/ );

note( 'non-existent AID' );
$activity_obj = '{ "aid" : 342342342, "long_desc" : 3434341, "remark" : 34334342 }';
req( $test, 404, 'root', 'POST', $base, $activity_obj );

note( 'throw a couple curve balls' );
my $weirded_object = '{ "copious_turds" : 555, "long_desc" : "wang wang wazoo", "disabled" : "f" }';
req( $test, 400, 'root', 'POST', $base, $weirded_object );

my $no_closing_bracket = '{ "copious_turds" : 555, "long_desc" : "wang wang wazoo", "disabled" : "f"';
req( $test, 400, 'root', 'POST', $base, $no_closing_bracket );

$weirded_object = '{ "aid" : "!!!!!", "long_desc" : "down it goes" }';
dbi_err( $test, 500, 'root', 'POST', $base, $weirded_object, qr/invalid input syntax for type integer/ );

t/dispatch/activity.t  view on Meta::CPAN

    code => 'WORK',
    long_desc => 'Work',
    remark => 'dbinit',
    disabled => 0,
}, "GET $base/:aid 4" );

note( "fail invalid (non-integer) AID" );
req( $test, 400, 'active', 'GET', "$base/jj" );

note( "fail non-existent AID" );
req( $test, 404, 'active', 'GET', "$base/444" );

note( "succeed disabled AID" );
$status = req( $test, 200, 'active', 'GET', "$base/$aid_of_foobar" );
is( $status->level, 'OK', "GET $base/:aid 13" );
is( $status->code, 'DISPATCH_ACTIVITY_FOUND', "GET $base/:aid 14" );
is_deeply( $status->payload, {
    aid => $aid_of_foobar,
    code => 'FOOBAR',
    long_desc => undef,
    remark => undef,

t/dispatch/activity.t  view on Meta::CPAN

note( 'active fail 403' );
req( $test, 403, 'active', 'DELETE', "$base/1" );

note( 'root success' );
note( "DELETE $base/$aid_of_foobar" );
$status = req( $test, 200, 'root', 'DELETE', "$base/$aid_of_foobar" );
is( $status->level, 'OK', "DELETE $base/:aid 3" );
is( $status->code, 'DOCHAZKA_CUD_OK', "DELETE $base/:aid 4" );

note( 'really gone' );
req( $test, 404, 'active', 'GET', "$base/$aid_of_foobar" );

note( 'root fail invalid AID' );
req( $test, 400, 'root', 'DELETE', "$base/asd" );


note( '=============================' );
note( '"activity/all" resource' );
note( '=============================' );
$base = 'activity/all';
docu_check($test, $base);

t/dispatch/activity.t  view on Meta::CPAN

is( $status->code, 'DISPATCH_ACTIVITY_FOUND', "GET $base/:code 6" );
is_deeply( $status->payload, {
    aid => $aid_of_foobar,
    code => 'FOOBAR',
    long_desc => undef,
    remark => 'bazblat',
    disabled => 0,
}, "GET $base/:code 7" );

note( 'non-existent code' );
req( $test, 404, 'root', 'GET', "$base/jj" );

note( 'invalid code' );
foreach my $invalid_code ( 
    '!!!! !134@@',
    'whiner*44',
    '@=1337',
    '/ninety/nine/luftbalons//',
) {
    foreach my $user ( qw( root demo ) ) {
        req( $test, 400, $user, 'GET', "$base/$invalid_code" );

t/dispatch/activity.t  view on Meta::CPAN

note( "POST on $base/:code" );
req( $test, 405, 'demo', 'POST', "$base/WORK" );
req( $test, 405, 'active', 'POST', "$base/WORK" );
req( $test, 405, 'root', 'POST', "$base/WORK" );

note( "DELETE on $base/:code" );

note( 'demo fail 403 once' );
req( $test, 403, 'demo', 'DELETE', "$base/FOOBAR1" );

note( 'root fail 404' );
req( $test, 404, 'root', 'DELETE', "$base/FOOBAR1" );

note( 'demo fail 403 a second time' );
req( $test, 403, 'demo', 'DELETE', "$base/FOOBAR" );

note( "root success: DELETE $base/FOOBAR" );
$status = req( $test, 200, 'root', 'DELETE', "$base/FOOBAR" );
is( $status->level, 'OK', "DELETE $base/FOOBAR 3" );
is( $status->code, 'DOCHAZKA_CUD_OK', "DELETE $base/FOOBAR 4" );

note( "really gone" );
req( $test, 404, 'root', 'GET', "$base/FOOBAR" );

note( "root: fail invalid code" );
req( $test, 400, 'root', 'DELETE', "$base/!!!" );

note( "delete FOOBARPUS, too" );
$status = req( $test, 200, 'root', 'DELETE', "$base/foobarpus" );
is( $status->level, 'OK', "DELETE $base/foobarpus 2" );
is( $status->code, 'DOCHAZKA_CUD_OK', "DELETE $base/foobarpus 3" );

note( "teardown" );

t/dispatch/component.t  view on Meta::CPAN

ok( -o File::Spec->catfile( $comp_root, $status->payload->{path} ) );
is( $full_path_of_foowop, File::Spec->catfile( $comp_root, $status->payload->{path} ) );
is( "wop wop ng", read_file( $full_path_of_foowop ) );

note( 'non-existent cid and also out of range' );
$component_obj = '{ "cid" : 3434342342342, "source" : 3434341, "acl" : "passerby" }';
dbi_err( $test, 500, 'root', 'POST', $base, $component_obj, qr/out of range for type integer/ );

note( 'non-existent cid' );
$component_obj = '{ "cid" : 342342342, "source" : 3434341, "acl" : "passerby" }';
req( $test, 404, 'root', 'POST', $base, $component_obj );

note( 'throw a couple curve balls' );
my $weirded_object = '{ "copious_turds" : 555, "source" : "wang wang wazoo", "acl" : "passerby" }';
req( $test, 400, 'root', 'POST', $base, $weirded_object );

my $no_closing_bracket = '{ "copious_turds" : 555, "source" : "wang wang wazoo", "acl" : "passerby"';
req( $test, 400, 'root', 'POST', $base, $no_closing_bracket );

$weirded_object = '{ "cid" : "!!!!!", "source" : "down it goes" }';
dbi_err( $test, 500, 'root', 'POST', $base, $weirded_object, qr/invalid input syntax for type integer/ );

t/dispatch/component.t  view on Meta::CPAN

    path => 'FOOBAR',
    source => 'wombat',
    acl => 'passerby',
    validations => undef,
}, "GET $base/:cid 4" );

note( "fail invalid (non-integer) cid" );
req( $test, 400, 'root', 'GET', "$base/jj" );

note( "fail non-existent cid" );
req( $test, 404, 'root', 'GET', "$base/444" );

note( "PUT on $base/:cid" );
$component_obj = '{ "path" : "FOOBAR", "source" : "The bar of foo", "acl" : "inactive" }';
# - test with demo fail 403
req( $test, 403, 'demo', 'PUT', "$base/$cid_of_foobar", $component_obj );

note( 'test with root (successful update)' );
$status = req( $test, 200, 'root', 'PUT', "$base/$cid_of_foobar", $component_obj );
is( $status->level, 'OK', "PUT $base/:cid 3" );
is( $status->code, 'DOCHAZKA_CUD_OK', "PUT $base/:cid 4" );

t/dispatch/component.t  view on Meta::CPAN

req( $test, 403, 'demo', 'DELETE', "$base/$cid_of_foobar" );

note( 'root success' );
note( "DELETE $base/$cid_of_foobar" );
ok( -e $full_path_of_foobar );
$status = req( $test, 200, 'root', 'DELETE', "$base/$cid_of_foobar" );
is( $status->level, 'OK', "DELETE $base/:cid 3" );
is( $status->code, 'DOCHAZKA_CUD_OK', "DELETE $base/:cid 4" );

note( 'really gone' );
req( $test, 404, 'root', 'GET', "$base/$cid_of_foobar" );
ok( ! -e $full_path_of_foobar );

note( 'root fail invalid cid' );
req( $test, 400, 'root', 'DELETE', "$base/asd" );


note( "=============================" );
note( "'component/path' resource" );
note( "=============================" );
$base = 'component/path';

t/dispatch/employee.t  view on Meta::CPAN

is( $mrsfu->nick, $mrsfuprime->nick, 'POST employee/eid 10' );
is( $mrsfu->fullname, $mrsfuprime->fullname, 'POST employee/eid 10' );
is( $mrsfu->email, $mrsfuprime->email, 'POST employee/eid 10' );
is( $mrsfu->remark, $mrsfuprime->remark, 'POST employee/eid 10' );

note( "attempt as demo and root to update Mr./Mrs. Fu to a non-existent EID" );
#diag("--- POST employee/eid (non-existent EID)");
req( $test, 403, 'demo', 'POST', $base, '{ "eid" : 5442' );
req( $test, 400, 'root', 'POST', $base, '{ "eid" : 5442' );
req( $test, 403, 'demo', 'POST', $base, '{ "eid" : 5442 }' );
req( $test, 404, 'root', 'POST', $base, '{ "eid" : 5442 }' );
req( $test, 404, 'root', 'POST', $base, '{ "eid": 534, "nick": "mrfu", "fullname":"Lizard Scale" }' );

note( 'missing EID' );
req( $test, 400, 'root', 'POST', $base, '{ "long-john": "silber" }' );

note( 'incorrigibly attempt to update totally bogus and invalid EIDs' );
req( $test, 400, 'root', 'POST', $base, '{ "eid" : }' );
req( $test, 400, 'root', 'POST', $base, '{ "eid" : jj }' );
$status = req( $test, 500, 'root', 'POST', $base, '{ "eid" : "jj" }' );
like( $status->text, qr/invalid input syntax for type integer/ );

t/dispatch/employee.t  view on Meta::CPAN

    ok( exists $status->payload->{'fullname'} );
    is( $status->payload->{'fullname'}, $params->[3] );
}

note( "GET $base/2 as demo" );
req( $test, 200, 'demo', 'GET', "$base/2" );
is( $status->level, 'OK' );
is( $status->code, 'DISPATCH_EMPLOYEE_FOUND' );

note( "GET $base/53432 as root" );
req( $test, 404, 'root', 'GET', "$base/53432" );

note( "GET $base/53432 as demo" );
req( $test, 403, 'demo', 'GET', "$base/53432" );

note( 'invalid EIDs caught by Path::Router validations clause' );
foreach my $eid ( @invalid_eids ) {
    foreach my $user ( qw( root demo ) ) {
        req( $test, 400, $user, 'GET', "$base/$eid" );
    }
}

t/dispatch/employee.t  view on Meta::CPAN

is( $mrfu->nick, 'mrfu' );
my $mrfuprime = App::Dochazka::REST::Model::Employee->spawn( eid => $eid_of_brchen,
    nick => 'mrfu', fullname => 'Lizard Scale', email => 'mrfu@dragon.cn',
    salt => 'tasty', sync => 0 );
is_deeply( $mrfu, $mrfuprime );
$eid_of_mrfu = $mrfu->eid;
is( $eid_of_mrfu, $eid_of_brchen );

note( 'provide non-existent EID' );
req( $test, 403, 'demo', 'PUT', "$base/5633", '{' );
req( $test, 404, 'root', 'PUT', "$base/5633", '{' );
req( $test, 403, 'demo', 'PUT', "$base/5633",
    '{ "nick": "mrfu", "fullname":"Lizard Scale" }' );
req( $test, 404, 'root', 'PUT', "$base/5633",
    '{ "eid": 534, "nick": "mrfu", "fullname":"Lizard Scale" }' );

note( 'with valid JSON that is not what we are expecting' );
req( $test, 400, 'root', 'PUT', "$base/2", 0 );

note( 'another kind of bogus JSON' );
$status = req( $test, 200, 'root', 'PUT', "$base/2", '{ "legal" : "json" }' );
is( $status->level, 'OK' );
is( $status->code, 'DISPATCH_UPDATE_NO_CHANGE_OK' ); 

t/dispatch/employee.t  view on Meta::CPAN

note( 'employee/eid/:eid - delete cannonfodder' );
req( $test, 403, 'demo', 'DELETE', "$base/$eid_of_cf" );
req( $test, 403, 'active', 'DELETE', "$base/$eid_of_cf" ); 
req( $test, 401, 'unknown', 'DELETE', "$base/$eid_of_cf" ); # 401 because 'unknown' doesn't exist
$status = req( $test, 200, 'root', 'DELETE', "$base/$eid_of_cf" );
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );

note( 'attempt to get cannonfodder - not there anymore' );
req( $test, 403, 'demo', 'GET', "$base/$eid_of_cf" );
req( $test, 404, 'root', 'GET', "$base/$eid_of_cf" );

note( 'create another "cannon fodder" employee' );
$cf = create_bare_employee( { nick => 'cannonfodder' } );
ok( $cf->eid > $eid_of_cf ); # EID will have incremented
$eid_of_cf = $cf->eid;

note( 'delete the sucker' );
req( $test, 403, 'demo', 'DELETE', '/employee/nick/cannonfodder' );
$status = req( $test, 200, 'root', 'DELETE', '/employee/nick/cannonfodder' );
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );

note( 'attempt to get cannonfodder - not there anymore' );
req( $test, 403, 'demo', 'GET',  "$base/$eid_of_cf" );
req( $test, 404, 'root', 'GET',  "$base/$eid_of_cf" );

note( 'attempt to delete "root the immutable" (won\'t work)' );
dbi_err( $test, 500, 'root', 'DELETE', "$base/1", undef, qr/immutable/i );

note( 'invalid EIDs caught by Path::Router validations clause' );
foreach my $eid ( @invalid_eids ) {
    foreach my $user ( qw( root demo ) ) {
        req( $test, 400, $user, 'GET', "$base/$eid" );
    }
}


note( '=============================' );
note( '"employee/eid/:eid/minimal" resource' );
note( '=============================' );
$base = 'employee/eid';
docu_check($test, "$base/:eid/minimal");

note( 'root attempt to get non-existent EID (minimal)' );
req( $test, 404, 'root', 'GET', "$base/53432/minimal" );

note( 'demo attempt to get non-existent EID (minimal)' );
req( $test, 403, 'demo', 'GET', "$base/53432/minimal" );

note( 'demo attempt to get existent EID (minimal)' );
note( 'DOCHAZKA_EMPLOYEE_MINIMAL_FIELDS is ' . Dumper( $site->DOCHAZKA_EMPLOYEE_MINIMAL_FIELDS ) );
req( $test, 403, 'demo', 'GET', "$base/" . $site->DOCHAZKA_EID_OF_ROOT . "/minimal" );

note( 'root get active (minimal)' );
$status = req( $test, 200, 'root', 'GET', "$base/$ts_eid_active/minimal" );

t/dispatch/employee.t  view on Meta::CPAN

is( $status->payload->{'nick'}, 'demo' );
ok( exists $status->payload->{'fullname'} );
is( $status->payload->{'fullname'}, 'Demo Employee' );

note( "GET $base/demo" );
req( $test, 200, 'demo', 'GET', "$base/demo" );
is( $status->level, "OK" );
is( $status->code, 'DISPATCH_EMPLOYEE_FOUND' );

note( "GET $base/nick/{various bogus nicks}" );
req( $test, 404, 'root', 'GET', "$base/53432" );
req( $test, 403, 'demo', 'GET', "$base/53432" );
req( $test, 404, 'root', 'GET', "$base/heathledger" );

# this one triggers "wide character in print" warnings
#req( $test, 404, 'root', 'GET', "$base/" . uri_escape_utf8('/employee/nick//////áěěoěščqwšáščšýš..-...-...-..-.00') );

note( 'single-character nicks' );
$status = req( $test, 404, 'root', 'GET', "$base/4" );


note( "PUT employee/nick/:nick" );

note( 'demo cannot PUT no matter what' );
req( $test, 403, 'demo', 'PUT', "$base/mrsfu", '{' );

note( 'root can PUT, but JSON entity is invalid' );
req( $test, 400, 'root', 'PUT', "$base/mrsfu", '{' );

t/dispatch/employee.t  view on Meta::CPAN

is( $status->level, 'OK' );
is( $status->code, 'DISPATCH_EMPLOYEE_FOUND' );

note( 'DELETE "employee/nick/:nick" with nick cannonfodder' );
req( $test, 403, 'demo', 'DELETE', $base . "/" . $cf->nick );
$status = req( $test, 200, 'root', 'DELETE', $base . "/" . $cf->nick );
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );

note( 'attempt to get cannonfodder - not there anymore' );
req( $test, 404, 'root', 'GET', "$base/cannonfodder" );

note( 'attempt to get in a different way' );
$status = App::Dochazka::REST::Model::Employee->load_by_nick( $dbix_conn, 'cannonfodder' );
is( $status->level, 'NOTICE' );
is( $status->code, 'DISPATCH_NO_RECORDS_FOUND' );

note( 'create another "cannon fodder" employee' );
$cf = create_bare_employee( { nick => 'cannonfodder' } );
ok( $cf->eid > $eid_of_cf ); # EID will have incremented
$eid_of_cf = $cf->eid;

note( 'get cannonfodder - again, no problem' );
$status = req( $test, 200, 'root', 'GET', "$base/cannonfodder" );
is( $status->level, 'OK' );
is( $status->code, 'DISPATCH_EMPLOYEE_FOUND' );

note( 'delete with a typo (non-existent nick)' );
req( $test, 403, 'demo', 'DELETE', "$base/cannonfoddertypo" );
req( $test, 404, 'root', 'DELETE', "$base/cannonfoddertypo" );

note( 'attempt to get cannonfodder - still there' );
$status = req( $test, 200, 'root', 'GET', "$base/cannonfodder" );
is( $status->level, 'OK' );
is( $status->code, 'DISPATCH_EMPLOYEE_FOUND' );

note( 'tear down testing employee' );
delete_bare_employee( $eid_of_cf );

note( 'attempt to delete \'root the immutable\' (won\'t work)' );
dbi_err( $test, 500, 'root', 'DELETE', "$base/root", undef, qr/immutable/i );


note( '=============================' );
note( '"employee/nick/:nick/minimal" resource' );
note( '=============================' );
$base = 'employee/nick';
docu_check($test, "$base/:nick/minimal");

note( 'root attempt to get non-existent nick (minimal)' );
req( $test, 404, 'root', 'GET', "$base/53432/minimal" );

note( 'demo attempt to get non-existent nick (minimal)' );
req( $test, 403, 'demo', 'GET', "$base/53432/minimal" );

note( 'demo attempt to get existent nick (minimal)' );
req( $test, 403, 'demo', 'GET', "$base/root/minimal" );

note( 'root get active (minimal)' );
$status = req( $test, 200, 'root', 'GET', "$base/active/minimal" );
is( $status->level, 'OK' );

t/dispatch/employee.t  view on Meta::CPAN

is_deeply( $status->payload, $mustr );


note( '=============================' );
note( '"employee/sec_id/:sec_id/minimal" resource' );
note( '=============================' );
$base = 'employee/sec_id';
docu_check($test, "$base/:sec_id/minimal");

note( 'root attempt to get non-existent sec_id (minimal)' );
req( $test, 404, 'root', 'GET', "$base/53432/minimal" );

note( 'demo attempt to get non-existent sec_id (minimal)' );
req( $test, 403, 'demo', 'GET', "$base/53432/minimal" );

note( 'set root\'s sec_id to be foobar' );
my $eid_of_root = $site->DOCHAZKA_EID_OF_ROOT;
$status = req( $test, 200, 'root', 'POST', 'employee/eid', <<"EOS" );
{ "eid" : $eid_of_root, "sec_id" : "foobar" }
EOS
is( $status->level, 'OK' );

t/dispatch/history.t  view on Meta::CPAN

    ok( exists $status->payload->{'history'} );
    is( scalar @{ $status->payload->{'history'} }, 1 );
    is( $status->payload->{'history'}->[0]->{'eid'}, $site->DOCHAZKA_EID_OF_ROOT );
    ok( exists $status->payload->{'history'}->[0]->{'effective'} );

    note( 'get history of non-existent EID' );

    note( 'fail 403 as demo' );
    req( $test, 403, 'demo', 'GET', "$base/4534" );

    note( '"succeed" (404) as root' );
    req( $test, 404, 'root', 'GET', "$base/4534" );

    note( 'GET history of various invalid EIDs' );
    foreach my $inv_eid ( 'asas', '!*!*', 'A long list of useless words followed by lots of spaces                                           \\,', '3.1415926', '; drop database dochazka-test;' ) {
        foreach my $user ( qw( demo root ) ) {
            req( $test, 400, 'demo', 'GET', "$base/$inv_eid" );
        }
    }

    foreach my $inv_eid ( '0', '-1' ) {
        # - as demo
        req( $test, 403, 'demo', 'GET', "$base/$inv_eid" );
        # - as root
        req( $test, 404, 'root', 'GET', "$base/$inv_eid" );
    }

    foreach my $inv_eid ( '3443/plus/several/bogus/levels/of/subresources' ) {
        # - as demo (entire resource is invalid, so ACL check is not reached)
        req( $test, 400, 'demo', 'GET', "$base/$inv_eid" );
        # - as root
        req( $test, 400, 'root', 'GET', "$base/$inv_eid" );
    }

    note( 'PUT' );

t/dispatch/history.t  view on Meta::CPAN

    is( scalar @{ $status->payload->{'history'} }, 1 );
    is( $status->payload->{'history'}->[0]->{'eid'}, $site->DOCHAZKA_EID_OF_ROOT );
    ok( exists $status->payload->{'history'}->[0]->{'effective'} );

    my $uri = $base . '/' .  $site->DOCHAZKA_EID_OF_ROOT .
              '/[1999-12-31 23:59, 2000-01-01 00:01)';

    note( 'fail 403 as demo' );
    req( $test, 403, 'demo', 'GET', $uri );

    note( '"succeed" (404) as root' );
    req( $test, 404, 'root', 'GET', $uri );

    note( 'non-existent EID');
    my $tsr = '[1999-12-31 23:59, 2000-01-01 00:01)';
    req( $test, 403, 'demo', 'GET', "$base/4534/$tsr" );
    req( $test, 404, 'root', 'GET', "$base/4534/$tsr" );

    note( 'invalid EID (caught by Path::Router validations)' );
    foreach my $user ( qw( demo root ) ) {
        req( $test, 400, $user, 'GET', "$base/asas/$tsr" );
    }
    
    note( 'PUT, POST, DELETE' );
    foreach my $user ( qw( demo root ) ) {
        foreach my $method ( qw( PUT POST DELETE ) ) {
            req( $test, 405, $user, $method, "$base/23/[,)" );

t/dispatch/history.t  view on Meta::CPAN

        BAIL_OUT(0);
    }
    ok( exists $status->payload->{'effective'} );

    my $uri = $base . '/' .  $site->DOCHAZKA_EID_OF_ROOT .
              '/"1891-12-31 23:59:59"';

    note( 'fail 403 as demo' );
    req( $test, 403, 'demo', 'GET', $uri );

    note( '"succeed" (404) as root' );
    req( $test, 404, 'root', 'GET', $uri );

    note( 'non-existent EID');
    my $ts = '\'2015-01-06 14:55\'';
    req( $test, 403, 'demo', 'GET', "$base/4534/$ts" );
    req( $test, 404, 'root', 'GET', "$base/4534/$ts" );

    note( 'invalid EID (caught by Path::Router validations)' );
    foreach my $user ( qw( demo root ) ) {
        req( $test, 400, $user, 'GET', "$base/asas/$ts" );
    }

    note( 'PUT, POST, DELETE' );
    foreach my $user ( qw( demo root ) ) {
        foreach my $method ( qw( PUT POST DELETE ) ) {
            req( $test, 405, $user, $method, "$base/23/1966-09-28 00:00" );

t/dispatch/history.t  view on Meta::CPAN

    }
    ok( exists $status->payload->{'effective'} );

    my $uri = $base . '/' .  $site->DOCHAZKA_EID_OF_ROOT . '/now';

    note( 'fail 403 as demo' );
    req( $test, 403, 'demo', 'GET', $uri );

    $uri = $base . '/' .  $site->DOCHAZKA_EID_OF_DEMO . '/now';

    note( '"succeed" (404) as root' );
    req( $test, 404, 'root', 'GET', $uri );

    note( 'non-existent EID');
    my $ts = '\'2015-01-06 14:55\'';
    req( $test, 403, 'demo', 'GET', "$base/4534/now" );
    req( $test, 404, 'root', 'GET', "$base/4534/now" );

    note( 'invalid EID (caught by Path::Router validations)' );
    foreach my $user ( qw( demo root ) ) {
        req( $test, 400, $user, 'GET', "$base/asas/now" );
    }

    note( 'PUT, POST, DELETE' );
    foreach my $user ( qw( demo root ) ) {
        foreach my $method ( qw( PUT POST DELETE ) ) {
            req( $test, 405, $user, $method, "$base/23/now" );

t/dispatch/history.t  view on Meta::CPAN

    ok( defined $status->payload );
    ok( exists $status->payload->{'nick'} );
    is( $status->payload->{'nick'}, 'root' );
    ok( exists $status->payload->{'history'} );
    is( scalar @{ $status->payload->{'history'} }, 1 );
    is( $status->payload->{'history'}->[0]->{'eid'}, 1 );
    ok( exists $status->payload->{'history'}->[0]->{'effective'} );

    note( 'non-existent employee' );
    req( $test, 403, 'demo', 'GET', "$base/rotoroot" );
    req( $test, 404, 'root', 'GET', "$base/rotoroot" );
    
    note( 'PUT' );
    req( $test, 405, 'demo', 'PUT', "$base/asdf" );
    req( $test, 405, 'root', 'PUT', "$base/asdf" );
    
    note( "POST" );
    $j = ( $base =~ m/^priv/ ) 
        ? '{ "effective":"1969-04-27 9:45", "priv":"inactive" }'
        : '{ "effective":"1969-04-27 9:45", "sid":' . $ts_sid . ' }';
    req( $test, 403, 'demo', 'POST', "$base/demo", $j );

t/dispatch/history.t  view on Meta::CPAN

    ok( exists $status->payload->{'nick'} );
    is( $status->payload->{'nick'}, 'root' );
    ok( exists $status->payload->{'history'} );
    is( scalar @{ $status->payload->{'history'} }, 1 );
    is( $status->payload->{'history'}->[0]->{'eid'}, 1 );
    ok( exists $status->payload->{'history'}->[0]->{'effective'} );

    note( 'non-existent employee' );
    my $tsr = '[1891-12-30, 1892-01-02)';
    req( $test, 403, 'demo', 'GET', "$base/humphreybogart/$tsr" );
    req( $test, 404, 'root', 'GET', "$base/humphreybogart/$tsr" );

    note( 'root employee, with tsrange but no records found' );
    req( $test, 403, 'demo', 'GET', "$base/root/[1999-12-31 23:59, 2000-01-01 00:01)" );
    req( $test, 404, 'root', 'GET', "$base/root/[1999-12-31 23:59, 2000-01-01 00:01)" );
    
    note( 'PUT, POST, DELETE' );
    foreach my $user ( qw( demo root ) ) {
        foreach my $method ( qw( PUT POST DELETE ) ) {
            req( $test, 405, $user, $method, "$base/root/[1999-12-31 23:59, 2000-01-01 00:01)" );
        }
    }
}    


t/dispatch/history.t  view on Meta::CPAN

    is( $status->payload->{'remark'}, 'I am foo!' );

    note( 'DELETE' );

    note( 'delete the privhistory record we created earlier' );
    $status = req( $test, 200, 'root', 'DELETE', "$base/$tphid" );
    is( $status->level, "OK" );
    is( $status->code, 'DOCHAZKA_CUD_OK' );
    
    note( 'not there anymore' );
    req( $test, 404, 'root', 'GET', "$base/$tphid" );
    
    note( 'and demo is a passerby again' );
    $status = req( $test, 200, 'demo', 'GET', "priv/self" );
    is( $status->level, 'OK' );
    is( $status->payload->{'priv'}, "passerby" );
} 

note( 'tear down' );
$status = delete_all_attendance_data();
BAIL_OUT(0) unless $status->ok;

t/dispatch/interval_lock.t  view on Meta::CPAN

    note( 'non-existent ID and also out of range' );
    $int_obj = <<"EOH";
{ "$idmap{$il}" : 3434342342342, "remark" : 34334342 }
EOH
    dbi_err( $test, 500, 'root', 'POST', $base, $int_obj, qr/out of range for type integer/ );
    
    note( 'non-existent ID' );
    $int_obj = <<"EOH";
{ "$idmap{$il}" : 342342342, "remark" : 34334342 }
EOH
    req( $test, 404, 'root', 'POST', $base, $int_obj );
    
    note( 'throw a couple curve balls: weirded object' );
    my $weirded_object = '{ "copious_turds" : 555, "long_desc" : "wang wang wazoo", "disabled" : "f" }';
    req( $test, 400, 'root', 'POST', $base, $weirded_object );
    
    note( 'throw a couple curve balls: no closing bracket' );
    my $no_closing_bracket = '{ "copious_turds" : 555, "long_desc" : "wang wang wazoo", "disabled" : "f"';
    req( $test, 400, 'root', 'POST', $base, $no_closing_bracket );
    
    note( 'throw a couple curve balls: weirded object 2' );

t/dispatch/interval_lock.t  view on Meta::CPAN

        ok( $status->payload->{'aid'} );
        ok( exists $status->payload->{'long_desc'} );
        ok( $status->payload->{'remark'} );
        ok( ! defined $status->payload->{'long_desc'} );
    }
    
    note( 'fail invalid ID' );
    req( $test, 400, 'active', 'GET', "$base/jj" );

    note( 'fail non-existent IID' );
    req( $test, 404, 'active', 'GET', "$base/444" );
    
    note( 'PUT' );
    my $int_obj = '{ "remark" : "Change is good" }';
    note( 'test with demo fail 405' );
    req( $test, 403, 'demo', 'PUT', "$base/$test_id", $int_obj );
    note( 'test with root no request body' );
    $status = req( $test, 200, 'root', 'PUT', "$base/$test_id" );
    is( $status->level, 'OK' );
    is( $status->code, 'DISPATCH_UPDATE_NO_CHANGE_OK' );
    note( 'test with root fail invalid JSON' );

t/dispatch/interval_lock.t  view on Meta::CPAN

    #req( $test, 403, 'active', 'DELETE', "$base/$test_id" );
    #
    # - test with root success
    #diag( "DELETE $base/$test_id" );

    note( 'delete something testy' );
    $status = req( $test, 200, 'root', 'DELETE', "$base/$test_id" );
    is( $status->level, 'OK', "DELETE $base/:iid 3" );
    is( $status->code, 'DOCHAZKA_CUD_OK', "DELETE $base/:iid 4" );
    note( 'really gone' );
    req( $test, 404, 'active', 'GET', "$base/$test_id" );
    note( 'test with root fail invalid IID' );
    req( $test, 400, 'root', 'DELETE', "$base/asd" );
}


note( 're-create the testing intervals' );
$test_iid = test_interval_new( $test );
$test_lid = create_testing_lock( $test );


t/dispatch/interval_lock.t  view on Meta::CPAN

note( '- this one will be OK' );
$status = req( $test, 201, 'active', 'POST', 'interval/new', <<"EOH" );
{ "aid" : $aid_of_work, "eid" : $eid_active, "intvl" : "[2014-07-31 20:00, 2014-08-01 00:00)" }
EOH
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );
my $tiid = $status->payload->{'iid'};
$status = req( $test, 200, 'active', 'DELETE', "interval/iid/$tiid" );
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );
req( $test, 404, 'active', 'GET', "interval/iid/$tiid" );

note( '- illegal interval' );
dbi_err( $test, 500, 'active', 'POST', 'interval/new', 
    '{ "aid" : ' . $aid_of_work . ', "eid" : ' . $eid_active . 
        ', "intvl" : "[2014-07-31 20:00, 2014-08-01 00:00]" }', $illegal );

note( '- upper bound not evenly divisible by 5 minutes' );
dbi_err( $test, 500, 'active', 'POST', 'interval/new',
    '{ "aid" : '. $aid_of_work . ', "eid" : ' . $eid_active .
        ', "intvl" : "[2014-07-31 20:00, 2014-08-01 00:01)" }',

t/dispatch/interval_lock.t  view on Meta::CPAN


$status = req( $test, 201, 'active', 'POST', 'interval/new', <<"EOH" );
{ "aid" : $aid_of_work, "eid" : $eid_active, "intvl" : "[2014-09-01 00:00, 2014-09-01 04:00)" }
EOH
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );
$tiid = $status->payload->{'iid'};
$status = req( $test, 200, 'active', 'DELETE', "interval/iid/$tiid" );
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );
req( $test, 404, 'active', 'GET', "interval/iid/$tiid" );

note( '- conclusion: I see no way to create an unexpected conflict (famous last words)' );

note( 'CLEANUP: delete the lock' );
$status = req( $test, 200, 'root', 'DELETE', "lock/lid/$tlid" );
is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );
req( $test, 404, 'root', 'GET', "lock/lid/$tlid" );

note( "have an active user try to create a lock on someone else's attendance" );

req( $test, 403, 'active', 'POST', $base, <<"EOH" );
{ "eid" : $eid_inactive, "intvl" : "[1957-02-01 00:00, 1957-03-01 00:00)" }
EOH

note( 'DELETE -> 405' );
req( $test, 405, 'demo', 'DELETE', $base );
req( $test, 405, 'root', 'DELETE', $base );

t/dispatch/schedule.t  view on Meta::CPAN

is( $status->level, 'OK' );
is( $status->code, 'DOCHAZKA_CUD_OK' );
ok( exists $status->{payload} );
ok( $status->payload->{shid} );
my $ts_shid = $status->payload->{shid};

note( 'GET' );

note( 'root has no schedule' );
req( $test, 403, 'demo', 'GET', "$base/1" );
req( $test, 404, 'root', 'GET', "$base/1" );

note( 'as root, with timestamp before 1892' );
req( $test, 404, 'root', 'GET', "$base/1/1891-12-31 23:59" );

note( 'as root, with timestamp 1892-01-01 01:01' );
req( $test, 404, 'root', 'GET', "$base/1/1892-01-01 00:01" );

note( 'get inactive\'s schedule in many different ways' );
foreach my $spec ( 
    [ 'root', "$base/$ts_eid_inactive" ], 
    [ 'root', "/schedule/nick/inactive" ],
    [ 'inactive', "/schedule/self" ],
    [ 'root', "$base/$ts_eid_inactive/2015-06-01 00:00" ], 
    [ 'root', "/schedule/nick/inactive/2015-06-01 00:00" ],
    [ 'inactive', "/schedule/self/2015-06-01 00:00" ],
   ) {

t/dispatch/schedule.t  view on Meta::CPAN

    note( 'payload is a schedule object' );
    my $sch = App::Dochazka::REST::Model::Schedule->spawn( %{ $status->payload->{schedule} } );
    is( $sch->scode, 'KOBOLD' );
}

note( 'attempt to GET inactive\'s schedule at a time when it didn\'t have one assigned' );
foreach my $spec ( [ 'root', "$base/$ts_eid_inactive/1955-06-01 00:00" ], 
                   [ 'root', "/schedule/nick/inactive/1955-06-01 00:00" ], 
                   [ 'inactive', "/schedule/self/1955-06-01 00:00" ] ) {
    note( 'GET ' . $spec->[1] . ' as ' . $spec->[0] );
    req( $test, 404, $spec->[0], 'GET', $spec->[1] );
}

note( "GET $base/5343 (non-existent EID)" );
req( $test, 404, 'root', 'GET', "$base/5343" );

note( "GET $base/-33 (negative EID)" );
req( $test, 404, 'root', 'GET', "$base/-33" );

note( "GET $base/34343.33322.22.21 (non-integer EID)" );
req( $test, 400, 'root', 'GET', "$base/34343.33322.22.21" );

note( "GET $base/a thousand clarinets (non-integer EID)" );
req( $test, 400, 'root', 'GET', "$base/a thousand clarinets" );

note( "GET $base/sad;f3.** * @#/ 12341 12 jjj (non-integer EID combined with invalid timestamp)" );
req( $test, 400, 'root', 'GET', "$base/sad;f3.** * @#/ 12341 12 jjj" );

note( "GET $base/2/ 12341 12 jjj (valid EID, stupid timestamp)" );
dbi_err( $test, 500, 'root', 'GET', "$base/2/ 12341 12 jjj", undef,
    qr/invalid input syntax for type timestamp with time zone/ );

note( "GET $base/999/ 12341 12 jjj (valid EID, stupid timestamp)" );
req( $test, 404, 'root', 'GET', "$base/999/ 12341 12 jjj" );

note( "GET $base/999/2999-01-33 00:-1 (valid EID, valid timestamp)" );
req( $test, 404, 'root', 'GET', "$base/999/2999-01-33 00:-1" );

note( "GET $base/1/2999-01-33 00:-1 (valid EID, valid timestamp)" );
dbi_err( $test, 500, 'root', 'GET', "$base/1/2999-01-33 00:-1", undef,
    qr#date/time field value out of range# );

note( "GET $base/1/wanger (wanger)" );
dbi_err( $test, 500, 'root', 'GET', "$base/1/wanger", undef,
    qr/invalid input syntax for type timestamp/ );


t/dispatch/schedule.t  view on Meta::CPAN

note( '===========================================' );
$base = "schedule/nick";
docu_check($test, "$base/:nick/?:ts");

note( 'GET' );

note( "GET $base/root as demo" );
req( $test, 403, 'demo', 'GET', "$base/root" );

note( "GET $base/root as root" );
req( $test, 404, 'root', 'GET', "$base/root" );

note( "GET $base/root as root, with timestamp before 1892-01-01" );
req( $test, 404, 'root', 'GET', "$base/root/1891-12-31 23:59" );

note( "GET $base/root as root, with timestamp 1892-01-01 00:00" );
req( $test, 404, 'root', 'GET', "$base/root/1892-01-01 00:01" );

note( 'non-existent nick' );
req( $test, 404, 'root', 'GET', "$base/wanger" );

note( 'negative nick (does not pass validations)' );
req( $test, 400, 'root', 'GET', "$base/-33" );

note( 'stupid nick (fails on validations)' );
req( $test, 400, 'root', 'GET', "$base/34343.33322.22.21" );

note( 'stupid nick (fails on validations)' );
req( $test, 400, 'root', 'GET', "$base/a thousand clarinets" );

note( 'stupid nick (fails on validations)' );
req( $test, 400, 'root', 'GET', "$base/sad;f3.** * @#/ 12341 12 jjj" );

note( 'stupid ts' );
dbi_err( $test, 500, 'root', 'GET', "$base/demo/ 12341 12 jjj", undef,
    qr/invalid input syntax for type timestamp/ );

note( 'valid nick, stupid timestamp' );
req( $test, 404, 'root', 'GET', "$base/wanger/ 12341 12 jjj" );

note( 'valid nick, valid timestamp' );
req( $test, 404, 'root', 'GET', "$base/wanger/2999-01-33 00:-1" );

note( 'valid nick, valid timestamp' );
dbi_err( $test, 500, 'root', 'GET', "$base/root/2999-01-33 00:-1", undef,
    qr#date/time field value out of range# );

note( 'wanger' );
#req( $test, 404, 'root', 'GET', "$base/root/wanger" );
dbi_err( $test, 500, 'root', 'GET', "$base/root/wanger", undef,
    qr/invalid input syntax for type timestamp/ );

note( 'PUT, POST, DELETE -> 405' );
foreach my $user ( qw( demo root ) ) {
    foreach my $method ( qw( PUT POST DELETE ) ) {
        foreach my $baz ( "$base/root", "$base/root/1892-01-01" ) {
            req( $test, 405, $user, $method, $baz );
        }
    }

t/dispatch/schedule.t  view on Meta::CPAN


note( '=============================' );
note( '"schedule/self/?:ts" resource' );
note( '=============================' );
$base = "schedule/self";
docu_check($test, "$base/?:ts");

note( 'GET' );

note( "GET $base as demo, but demo has no schedule history" );
req( $test, 404, 'demo', 'GET', $base );

note( "GET $base as root, but root has no schedule history, either" );
req( $test, 404, 'root', 'GET', $base );

note( "GET $base as root, with timestamp before 1892 A.D." );
req( $test, 404, 'root', 'GET', "$base/1891-12-31 23:59" );

note( "GET $base as root, with timestamp 1892-01-01 00:00" );
req( $test, 404, 'root', 'GET', "$base/1892-01-01 00:01" );

note( "wanger" );
dbi_err( $test, 500, 'root', 'GET', "$base/wanger", undef,
    qr/invalid input syntax for type timestamp/ );

note( "stupid ts" );
dbi_err( $test, 500, 'root', 'GET', "$base/ 12341 12 jjj", undef, 
    qr/invalid input syntax for type timestamp/ );

note( "valid nick, valid timestamp" );

t/dispatch/top.t  view on Meta::CPAN

is( $status->level, 'OK' );
is( $status->code, 'DISPATCH_ONLINE_DOCUMENTATION' );
ok( exists $status->payload->{'resource'} );
is( $status->payload->{'resource'}, '/' );
ok( exists $status->payload->{'documentation'} );
ok( length( $status->payload->{'documentation'} ) > 10 );
isnt( $status->payload->{'documentation'}, $docustr, "We are not getting the same string over and over again" );
isnt( $docustr_len, length( $status->payload->{'documentation'} ), "We are not getting the same string over and over again" );

note( '- be nice but not careful (non-existent resource)' );
$status = req( $test, 404, 'demo', 'POST', $base, '"echop"' );
is( $status->text, 'Could not find resource definition for echop');

note( '- be pathological (invalid JSON)' );
req( $test, 400, 'demo', 'POST', $base, 'bare, unquoted string will never pass for JSON' );
req( $test, 400, 'demo', 'POST', $base, '[ 1, 2' );

note( 'DELETE docu -> 405' );
req( $test, 405, 'demo', 'DELETE', $base );
req( $test, 405, 'root', 'DELETE', $base );
    

t/dispatch/top.t  view on Meta::CPAN

        req( $test, 400, $user, 'GET', "$base//////1/1/234/20" );
        req( $test, 400, $user, 'GET', "$base/{}" );
        req( $test, 400, $user, 'GET', "$base/-1" );
        req( $test, 400, $user, 'GET', "$base/0" );
        req( $test, 400, $user, 'GET', "$base/" . '\b\b\o\o\g\\' );
        req( $test, 400, $user, 'GET', "$base/" . '\b\b\o\o\\' );
        req( $test, 400, $user, 'GET', "$base/**0" );
        req( $test, 400, $user, 'GET', "$base/}lieutenant" );
        req( $test, 400, $user, 'GET', "$base/<HEAD><tail><body>&nbsp;" );
    }
    my $mapping = { "demo" => 403, "root" => 404 };
    foreach my $user ( qw( demo root ) ) {
        # these are bogus in that the parameter does not exist
        req( $test, $mapping->{$user}, $user, 'GET', "$base/DOCHEEEHAWHAZKA_appname" );
        req( $test, $mapping->{$user}, $user, 'GET', "$base/abc123" );
        req( $test, $mapping->{$user}, $user, 'GET', "$base/null" );
    }
}

note( 'metaparam-specific tests' );

note( '- try to use metaparam to access a site parameter' );
req( $test, 404, 'root', 'GET', "param/meta/DOCHAZKA_APPNAME" );

note( '- as root, existent parameter' );
$status = req( $test, 200, 'root', 'GET', 'param/meta/META_DOCHAZKA_UNIT_TESTING/' );
is( $status->level, 'OK' );
is( $status->code, 'MREST_PARAMETER_VALUE' );
is_deeply( $status->payload, { 'META_DOCHAZKA_UNIT_TESTING' => 1 } );

note( "- as root, existent parameter without trailing '/'" );
$status = req( $test, 200, 'root', 'GET', 'param/meta/META_DOCHAZKA_UNIT_TESTING' );
is( $status->level, 'OK' );

t/ldap.t  view on Meta::CPAN

    note( "1. populate $noex employee object from LDAP: fail" );
    $emp = $noex_obj->clone();
    $status = $emp->ldap_sync();
    ok( $status->not_ok );


    note( '####################################################' );
    note( 'DISPATCH TESTS' );
    note( '####################################################' );

    note( "GET employee/nick/$noex/ldap returns 404" );
    req( $test, 404, 'root', 'GET', "employee/nick/$noex/ldap" );

    note( "PUT employee/nick/$noex/ldap returns 404" );
    req( $test, 404, 'root', 'PUT', "employee/nick/$noex/ldap" );

    note( "GET employee/nick/$ex/ldap returns 200" );
    $status = req( $test, 200, 'root', 'GET', "employee/nick/$ex/ldap" );
    is( $status->level, 'OK' );
    ok( $status->payload, "There is a payload" );
    map {
        my $value = $status->payload->{$_};
        ok( $value, "$_ property has value $value" );
    } @props;



( run in 1.585 second using v1.01-cache-2.11-cpan-39bf76dae61 )