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