Bivio::Test::Unit::FormModel
# Copyright (c) 2005-2011 bivio Software, Inc. All Rights Reserved.
# $Id$
package Bivio::Test::Unit::FormModel;
use strict;
use Bivio::Base 'TestUnit.Unit';
my($_M) = b_use('Biz.Model');
my($_A) = b_use('IO.Alert');
my($_R) = b_use('IO.Ref');
sub empty_case {
my($proto, $return) = @_;
return ([] => [{
$proto->builtin_class() => $return,
}]);
}
sub error_case {
return shift->simple_case(@_);
}
sub simple_case {
my($proto, $input, $return) = @_;
return ([$input] => [{
$proto->builtin_class() => $return,
}]);
}
sub new_unit {
my($proto, $class_name, $attrs) = @_;
my($class) = $proto->use(($attrs ||= {})->{class_name} ||= $class_name);
$proto->builtin_options({class_name => $class});
# $attrs gets passed to SUPER below and SUPER doesn't know setup_request
my($setup_request) = delete($attrs->{setup_request});
$attrs = {}
unless ref($attrs);
my($form_is_json) = delete($attrs->{form_is_json}) || 0;
my($req) = b_use('Test.Request')->get_instance;
return $proto->SUPER::new({
class_name => $_M->get_instance($class)->package_name,
compute_params => sub {
my($case, $params, $method, $object) = @_;
$object->reset_instance_state;
return $params
unless $method eq 'process';
$_M->new($req, 'Lock')->release_all;
_setup_request($proto, $setup_request, @_);
unless (@$params) {
$req->delete('form');
return [$req];
}
my($hash) = $params->[0];
return $params
unless ref($hash) eq 'HASH';
$hash = {
%{$object->get_fields_for_primary_keys()},
%$hash,
} if $object->isa('Bivio::Biz::ListFormModel');
Bivio::Die->die('You must set empty_row_count on case: ', $case)
if $object->isa('Bivio::Biz::ExpandableListFormModel')
&& !exists($hash->{empty_row_count});
Bivio::Die->die(
ref($object),
': auxiliary form; set task with initialize_fully; primary=',
$req->get('task')->get('form_model'),
) if $object->is_auxiliary_on_task;
return [$req->put(
form => {
$object->VERSION_FIELD => $object->get_info('version'),
$form_is_json ? (
%$hash,
b_use('Biz.FormModel')->CONTENT_TYPE_FIELD => 'application/json',
) : map(
{
my($t) = $object->get_field_type($_);
my($v) = $hash->{$_};
$v = $v->($case)
if ref($v) eq 'CODE';
($object->get_field_name_for_html($_) =>
($t->isa('Bivio::Type::FileField')
? $v
: $t->to_literal($v)));
}
keys(%$hash),
),
},
)];
},
check_return => sub {
my($case, $actual, $expect) = @_;
my($o) = $case->get('object');
$case->error_note($o->get_errors);
return $expect
unless $case->get('method') eq 'process';
my($e) = $expect->[0];
if ($case->get('comparator') eq 'nested_contains') {
$case->actual_return([_walk_tree_actual($case, $e, $req)]);
return [$e];
}
return $expect
unless (ref($e) eq 'HASH' && @$expect == 1)
|| ($o->isa('Bivio::Biz::ListFormModel')
&& ref($expect->[0]) eq 'HASH');
$e = _walk_tree_expect($case, $e);
$case->actual_return([_walk_tree_actual($case, $e, $req)]);
return [$e];
},
compute_return => sub {
my($case, $actual) = @_;
$case->error_note($case->get('object')->get_errors);
return $actual;
},
%$attrs,
});
}
sub req_state {
my($proto, $params) = @_;
$params = $_R->nested_copy($params);
return sub {
$proto->builtin_self->put(req_state => $params);
return 1;
} => 1;
}
sub req_state_merge {
my($proto, $params) = @_;
$params = $_R->nested_copy($params);
return sub {
my($self) = $proto->builtin_self;
$self->put(req_state => {
%{$self->get_or_default('req_state', {})},
%$params,
});
return 1;
} => 1;
}
sub run_unit {
return shift->SUPER::run_unit(@_)
if @_ == 3;
my($self, $case_group) = @_;
my($req) = b_use('Test.Request')->initialize_fully;
return $self->SUPER::run_unit(
$self->map_by_two(sub {
my($params, $return) = @_;
return ([$req] => [
ref($params) eq 'ARRAY' ? (process => [$params => $return])
: ($params, $return),
]);
}, $case_group),
);
}
sub _eval {
return map(ref($_) eq 'CODE' ? $_->() : $_, @_);
}
sub _field_err {
my($o, $field) = @_;
my($ed) = $o->get_field_error_detail($field);
return $o->get_field_error($field)->get_name
. ($ed ? ": $ed" : '');
}
sub _setup_request {
my($proto, $setup_request, $case, $params, undef, $object) = @_;
my($self) = $proto->builtin_self;
my($req) = $proto->builtin_req;
$req->clear_nondurable_state;
$req->put(
path_info => undef,
query => undef,
form => undef,
);
$req->get('task')->put_attr_for_test(
form_model => ref($object),
next => $req->get('task_id'),
require_context => 0,
) unless $req->get('task')->unsafe_get_attr_as_id('next')
&& !$req->get('task_id')->eq_shell_util;
if (my $rs = $self->unsafe_get('req_state')) {
$rs = {%$rs};
foreach my $p (qw(user realm)) {
next
unless exists($rs->{$p});
my($m) = "set_$p";
$req->$m(_eval(delete($rs->{$p})));
};
if (my $t = delete($rs->{task})) {
$req->initialize_fully(_eval($t));
}
$req->put(_eval(%$rs))
if %$rs;
}
$setup_request->($case, $params)
if $setup_request;
return;
}
sub _walk_tree_actual {
my($case, $e, $o) = @_;
return ref($e) eq 'HASH'
? {map(($_ => _walk_tree_actual(
$case, $e->{$_},
!defined($o) ? undef
: UNIVERSAL::can($o, 'unsafe_get')
? $o->can('get_field_error') && $o->get_field_error($_)
? _field_err($o, $_)
: $o->unsafe_get($_)
: ref($o) eq 'HASH' ? $o->{$_}
: ref($o) eq 'ARRAY' && $_ =~ /^\d+$/ ? $o->[$_]
: $_A->format_args($_, ': not index of ', $o))),
keys(%$e),
)} : $o;
}
sub _walk_tree_expect {
my($case, $e) = @_;
return ref($e) eq 'HASH'
? {map(($_ => _walk_tree_expect($case, $e->{$_})), keys(%$e))}
: ref($e) eq 'CODE'
? $e->($case)
: $e;
}
1;