proper support for an Optional keyword
[gitmo/MooseX-Types-Structured.git] / t / 02-constraints.t
1 BEGIN {
2         use strict;
3         use warnings;
4         use Test::More tests=>42;
5         use Test::Exception;
6 }
7
8 {
9     package Test::MooseX::Meta::TypeConstraint::Structured;
10
11     use Moose;
12     use MooseX::Types::Structured qw(Tuple Dict Optional);
13         use Moose::Util::TypeConstraints;
14
15     subtype 'MyString',
16      as 'Str',
17      where { $_=~m/abc/};
18
19     has 'tuple' => (is=>'rw', isa=>Tuple['Int', 'Str', 'MyString']);
20     has 'dict' => (is=>'rw', isa=>Dict[name=>'Str', age=>'Int']);
21     has 'dict_with_maybe' => (is=>'rw', isa=>Dict[name=>'Str', age=>'Maybe[Int]']);     
22         has 'tuple_with_param' => (is=>'rw', isa=>Tuple['Int', 'Str', 'ArrayRef[Int]']);
23         has 'tuple_with_maybe' => (is=>'rw', isa=>Tuple['Int', 'Str', 'Maybe[Int]']);
24         has 'dict_with_tuple' => (is=>'rw', isa=>Dict[key1=>'Str', key2=>Tuple['Int','Str']]);
25     has 'optional_tuple' => (is=>'rw', isa=>Tuple['Int', 'Int', Optional['Int']] );
26     has 'optional_dict' => (is=>'rw', isa=>Dict[key1=>'Int', Optional[key2=>'Int']] );
27     
28     has 'crazy' => (
29         is=>'rw',
30         isa=>Tuple
31             ## First ArrayRef Arg is the required type constraints for the top
32             ## level Tuple.
33             [
34                 'Int',
35                 'MyString',
36                 ## The third required element is a Dict type constraint, which
37                 ## itself has two required keys and a third optional key.
38                 Dict[name=>'Str',age=>'Int', Optional[visits=>'Int']],
39                 Optional[
40                     'Int',
41                     ## This Tuple has one required type constraint and two optional.
42                     Tuple['Int', Optional['Int','HashRef']],                    
43                 ],
44             ],      
45     );
46 }
47
48 ## Instantiate a new test object
49
50 ok my $record = Test::MooseX::Meta::TypeConstraint::Structured->new
51  => 'Instantiated new Record test class.';
52  
53 isa_ok $record => 'Test::MooseX::Meta::TypeConstraint::Structured'
54  => 'Created correct object type.';
55  
56 ## Test crazy
57
58 lives_ok sub {
59     $record->crazy([1,'hello.abc.world', {name=>'John', age=>39}]);
60 } => 'Set crazy attribute with no optionals used';
61
62 is_deeply $record->crazy, [1, 'hello.abc.world', {name=>'John', age=>39}]
63  => 'correct values for crazy attributes no optionals';
64  
65 lives_ok sub {
66     $record->crazy([1,'hello.abc.world', {name=>'John', age=>39, visits=>10},10, [1,2,{key=>'value'}]]);
67 } => 'Set crazy attribute with all optionals used';
68
69 is_deeply $record->crazy, [1,'hello.abc.world', {name=>'John', age=>39, visits=>10},10, [1,2,{key=>'value'}]]
70  => 'correct values for crazy attributes all optionals';
71
72 lives_ok sub {
73     $record->crazy([1,'hello.abc.world', {name=>'John', age=>39},10, [1,2]]);
74 } => 'Set crazy attribute with some optionals used';
75
76 throws_ok sub {
77     $record->crazy([1,'hello', 'test.xxx.test']);    
78 }, qr/Validation failed for 'MyString'/
79  => 'Properly failed for bad value in crazy attribute 01';
80
81 throws_ok sub {
82     $record->crazy([1,'hello.abc.world', {notname=>'John', notage=>39}]);    
83 }, qr/Validation failed for 'Str'/
84  => 'Properly failed for bad value in crazy attribute 02';
85  
86 ## Test Tuple type constraint
87
88 lives_ok sub {
89     $record->tuple([1,'hello', 'test.abc.test']);
90 } => 'Set tuple attribute without error';
91
92 is $record->tuple->[0], 1
93  => 'correct set the tuple attribute index 0';
94
95 is $record->tuple->[1], 'hello'
96  => 'correct set the tuple attribute index 1';
97
98 is $record->tuple->[2], 'test.abc.test'
99  => 'correct set the tuple attribute index 2';
100
101 throws_ok sub {
102     $record->tuple([1,'hello', 'test.xxx.test']);    
103 }, qr/Validation failed for 'MyString'/
104  => 'Properly failed for bad value in custom type constraint';
105  
106 throws_ok sub {
107     $record->tuple(['asdasd',2, 'test.abc.test']);      
108 }, qr/Validation failed for 'Int'/
109  => 'Got Expected Error for violating constraints';
110
111 ## Test the Dictionary type constraint
112  
113 lives_ok sub {
114     $record->dict({name=>'frith', age=>23});
115 } => 'Set dict attribute without error';
116
117 is $record->dict->{name}, 'frith'
118  => 'correct set the dict attribute name';
119
120 is $record->dict->{age}, 23
121  => 'correct set the dict attribute age';
122  
123 throws_ok sub {
124     $record->dict({name=>[1,2,3], age=>'sdfsdfsd'});      
125 }, qr/Validation failed for 'Str'/
126  => 'Got Expected Error for bad value in dict';
127
128 ## Test tuple_with_maybe
129
130 lives_ok sub {
131     $record->tuple_with_maybe([1,'hello', 1]);
132 } => 'Set tuple attribute without error';
133
134 throws_ok sub {
135     $record->tuple_with_maybe([1,'hello', 'a']);
136 }, qr/Validation failed for 'Maybe\[Int\]'/
137  => 'Properly failed for bad value parameterized constraint';
138
139 lives_ok sub {
140     $record->tuple_with_maybe([1,'hello']);
141 } => 'Set tuple attribute without error skipping optional parameter';
142
143 ## Test Tuple with parameterized type
144
145 lives_ok sub {
146     $record->tuple_with_param([1,'hello', [1,2,3]]);
147 } => 'Set tuple attribute without error';
148
149 throws_ok sub {
150     $record->tuple_with_param([1,'hello', [qw/a b c/]]);
151 }, qr/Validation failed for 'ArrayRef\[Int\]'/
152  => 'Properly failed for bad value parameterized constraint';
153
154 ## Test dict_with_maybe
155
156 lives_ok sub {
157     $record->dict_with_maybe({name=>'frith', age=>23});
158 } => 'Set dict attribute without error';
159
160 is $record->dict_with_maybe->{name}, 'frith'
161  => 'correct set the dict attribute name';
162
163 is $record->dict_with_maybe->{age}, 23
164  => 'correct set the dict attribute age';
165  
166 throws_ok sub {
167     $record->dict_with_maybe({name=>[1,2,3], age=>'sdfsdfsd'});      
168 }, qr/Validation failed for 'Str'/
169  => 'Got Expected Error for bad value in dict';
170
171 throws_ok sub {
172     $record->dict_with_maybe({age=>30});      
173 }, qr/Validation failed for 'Str'/
174  => 'Got Expected Error for missing named parameter';
175
176 lives_ok sub {
177     $record->dict_with_maybe({name=>'usal'});
178 } => 'Set dict attribute without error, skipping optional';
179
180 ## Test dict_with_tuple
181
182 lives_ok sub {
183     $record->dict_with_tuple({key1=>'Hello', key2=>[1,'World']});
184 } => 'Set tuple attribute without error';
185
186 throws_ok sub {
187     $record->dict_with_tuple({key1=>'Hello', key2=>['World',2]});
188 }, qr/Validation failed for 'Int'/
189  => 'Threw error on bad constraint';
190
191 ## Test optional_tuple
192
193 lives_ok sub {
194     $record->optional_tuple([1,2,3]);
195 } => 'Set tuple attribute with optional bits';
196
197 is_deeply $record->optional_tuple, [1,2,3]
198  => 'correct values set';
199  
200 lives_ok sub {
201     $record->optional_tuple([4,5]);
202 } => 'Set tuple attribute withOUT optional bits';
203
204 is_deeply $record->optional_tuple, [4,5]
205  => 'correct values set again';
206  
207 throws_ok sub {
208     $record->optional_tuple([1,2,'bad']);   
209 }, qr/Validation failed for 'Int'/
210  => 'Properly failed for bad value in optional bit';
211
212 # Test optional_dict
213
214 lives_ok sub {
215     $record->optional_dict({key1=>1,key2=>2});
216 } => 'Set tuple attribute with optional bits';
217
218 is_deeply $record->optional_dict, {key1=>1,key2=>2}
219  => 'correct values set';
220  
221 lives_ok sub {
222     $record->optional_dict({key1=>3});
223 } => 'Set tuple attribute withOUT optional bits';
224
225 is_deeply $record->optional_dict, {key1=>3}
226  => 'correct values set again';
227  
228 throws_ok sub {
229     $record->optional_dict({key1=>1,key2=>'bad'});   
230 }, qr/Validation failed for 'Int'/
231  => 'Properly failed for bad value in optional bit';
232