logxpy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- logxpy/__init__.py +126 -0
- logxpy/_action.py +958 -0
- logxpy/_async.py +186 -0
- logxpy/_base.py +80 -0
- logxpy/_compat.py +71 -0
- logxpy/_config.py +45 -0
- logxpy/_dest.py +88 -0
- logxpy/_errors.py +58 -0
- logxpy/_fmt.py +68 -0
- logxpy/_generators.py +136 -0
- logxpy/_mask.py +23 -0
- logxpy/_message.py +195 -0
- logxpy/_output.py +517 -0
- logxpy/_pool.py +93 -0
- logxpy/_traceback.py +126 -0
- logxpy/_types.py +71 -0
- logxpy/_util.py +56 -0
- logxpy/_validation.py +486 -0
- logxpy/_version.py +21 -0
- logxpy/cli.py +61 -0
- logxpy/dask.py +172 -0
- logxpy/decorators.py +268 -0
- logxpy/filter.py +124 -0
- logxpy/journald.py +88 -0
- logxpy/json.py +149 -0
- logxpy/loggerx.py +253 -0
- logxpy/logwriter.py +84 -0
- logxpy/parse.py +191 -0
- logxpy/prettyprint.py +173 -0
- logxpy/serializers.py +36 -0
- logxpy/stdlib.py +23 -0
- logxpy/tai64n.py +45 -0
- logxpy/testing.py +472 -0
- logxpy/tests/__init__.py +9 -0
- logxpy/tests/common.py +36 -0
- logxpy/tests/strategies.py +231 -0
- logxpy/tests/test_action.py +1751 -0
- logxpy/tests/test_api.py +86 -0
- logxpy/tests/test_async.py +67 -0
- logxpy/tests/test_compat.py +13 -0
- logxpy/tests/test_config.py +21 -0
- logxpy/tests/test_coroutines.py +105 -0
- logxpy/tests/test_dask.py +211 -0
- logxpy/tests/test_decorators.py +54 -0
- logxpy/tests/test_filter.py +122 -0
- logxpy/tests/test_fmt.py +42 -0
- logxpy/tests/test_generators.py +292 -0
- logxpy/tests/test_journald.py +246 -0
- logxpy/tests/test_json.py +208 -0
- logxpy/tests/test_loggerx.py +44 -0
- logxpy/tests/test_logwriter.py +262 -0
- logxpy/tests/test_message.py +334 -0
- logxpy/tests/test_output.py +921 -0
- logxpy/tests/test_parse.py +309 -0
- logxpy/tests/test_pool.py +55 -0
- logxpy/tests/test_prettyprint.py +303 -0
- logxpy/tests/test_pyinstaller.py +35 -0
- logxpy/tests/test_serializers.py +36 -0
- logxpy/tests/test_stdlib.py +73 -0
- logxpy/tests/test_tai64n.py +66 -0
- logxpy/tests/test_testing.py +1051 -0
- logxpy/tests/test_traceback.py +251 -0
- logxpy/tests/test_twisted.py +814 -0
- logxpy/tests/test_util.py +45 -0
- logxpy/tests/test_validation.py +989 -0
- logxpy/twisted.py +265 -0
- logxpy-0.1.0.dist-info/METADATA +100 -0
- logxpy-0.1.0.dist-info/RECORD +72 -0
- logxpy-0.1.0.dist-info/WHEEL +5 -0
- logxpy-0.1.0.dist-info/entry_points.txt +2 -0
- logxpy-0.1.0.dist-info/licenses/LICENSE +201 -0
- logxpy-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for L{eliot._validation}.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from unittest import TestCase
|
|
6
|
+
|
|
7
|
+
from .._validation import (
|
|
8
|
+
Field,
|
|
9
|
+
MessageType,
|
|
10
|
+
ActionType,
|
|
11
|
+
ValidationError,
|
|
12
|
+
fields,
|
|
13
|
+
_MessageSerializer,
|
|
14
|
+
)
|
|
15
|
+
from .._action import start_action, startTask
|
|
16
|
+
from .._output import MemoryLogger
|
|
17
|
+
from ..serializers import identity
|
|
18
|
+
from .. import add_destination, remove_destination
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TypedFieldTests(TestCase):
|
|
22
|
+
"""
|
|
23
|
+
Tests for L{Field.forTypes}.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def test_validateCorrectType(self):
|
|
27
|
+
"""
|
|
28
|
+
L{Field.validate} will not raise an exception if the given value is in
|
|
29
|
+
the list of supported classes.
|
|
30
|
+
"""
|
|
31
|
+
field = Field.forTypes("path", [str, int], "A path!")
|
|
32
|
+
field.validate(123)
|
|
33
|
+
field.validate("hello")
|
|
34
|
+
|
|
35
|
+
def test_validateNone(self):
|
|
36
|
+
"""
|
|
37
|
+
When given a "class" of C{None}, L{Field.validate} will support
|
|
38
|
+
validating C{None}.
|
|
39
|
+
"""
|
|
40
|
+
field = Field.forTypes("None", [None], "Nothing!")
|
|
41
|
+
field.validate(None)
|
|
42
|
+
|
|
43
|
+
def test_validateWrongType(self):
|
|
44
|
+
"""
|
|
45
|
+
L{Field.validate} will raise a L{ValidationError} exception if the
|
|
46
|
+
given value's type is not in the list of supported classes.
|
|
47
|
+
"""
|
|
48
|
+
field = Field.forTypes("key", [int], "An integer key")
|
|
49
|
+
self.assertRaises(ValidationError, field.validate, "lala")
|
|
50
|
+
self.assertRaises(ValidationError, field.validate, None)
|
|
51
|
+
self.assertRaises(ValidationError, field.validate, object())
|
|
52
|
+
|
|
53
|
+
def test_extraValidatorPasses(self):
|
|
54
|
+
"""
|
|
55
|
+
L{Field.validate} will not raise an exception if the extra validator
|
|
56
|
+
does not raise an exception.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def validate(i):
|
|
60
|
+
if i > 10:
|
|
61
|
+
return
|
|
62
|
+
else:
|
|
63
|
+
raise ValidationError("too small")
|
|
64
|
+
|
|
65
|
+
field = Field.forTypes("key", [int], "An integer key", validate)
|
|
66
|
+
field.validate(11)
|
|
67
|
+
|
|
68
|
+
def test_extraValidatorFails(self):
|
|
69
|
+
"""
|
|
70
|
+
L{Field.validate} will raise a L{ValidationError} exception if the
|
|
71
|
+
extra validator raises one.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def validate(i):
|
|
75
|
+
if i > 10:
|
|
76
|
+
return
|
|
77
|
+
else:
|
|
78
|
+
raise ValidationError("too small")
|
|
79
|
+
|
|
80
|
+
field = Field.forTypes("key", [int], "An int", validate)
|
|
81
|
+
self.assertRaises(ValidationError, field.validate, 10)
|
|
82
|
+
|
|
83
|
+
def test_onlyValidTypes(self):
|
|
84
|
+
"""
|
|
85
|
+
Only JSON supported types can be passed to L{Field.forTypes}.
|
|
86
|
+
"""
|
|
87
|
+
self.assertRaises(TypeError, Field.forTypes, "key", [complex], "Oops")
|
|
88
|
+
|
|
89
|
+
def test_listIsValidType(self):
|
|
90
|
+
"""
|
|
91
|
+
A C{list} is a valid type for L{Field.forTypes}.
|
|
92
|
+
"""
|
|
93
|
+
Field.forTypes("key", [list], "Oops")
|
|
94
|
+
|
|
95
|
+
def test_dictIsValidType(self):
|
|
96
|
+
"""
|
|
97
|
+
A C{dict} is a valid type for L{Field.forTypes}.
|
|
98
|
+
"""
|
|
99
|
+
Field.forTypes("key", [dict], "Oops")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class FieldTests(TestCase):
|
|
103
|
+
"""
|
|
104
|
+
Tests for L{Field}.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def test_description(self):
|
|
108
|
+
"""
|
|
109
|
+
L{Field.description} stores the passed in description.
|
|
110
|
+
"""
|
|
111
|
+
field = Field("path", identity, "A path!")
|
|
112
|
+
self.assertEqual(field.description, "A path!")
|
|
113
|
+
|
|
114
|
+
def test_optionalDescription(self):
|
|
115
|
+
"""
|
|
116
|
+
L{Field} can be constructed with no description.
|
|
117
|
+
"""
|
|
118
|
+
field = Field("path", identity)
|
|
119
|
+
self.assertEqual(field.description, "")
|
|
120
|
+
|
|
121
|
+
def test_key(self):
|
|
122
|
+
"""
|
|
123
|
+
L{Field.key} stores the passed in field key.
|
|
124
|
+
"""
|
|
125
|
+
field = Field("path", identity, "A path!")
|
|
126
|
+
self.assertEqual(field.key, "path")
|
|
127
|
+
|
|
128
|
+
def test_serialize(self):
|
|
129
|
+
"""
|
|
130
|
+
L{Field.serialize} calls the given serializer function.
|
|
131
|
+
"""
|
|
132
|
+
result = []
|
|
133
|
+
Field("key", result.append, "field").serialize(123)
|
|
134
|
+
self.assertEqual(result, [123])
|
|
135
|
+
|
|
136
|
+
def test_serializeResult(self):
|
|
137
|
+
"""
|
|
138
|
+
L{Field.serialize} returns the result of the given serializer function.
|
|
139
|
+
"""
|
|
140
|
+
result = Field("key", lambda obj: 456, "field").serialize(None)
|
|
141
|
+
self.assertEqual(result, 456)
|
|
142
|
+
|
|
143
|
+
def test_serializeCallsValidate(self):
|
|
144
|
+
"""
|
|
145
|
+
L{Field.validate} calls the serializer, in case that raises an
|
|
146
|
+
exception for the given input.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
class MyException(Exception):
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
def serialize(obj):
|
|
153
|
+
raise MyException()
|
|
154
|
+
|
|
155
|
+
field = Field("key", serialize, "")
|
|
156
|
+
self.assertRaises(MyException, field.validate, 123)
|
|
157
|
+
|
|
158
|
+
def test_noExtraValidator(self):
|
|
159
|
+
"""
|
|
160
|
+
L{Field.validate} doesn't break if there is no extra validator.
|
|
161
|
+
"""
|
|
162
|
+
field = Field("key", identity, "")
|
|
163
|
+
field.validate(123)
|
|
164
|
+
|
|
165
|
+
def test_extraValidatorPasses(self):
|
|
166
|
+
"""
|
|
167
|
+
L{Field.validate} will not raise an exception if the extra validator
|
|
168
|
+
does not raise an exception.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def validate(i):
|
|
172
|
+
if i > 10:
|
|
173
|
+
return
|
|
174
|
+
else:
|
|
175
|
+
raise ValidationError("too small")
|
|
176
|
+
|
|
177
|
+
field = Field("path", identity, "A path!", validate)
|
|
178
|
+
field.validate(11)
|
|
179
|
+
|
|
180
|
+
def test_extraValidatorFails(self):
|
|
181
|
+
"""
|
|
182
|
+
L{Field.validate} will raise a L{ValidationError} exception if the
|
|
183
|
+
extra validator raises one.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
def validate(i):
|
|
187
|
+
if i > 10:
|
|
188
|
+
return
|
|
189
|
+
else:
|
|
190
|
+
raise ValidationError("too small")
|
|
191
|
+
|
|
192
|
+
field = Field("path", identity, "A path!", validate)
|
|
193
|
+
self.assertRaises(ValidationError, field.validate, 10)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class FieldForValueTests(TestCase):
|
|
197
|
+
"""
|
|
198
|
+
Tests for L{Field.forValue}.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def test_forValue(self):
|
|
202
|
+
"""
|
|
203
|
+
L{Field.forValue} creates a L{Field} with the given key and description.
|
|
204
|
+
"""
|
|
205
|
+
field = Field.forValue("key", None, "description")
|
|
206
|
+
self.assertEqual(field.key, "key")
|
|
207
|
+
self.assertEqual(field.description, "description")
|
|
208
|
+
|
|
209
|
+
def test_forValueGoodValue(self):
|
|
210
|
+
"""
|
|
211
|
+
The L{Field.forValue}-created L{Field} validates the value it was
|
|
212
|
+
constructed with.
|
|
213
|
+
"""
|
|
214
|
+
field = Field.forValue("key", 1234, "description")
|
|
215
|
+
field.validate(1234)
|
|
216
|
+
|
|
217
|
+
def test_valueFieldWrongValue(self):
|
|
218
|
+
"""
|
|
219
|
+
The L{Field.forValue}-created L{Field} raises a L{ValidationError} for
|
|
220
|
+
different values.
|
|
221
|
+
"""
|
|
222
|
+
field = Field.forValue("key", 1234, "description")
|
|
223
|
+
self.assertRaises(ValidationError, field.validate, 5678)
|
|
224
|
+
|
|
225
|
+
def test_serialize(self):
|
|
226
|
+
"""
|
|
227
|
+
The L{Field.forValue}-created L{Field} returns the given object when
|
|
228
|
+
serializing, regardless of input.
|
|
229
|
+
|
|
230
|
+
If the caller is buggy, no need to log garbage if we know what needs
|
|
231
|
+
logging. These bugs will be caught by unit tests, anyway, if author of
|
|
232
|
+
code is doing things correctly.
|
|
233
|
+
"""
|
|
234
|
+
field = Field.forValue("key", 1234, "description")
|
|
235
|
+
self.assertEqual(field.serialize(None), 1234)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class FieldsTests(TestCase):
|
|
239
|
+
"""
|
|
240
|
+
Tests for L{fields}.
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
def test_positional(self):
|
|
244
|
+
"""
|
|
245
|
+
L{fields} accepts positional arguments of L{Field} instances and
|
|
246
|
+
combines them with fields specied as keyword arguments.
|
|
247
|
+
"""
|
|
248
|
+
a_field = Field("akey", identity)
|
|
249
|
+
l = fields(a_field, another=str)
|
|
250
|
+
self.assertIn(a_field, l)
|
|
251
|
+
self.assertEqual(
|
|
252
|
+
{(type(field), field.key) for field in l},
|
|
253
|
+
{(Field, "akey"), (Field, "another")},
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
def test_keys(self):
|
|
257
|
+
"""
|
|
258
|
+
L{fields} creates L{Field} instances with the given keys.
|
|
259
|
+
"""
|
|
260
|
+
l = fields(key=int, status=str)
|
|
261
|
+
self.assertEqual(
|
|
262
|
+
{(type(field), field.key) for field in l},
|
|
263
|
+
{(Field, "key"), (Field, "status")},
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
def test_validTypes(self):
|
|
267
|
+
"""
|
|
268
|
+
The L{Field} instances constructed by L{fields} validate the specified
|
|
269
|
+
types.
|
|
270
|
+
"""
|
|
271
|
+
(field,) = fields(key=int)
|
|
272
|
+
self.assertRaises(ValidationError, field.validate, "abc")
|
|
273
|
+
|
|
274
|
+
def test_noSerialization(self):
|
|
275
|
+
"""
|
|
276
|
+
The L{Field} instances constructed by L{fields} do no special
|
|
277
|
+
serialization.
|
|
278
|
+
"""
|
|
279
|
+
(field,) = fields(key=int)
|
|
280
|
+
self.assertEqual(field.serialize("abc"), "abc")
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class MessageSerializerTests(TestCase):
|
|
284
|
+
"""
|
|
285
|
+
Tests for L{_MessageSerializer}.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
def test_noMultipleFields(self):
|
|
289
|
+
"""
|
|
290
|
+
L{_MessageSerializer.__init__} will raise a L{ValueError} exception if
|
|
291
|
+
constructed with more than object per field name.
|
|
292
|
+
"""
|
|
293
|
+
self.assertRaises(
|
|
294
|
+
ValueError,
|
|
295
|
+
_MessageSerializer,
|
|
296
|
+
[
|
|
297
|
+
Field("akey", identity, ""),
|
|
298
|
+
Field("akey", identity, ""),
|
|
299
|
+
Field("message_type", identity, ""),
|
|
300
|
+
],
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
def test_noBothTypeFields(self):
|
|
304
|
+
"""
|
|
305
|
+
L{_MessageSerializer.__init__} will raise a L{ValueError} exception if
|
|
306
|
+
constructed with both a C{"message_type"} and C{"action_type"} field.
|
|
307
|
+
"""
|
|
308
|
+
self.assertRaises(
|
|
309
|
+
ValueError,
|
|
310
|
+
_MessageSerializer,
|
|
311
|
+
[Field("message_type", identity, ""), Field("action_type", identity, "")],
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def test_missingTypeField(self):
|
|
315
|
+
"""
|
|
316
|
+
L{_MessageSerializer.__init__} will raise a L{ValueError} if there is
|
|
317
|
+
neither a C{"message_type"} nor a C{"action_type"} field.
|
|
318
|
+
"""
|
|
319
|
+
self.assertRaises(ValueError, _MessageSerializer, [])
|
|
320
|
+
|
|
321
|
+
def test_noTaskLevel(self):
|
|
322
|
+
"""
|
|
323
|
+
L{_MessageSerializer.__init__} will raise a L{ValueError} if there is
|
|
324
|
+
a C{"task_level"} field included.
|
|
325
|
+
"""
|
|
326
|
+
self.assertRaises(
|
|
327
|
+
ValueError,
|
|
328
|
+
_MessageSerializer,
|
|
329
|
+
[Field("message_type", identity, ""), Field("task_level", identity, "")],
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def test_noTaskUuid(self):
|
|
333
|
+
"""
|
|
334
|
+
L{_MessageSerializer.__init__} will raise a L{ValueError} if there is
|
|
335
|
+
a C{"task_uuid"} field included.
|
|
336
|
+
"""
|
|
337
|
+
self.assertRaises(
|
|
338
|
+
ValueError,
|
|
339
|
+
_MessageSerializer,
|
|
340
|
+
[Field("message_type", identity, ""), Field("task_uuid", identity, "")],
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def test_noTimestamp(self):
|
|
344
|
+
"""
|
|
345
|
+
L{_MessageSerializer.__init__} will raise a L{ValueError} if there is
|
|
346
|
+
a C{"timestamp"} field included.
|
|
347
|
+
"""
|
|
348
|
+
self.assertRaises(
|
|
349
|
+
ValueError,
|
|
350
|
+
_MessageSerializer,
|
|
351
|
+
[Field("message_type", identity, ""), Field("timestamp", identity, "")],
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
def test_noUnderscoreStart(self):
|
|
355
|
+
"""
|
|
356
|
+
L{_MessageSerializer.__init__} will raise a L{ValueError} if there is
|
|
357
|
+
a field included whose name starts with C{"_"}.
|
|
358
|
+
"""
|
|
359
|
+
self.assertRaises(
|
|
360
|
+
ValueError,
|
|
361
|
+
_MessageSerializer,
|
|
362
|
+
[Field("message_type", identity, ""), Field("_key", identity, "")],
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
def test_serialize(self):
|
|
366
|
+
"""
|
|
367
|
+
L{_MessageSerializer.serialize} will serialize all values in the given
|
|
368
|
+
dictionary using the respective L{Field}.
|
|
369
|
+
"""
|
|
370
|
+
serializer = _MessageSerializer(
|
|
371
|
+
[
|
|
372
|
+
Field.forValue("message_type", "mymessage", "The type"),
|
|
373
|
+
Field("length", len, "The length of a thing"),
|
|
374
|
+
]
|
|
375
|
+
)
|
|
376
|
+
message = {"message_type": "mymessage", "length": "thething"}
|
|
377
|
+
serializer.serialize(message)
|
|
378
|
+
self.assertEqual(message, {"message_type": "mymessage", "length": 8})
|
|
379
|
+
|
|
380
|
+
def test_missingSerializer(self):
|
|
381
|
+
"""
|
|
382
|
+
If a value in the dictionary passed to L{_MessageSerializer.serialize}
|
|
383
|
+
has no respective field, it is unchanged.
|
|
384
|
+
|
|
385
|
+
Logging attempts to capture everything, with minimal work; with any
|
|
386
|
+
luck this value is JSON-encodable. Unit tests should catch such bugs, in any case.
|
|
387
|
+
"""
|
|
388
|
+
serializer = _MessageSerializer(
|
|
389
|
+
[
|
|
390
|
+
Field.forValue("message_type", "mymessage", "The type"),
|
|
391
|
+
Field("length", len, "The length of a thing"),
|
|
392
|
+
]
|
|
393
|
+
)
|
|
394
|
+
message = {"message_type": "mymessage", "length": "thething", "extra": 123}
|
|
395
|
+
serializer.serialize(message)
|
|
396
|
+
self.assertEqual(
|
|
397
|
+
message, {"message_type": "mymessage", "length": 8, "extra": 123}
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
def test_fieldInstances(self):
|
|
401
|
+
"""
|
|
402
|
+
Fields to L{_MessageSerializer.__init__} should be instances of
|
|
403
|
+
L{Field}.
|
|
404
|
+
"""
|
|
405
|
+
a_field = Field("a_key", identity)
|
|
406
|
+
arg = object()
|
|
407
|
+
with self.assertRaises(TypeError) as cm:
|
|
408
|
+
_MessageSerializer([a_field, arg])
|
|
409
|
+
self.assertEqual(("Expected a Field instance but got", arg), cm.exception.args)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class MessageTypeTests(TestCase):
|
|
413
|
+
"""
|
|
414
|
+
Tests for L{MessageType}.
|
|
415
|
+
"""
|
|
416
|
+
|
|
417
|
+
def messageType(self):
|
|
418
|
+
"""
|
|
419
|
+
Return a L{MessageType} suitable for unit tests.
|
|
420
|
+
"""
|
|
421
|
+
return MessageType(
|
|
422
|
+
"myapp:mysystem",
|
|
423
|
+
[Field.forTypes("key", [int], ""), Field.forTypes("value", [int], "")],
|
|
424
|
+
"A message type",
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
def test_validateMissingType(self):
|
|
428
|
+
"""
|
|
429
|
+
L{MessageType._serializer.validate} raises a L{ValidationError} exception if the
|
|
430
|
+
given dictionary has no C{"message_type"} field.
|
|
431
|
+
"""
|
|
432
|
+
messageType = self.messageType()
|
|
433
|
+
self.assertRaises(
|
|
434
|
+
ValidationError, messageType._serializer.validate, {"key": 1, "value": 2}
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
def test_validateWrongType(self):
|
|
438
|
+
"""
|
|
439
|
+
L{MessageType._serializer.validate} raises a L{ValidationError}
|
|
440
|
+
exception if the given dictionary has the wrong value for the
|
|
441
|
+
C{"message_type"} field.
|
|
442
|
+
"""
|
|
443
|
+
messageType = self.messageType()
|
|
444
|
+
self.assertRaises(
|
|
445
|
+
ValidationError,
|
|
446
|
+
messageType._serializer.validate,
|
|
447
|
+
{"key": 1, "value": 2, "message_type": "wrong"},
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
def test_validateExtraField(self):
|
|
451
|
+
"""
|
|
452
|
+
L{MessageType._serializer.validate} raises a L{ValidationError}
|
|
453
|
+
exception if the given dictionary has an extra unknown field.
|
|
454
|
+
"""
|
|
455
|
+
messageType = self.messageType()
|
|
456
|
+
self.assertRaises(
|
|
457
|
+
ValidationError,
|
|
458
|
+
messageType._serializer.validate,
|
|
459
|
+
{"key": 1, "value": 2, "message_type": "myapp:mysystem", "extra": "hello"},
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
def test_validateMissingField(self):
|
|
463
|
+
"""
|
|
464
|
+
L{MessageType._serializer.validate} raises a L{ValidationError}
|
|
465
|
+
exception if the given dictionary has a missing field.
|
|
466
|
+
"""
|
|
467
|
+
messageType = self.messageType()
|
|
468
|
+
self.assertRaises(
|
|
469
|
+
ValidationError,
|
|
470
|
+
messageType._serializer.validate,
|
|
471
|
+
{"key": 1, "message_type": "myapp:mysystem"},
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
def test_validateFieldValidation(self):
|
|
475
|
+
"""
|
|
476
|
+
L{MessageType._serializer.validate} raises a L{ValidationError}
|
|
477
|
+
exception if the one of the field values fails field-specific
|
|
478
|
+
validation.
|
|
479
|
+
"""
|
|
480
|
+
messageType = self.messageType()
|
|
481
|
+
self.assertRaises(
|
|
482
|
+
ValidationError,
|
|
483
|
+
messageType._serializer.validate,
|
|
484
|
+
{"key": 1, "value": None, "message_type": "myapp:mysystem"},
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
def test_validateStandardFields(self):
|
|
488
|
+
"""
|
|
489
|
+
L{MessageType._serializer.validate} does not raise an exception if the
|
|
490
|
+
dictionary has the standard fields that are added to all messages.
|
|
491
|
+
"""
|
|
492
|
+
messageType = self.messageType()
|
|
493
|
+
messageType._serializer.validate(
|
|
494
|
+
{
|
|
495
|
+
"key": 1,
|
|
496
|
+
"value": 2,
|
|
497
|
+
"message_type": "myapp:mysystem",
|
|
498
|
+
"task_level": "/",
|
|
499
|
+
"task_uuid": "123",
|
|
500
|
+
"timestamp": "xxx",
|
|
501
|
+
}
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
def test_call(self):
|
|
505
|
+
"""
|
|
506
|
+
L{MessageType.__call__} creates a new L{Message} with correct
|
|
507
|
+
C{message_type} field value added.
|
|
508
|
+
"""
|
|
509
|
+
messageType = self.messageType()
|
|
510
|
+
message = messageType()
|
|
511
|
+
self.assertEqual(message._contents, {"message_type": messageType.message_type})
|
|
512
|
+
|
|
513
|
+
def test_callSerializer(self):
|
|
514
|
+
"""
|
|
515
|
+
L{MessageType.__call__} creates a new L{Message} with the
|
|
516
|
+
L{MessageType._serializer} as its serializer.
|
|
517
|
+
"""
|
|
518
|
+
messageType = self.messageType()
|
|
519
|
+
message = messageType()
|
|
520
|
+
self.assertIs(message._serializer, messageType._serializer)
|
|
521
|
+
|
|
522
|
+
def test_callWithFields(self):
|
|
523
|
+
"""
|
|
524
|
+
L{MessageType.__call__} creates a new L{Message} with the additional
|
|
525
|
+
given fields.
|
|
526
|
+
"""
|
|
527
|
+
messageType = self.messageType()
|
|
528
|
+
message = messageType(key=2, value=3)
|
|
529
|
+
self.assertEqual(
|
|
530
|
+
message._contents,
|
|
531
|
+
{"message_type": messageType.message_type, "key": 2, "value": 3},
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
def test_logCallsDefaultLoggerWrite(self):
|
|
535
|
+
"""
|
|
536
|
+
L{MessageType.log} calls the given logger's C{write} method with a
|
|
537
|
+
dictionary that is superset of the L{Message} contents.
|
|
538
|
+
"""
|
|
539
|
+
messages = []
|
|
540
|
+
add_destination(messages.append)
|
|
541
|
+
self.addCleanup(remove_destination, messages.append)
|
|
542
|
+
message_type = self.messageType()
|
|
543
|
+
message_type.log(key=1234, value=3)
|
|
544
|
+
self.assertEqual(messages[0]["key"], 1234)
|
|
545
|
+
self.assertEqual(messages[0]["value"], 3)
|
|
546
|
+
self.assertEqual(messages[0]["message_type"], message_type.message_type)
|
|
547
|
+
|
|
548
|
+
def test_description(self):
|
|
549
|
+
"""
|
|
550
|
+
L{MessageType.description} stores the passed in description.
|
|
551
|
+
"""
|
|
552
|
+
messageType = self.messageType()
|
|
553
|
+
self.assertEqual(messageType.description, "A message type")
|
|
554
|
+
|
|
555
|
+
def test_optionalDescription(self):
|
|
556
|
+
"""
|
|
557
|
+
L{MessageType} can be constructed without a description.
|
|
558
|
+
"""
|
|
559
|
+
messageType = MessageType("name", [])
|
|
560
|
+
self.assertEqual(messageType.description, "")
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
class ActionTypeTestsMixin(object):
|
|
564
|
+
"""
|
|
565
|
+
Mixin for tests for the three L{ActionType} message variants.
|
|
566
|
+
"""
|
|
567
|
+
|
|
568
|
+
def getValidMessage(self):
|
|
569
|
+
"""
|
|
570
|
+
Return a dictionary of a message that is of the action status being
|
|
571
|
+
tested.
|
|
572
|
+
"""
|
|
573
|
+
raise NotImplementedError("Override in subclasses")
|
|
574
|
+
|
|
575
|
+
def getSerializer(self, actionType):
|
|
576
|
+
"""
|
|
577
|
+
Given a L{ActionType}, return the L{_MessageSerializer} for this
|
|
578
|
+
variant.
|
|
579
|
+
"""
|
|
580
|
+
raise NotImplementedError("Override in subclasses")
|
|
581
|
+
|
|
582
|
+
def actionType(self):
|
|
583
|
+
"""
|
|
584
|
+
Return a L{ActionType} suitable for unit tests.
|
|
585
|
+
"""
|
|
586
|
+
return ActionType(
|
|
587
|
+
"myapp:mysystem:myaction",
|
|
588
|
+
[Field.forTypes("key", [int], "")], # start fields
|
|
589
|
+
[Field.forTypes("value", [int], "")], # success fields
|
|
590
|
+
"A action type",
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
def test_validateMissingType(self):
|
|
594
|
+
"""
|
|
595
|
+
L{ActionType.validate} raises a L{ValidationError} exception if the
|
|
596
|
+
given dictionary has no C{"action_type"} field.
|
|
597
|
+
"""
|
|
598
|
+
actionType = self.actionType()
|
|
599
|
+
message = self.getValidMessage()
|
|
600
|
+
del message["action_type"]
|
|
601
|
+
self.assertRaises(
|
|
602
|
+
ValidationError, self.getSerializer(actionType).validate, message
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
def test_validateWrongType(self):
|
|
606
|
+
"""
|
|
607
|
+
L{ActionType.validate} raises a L{ValidationError} exception if the
|
|
608
|
+
given dictionary has the wrong value for the C{"action_type"} field.
|
|
609
|
+
"""
|
|
610
|
+
actionType = self.actionType()
|
|
611
|
+
message = self.getValidMessage()
|
|
612
|
+
message["action_type"] = "xxx"
|
|
613
|
+
self.assertRaises(
|
|
614
|
+
ValidationError, self.getSerializer(actionType).validate, message
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
def test_validateExtraField(self):
|
|
618
|
+
"""
|
|
619
|
+
L{ActionType.validate} raises a L{ValidationError} exception if the
|
|
620
|
+
given dictionary has an extra unknown field.
|
|
621
|
+
"""
|
|
622
|
+
actionType = self.actionType()
|
|
623
|
+
message = self.getValidMessage()
|
|
624
|
+
message["extra"] = "ono"
|
|
625
|
+
self.assertRaises(
|
|
626
|
+
ValidationError, self.getSerializer(actionType).validate, message
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
def test_validateMissingField(self):
|
|
630
|
+
"""
|
|
631
|
+
L{ActionType.validate} raises a L{ValidationError} exception if the
|
|
632
|
+
given dictionary has a missing field.
|
|
633
|
+
"""
|
|
634
|
+
actionType = self.actionType()
|
|
635
|
+
message = self.getValidMessage()
|
|
636
|
+
for key in message:
|
|
637
|
+
if key != "action_type":
|
|
638
|
+
del message[key]
|
|
639
|
+
break
|
|
640
|
+
self.assertRaises(
|
|
641
|
+
ValidationError, self.getSerializer(actionType).validate, message
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
def test_validateFieldValidation(self):
|
|
645
|
+
"""
|
|
646
|
+
L{ActionType.validate} raises a L{ValidationError} exception if the
|
|
647
|
+
one of the field values fails field-specific validation.
|
|
648
|
+
"""
|
|
649
|
+
actionType = self.actionType()
|
|
650
|
+
message = self.getValidMessage()
|
|
651
|
+
for key in message:
|
|
652
|
+
if key != "action_type":
|
|
653
|
+
message[key] = object()
|
|
654
|
+
break
|
|
655
|
+
self.assertRaises(
|
|
656
|
+
ValidationError, self.getSerializer(actionType).validate, message
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
def test_validateStandardFields(self):
|
|
660
|
+
"""
|
|
661
|
+
L{ActionType.validate} does not raise an exception if the dictionary
|
|
662
|
+
has the standard fields that are added to all messages.
|
|
663
|
+
"""
|
|
664
|
+
actionType = self.actionType()
|
|
665
|
+
message = self.getValidMessage()
|
|
666
|
+
message.update({"task_level": "/", "task_uuid": "123", "timestamp": "xxx"})
|
|
667
|
+
self.getSerializer(actionType).validate(message)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
class ActionTypeStartMessage(TestCase, ActionTypeTestsMixin):
|
|
671
|
+
"""
|
|
672
|
+
Tests for L{ActionType} validation of action start messages.
|
|
673
|
+
"""
|
|
674
|
+
|
|
675
|
+
def getValidMessage(self):
|
|
676
|
+
"""
|
|
677
|
+
Return a dictionary of a valid action start message.
|
|
678
|
+
"""
|
|
679
|
+
return {
|
|
680
|
+
"action_type": "myapp:mysystem:myaction",
|
|
681
|
+
"action_status": "started",
|
|
682
|
+
"key": 1,
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
def getSerializer(self, actionType):
|
|
686
|
+
return actionType._serializers.start
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
class ActionTypeSuccessMessage(TestCase, ActionTypeTestsMixin):
|
|
690
|
+
"""
|
|
691
|
+
Tests for L{ActionType} validation of action success messages.
|
|
692
|
+
"""
|
|
693
|
+
|
|
694
|
+
def getValidMessage(self):
|
|
695
|
+
"""
|
|
696
|
+
Return a dictionary of a valid action success message.
|
|
697
|
+
"""
|
|
698
|
+
return {
|
|
699
|
+
"action_type": "myapp:mysystem:myaction",
|
|
700
|
+
"action_status": "succeeded",
|
|
701
|
+
"value": 2,
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
def getSerializer(self, actionType):
|
|
705
|
+
return actionType._serializers.success
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
class ActionTypeFailureMessage(TestCase, ActionTypeTestsMixin):
|
|
709
|
+
"""
|
|
710
|
+
Tests for L{ActionType} validation of action failure messages.
|
|
711
|
+
"""
|
|
712
|
+
|
|
713
|
+
def getValidMessage(self):
|
|
714
|
+
"""
|
|
715
|
+
Return a dictionary of a valid action failure message.
|
|
716
|
+
"""
|
|
717
|
+
return {
|
|
718
|
+
"action_type": "myapp:mysystem:myaction",
|
|
719
|
+
"action_status": "failed",
|
|
720
|
+
"exception": "exceptions.RuntimeError",
|
|
721
|
+
"reason": "because",
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
def getSerializer(self, actionType):
|
|
725
|
+
return actionType._serializers.failure
|
|
726
|
+
|
|
727
|
+
def test_validateExtraField(self):
|
|
728
|
+
"""
|
|
729
|
+
Additional fields (which can be added by exception extraction) don't
|
|
730
|
+
cause a validation failure for failed action messages.
|
|
731
|
+
"""
|
|
732
|
+
actionType = self.actionType()
|
|
733
|
+
message = self.getValidMessage()
|
|
734
|
+
message.update({"task_level": "/", "task_uuid": "123", "timestamp": "xxx"})
|
|
735
|
+
message.update({"extra_field": "hello"})
|
|
736
|
+
self.getSerializer(actionType).validate(message)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
class ChildActionTypeStartMessage(TestCase):
|
|
740
|
+
"""
|
|
741
|
+
Tests for validation of child actions created with L{ActionType}.
|
|
742
|
+
"""
|
|
743
|
+
|
|
744
|
+
def test_childActionUsesChildValidator(self):
|
|
745
|
+
"""
|
|
746
|
+
Validation of child actions uses the child's validator.
|
|
747
|
+
"""
|
|
748
|
+
A = ActionType("myapp:foo", [Field.forTypes("a", [int], "")], [], "")
|
|
749
|
+
B = ActionType("myapp:bar", [Field.forTypes("b", [int], "")], [], "")
|
|
750
|
+
|
|
751
|
+
logger = MemoryLogger()
|
|
752
|
+
|
|
753
|
+
with A(logger, a=1):
|
|
754
|
+
with B(logger, b=2):
|
|
755
|
+
pass
|
|
756
|
+
# If wrong serializers/validators were used, this will fail:
|
|
757
|
+
logger.validate()
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
class ActionTypeTests(TestCase):
|
|
761
|
+
"""
|
|
762
|
+
General tests for L{ActionType}.
|
|
763
|
+
"""
|
|
764
|
+
|
|
765
|
+
def actionType(self):
|
|
766
|
+
"""
|
|
767
|
+
Return a L{ActionType} suitable for unit tests.
|
|
768
|
+
"""
|
|
769
|
+
return ActionType("myapp:mysystem:myaction", [], [], "An action type")
|
|
770
|
+
|
|
771
|
+
def test_call(self):
|
|
772
|
+
"""
|
|
773
|
+
L{ActionType.__call__} returns the result of calling
|
|
774
|
+
C{self._start_action}.
|
|
775
|
+
"""
|
|
776
|
+
actionType = self.actionType()
|
|
777
|
+
actionType._start_action = lambda *args, **kwargs: 1234
|
|
778
|
+
result = actionType(object())
|
|
779
|
+
self.assertEqual(result, 1234)
|
|
780
|
+
|
|
781
|
+
def test_callArguments(self):
|
|
782
|
+
"""
|
|
783
|
+
L{ActionType.__call__} calls C{self._start_action} with the logger,
|
|
784
|
+
action type, serializers and passed in fields.
|
|
785
|
+
"""
|
|
786
|
+
called = []
|
|
787
|
+
actionType = self.actionType()
|
|
788
|
+
actionType._start_action = lambda *args, **kwargs: called.append((args, kwargs))
|
|
789
|
+
logger = object()
|
|
790
|
+
actionType(logger, key=5)
|
|
791
|
+
self.assertEqual(
|
|
792
|
+
called,
|
|
793
|
+
[
|
|
794
|
+
(
|
|
795
|
+
(logger, "myapp:mysystem:myaction", actionType._serializers),
|
|
796
|
+
{"key": 5},
|
|
797
|
+
)
|
|
798
|
+
],
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
def test_defaultStartAction(self):
|
|
802
|
+
"""
|
|
803
|
+
L{ActionType._start_action} is L{eliot.start_action} by default.
|
|
804
|
+
"""
|
|
805
|
+
self.assertEqual(ActionType._start_action, start_action)
|
|
806
|
+
|
|
807
|
+
def test_as_task(self):
|
|
808
|
+
"""
|
|
809
|
+
L{ActionType.as_task} returns the result of calling C{self._startTask}.
|
|
810
|
+
"""
|
|
811
|
+
actionType = self.actionType()
|
|
812
|
+
actionType._startTask = lambda *args, **kwargs: 1234
|
|
813
|
+
result = actionType.as_task(object())
|
|
814
|
+
self.assertEqual(result, 1234)
|
|
815
|
+
|
|
816
|
+
def test_as_taskArguments(self):
|
|
817
|
+
"""
|
|
818
|
+
L{ActionType.as_task} calls C{self._startTask} with the logger,
|
|
819
|
+
action type and passed in fields.
|
|
820
|
+
"""
|
|
821
|
+
called = []
|
|
822
|
+
actionType = self.actionType()
|
|
823
|
+
actionType._startTask = lambda *args, **kwargs: called.append((args, kwargs))
|
|
824
|
+
logger = object()
|
|
825
|
+
actionType.as_task(logger, key=5)
|
|
826
|
+
self.assertEqual(
|
|
827
|
+
called,
|
|
828
|
+
[
|
|
829
|
+
(
|
|
830
|
+
(logger, "myapp:mysystem:myaction", actionType._serializers),
|
|
831
|
+
{"key": 5},
|
|
832
|
+
)
|
|
833
|
+
],
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
def test_defaultStartTask(self):
|
|
837
|
+
"""
|
|
838
|
+
L{ActionType._startTask} is L{eliot.startTask} by default.
|
|
839
|
+
"""
|
|
840
|
+
self.assertEqual(ActionType._startTask, startTask)
|
|
841
|
+
|
|
842
|
+
def test_description(self):
|
|
843
|
+
"""
|
|
844
|
+
L{ActionType.description} stores the passed in description.
|
|
845
|
+
"""
|
|
846
|
+
actionType = self.actionType()
|
|
847
|
+
self.assertEqual(actionType.description, "An action type")
|
|
848
|
+
|
|
849
|
+
def test_optionalDescription(self):
|
|
850
|
+
"""
|
|
851
|
+
L{ActionType} can be constructed without a description.
|
|
852
|
+
"""
|
|
853
|
+
actionType = ActionType("name", [], [])
|
|
854
|
+
self.assertEqual(actionType.description, "")
|
|
855
|
+
|
|
856
|
+
def test_as_taskDefaultLogger(self):
|
|
857
|
+
"""
|
|
858
|
+
L{ActionType.as_task} doesn't require passing in a logger.
|
|
859
|
+
"""
|
|
860
|
+
actionType = self.actionType()
|
|
861
|
+
actionType.as_task(key=5)
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
class EndToEndValidationTests(TestCase):
|
|
865
|
+
"""
|
|
866
|
+
Test validation of messages created using L{MessageType} and
|
|
867
|
+
L{ActionType}.
|
|
868
|
+
"""
|
|
869
|
+
|
|
870
|
+
MESSAGE = MessageType(
|
|
871
|
+
"myapp:mymessage",
|
|
872
|
+
[Field.forTypes("key", [int], "The key")],
|
|
873
|
+
"A message for testing.",
|
|
874
|
+
)
|
|
875
|
+
ACTION = ActionType(
|
|
876
|
+
"myapp:myaction",
|
|
877
|
+
[Field.forTypes("key", [int], "The key")],
|
|
878
|
+
[Field.forTypes("result", [str], "The result")],
|
|
879
|
+
"An action for testing.",
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
def test_correctFromMessageType(self):
|
|
883
|
+
"""
|
|
884
|
+
A correct message created using L{MessageType} will be logged to a
|
|
885
|
+
L{MemoryLogger}.
|
|
886
|
+
"""
|
|
887
|
+
logger = MemoryLogger()
|
|
888
|
+
msg = self.MESSAGE().bind(key=123)
|
|
889
|
+
msg.write(logger)
|
|
890
|
+
self.assertEqual(logger.messages[0]["key"], 123)
|
|
891
|
+
|
|
892
|
+
def test_incorrectFromMessageType(self):
|
|
893
|
+
"""
|
|
894
|
+
An incorrect message created using L{MessageType} will raise a
|
|
895
|
+
L{ValidationError} in L{MemoryLogger.validate}.
|
|
896
|
+
"""
|
|
897
|
+
logger = MemoryLogger()
|
|
898
|
+
msg = self.MESSAGE().bind(key="123")
|
|
899
|
+
msg.write(logger)
|
|
900
|
+
self.assertRaises(ValidationError, logger.validate)
|
|
901
|
+
|
|
902
|
+
def test_correctStartFromActionType(self):
|
|
903
|
+
"""
|
|
904
|
+
A correct start message created using a L{ActionType} will be logged
|
|
905
|
+
to a L{MemoryLogger}.
|
|
906
|
+
"""
|
|
907
|
+
logger = MemoryLogger()
|
|
908
|
+
with self.ACTION(logger, key=123) as action:
|
|
909
|
+
action.addSuccessFields(result="foo")
|
|
910
|
+
self.assertEqual(logger.messages[0]["key"], 123)
|
|
911
|
+
|
|
912
|
+
def test_omitLoggerFromActionType(self):
|
|
913
|
+
"""
|
|
914
|
+
If no logger is given to the L{ActionType} the default logger is used.
|
|
915
|
+
"""
|
|
916
|
+
messages = []
|
|
917
|
+
add_destination(messages.append)
|
|
918
|
+
self.addCleanup(remove_destination, messages.append)
|
|
919
|
+
with self.ACTION(key=123) as action:
|
|
920
|
+
action.add_success_fields(result="foo")
|
|
921
|
+
self.assertEqual(messages[0]["key"], 123)
|
|
922
|
+
|
|
923
|
+
def test_incorrectStartFromActionType(self):
|
|
924
|
+
"""
|
|
925
|
+
An incorrect start message created using a L{ActionType} will raise a
|
|
926
|
+
L{ValidationError}.
|
|
927
|
+
"""
|
|
928
|
+
logger = MemoryLogger()
|
|
929
|
+
with self.ACTION(logger, key="123") as action:
|
|
930
|
+
action.addSuccessFields(result="foo")
|
|
931
|
+
self.assertRaises(ValidationError, logger.validate)
|
|
932
|
+
|
|
933
|
+
def test_correctSuccessFromActionType(self):
|
|
934
|
+
"""
|
|
935
|
+
A correct success message created using a L{ActionType} will be logged
|
|
936
|
+
to a L{MemoryLogger}.
|
|
937
|
+
"""
|
|
938
|
+
logger = MemoryLogger()
|
|
939
|
+
with self.ACTION(logger, key=123) as action:
|
|
940
|
+
action.addSuccessFields(result="foo")
|
|
941
|
+
self.assertEqual(logger.messages[1]["result"], "foo")
|
|
942
|
+
|
|
943
|
+
def test_incorrectSuccessFromActionType(self):
|
|
944
|
+
"""
|
|
945
|
+
An incorrect success message created using a L{ActionType} will raise a
|
|
946
|
+
L{ValidationError}.
|
|
947
|
+
"""
|
|
948
|
+
logger = MemoryLogger()
|
|
949
|
+
with self.ACTION(logger, key=123) as action:
|
|
950
|
+
action.addSuccessFields(result=-1)
|
|
951
|
+
self.assertRaises(ValidationError, logger.validate)
|
|
952
|
+
|
|
953
|
+
def test_correctFailureFromActionType(self):
|
|
954
|
+
"""
|
|
955
|
+
A correct failure message created using a L{ActionType} will be logged
|
|
956
|
+
to a L{MemoryLogger}.
|
|
957
|
+
"""
|
|
958
|
+
logger = MemoryLogger()
|
|
959
|
+
|
|
960
|
+
def run():
|
|
961
|
+
with self.ACTION(logger, key=123):
|
|
962
|
+
raise RuntimeError("hello")
|
|
963
|
+
|
|
964
|
+
self.assertRaises(RuntimeError, run)
|
|
965
|
+
self.assertEqual(logger.messages[1]["reason"], "hello")
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
class PEP8Tests(TestCase):
|
|
969
|
+
"""
|
|
970
|
+
Tests for PEP 8 method compatibility.
|
|
971
|
+
"""
|
|
972
|
+
|
|
973
|
+
def test_for_value(self):
|
|
974
|
+
"""
|
|
975
|
+
L{Field.for_value} is the same as L{Field.forValue}.
|
|
976
|
+
"""
|
|
977
|
+
self.assertEqual(Field.for_value, Field.forValue)
|
|
978
|
+
|
|
979
|
+
def test_for_types(self):
|
|
980
|
+
"""
|
|
981
|
+
L{Field.for_types} is the same as L{Field.forTypes}.
|
|
982
|
+
"""
|
|
983
|
+
self.assertEqual(Field.for_types, Field.forTypes)
|
|
984
|
+
|
|
985
|
+
def test_as_task(self):
|
|
986
|
+
"""
|
|
987
|
+
L{ActionType.as_task} is the same as L{ActionType.asTask}.
|
|
988
|
+
"""
|
|
989
|
+
self.assertEqual(ActionType.as_task, ActionType.asTask)
|