def validate_groupingEnhancements(self, attrs, source): if not attrs[source]: return attrs try: Enhancements.from_config_string(attrs[source]) except InvalidEnhancerConfig as e: raise serializers.ValidationError(e.message) return attrs
def validate_groupingEnhancements(self, value): if not value: return value try: Enhancements.from_config_string(value) except InvalidEnhancerConfig as e: raise serializers.ValidationError(str(e)) return value
def _get_project_enhancements_config(project): enhancements = project.get_option("sentry:grouping_enhancements") config_id = project.get_option("sentry:grouping_config", validate=lambda x: x in CONFIGURATIONS) enhancements_base = CONFIGURATIONS[config_id].enhancements_base # Instead of parsing and dumping out config here, we can make a # shortcut from sentry.utils.cache import cache from sentry.utils.hashlib import md5_text cache_key = ("grouping-enhancements:" + md5_text(f"{enhancements_base}|{enhancements}").hexdigest()) rv = cache.get(cache_key) if rv is not None: return rv try: rv = Enhancements.from_config_string(enhancements, bases=[enhancements_base ]).dumps() except InvalidEnhancerConfig: rv = get_default_enhancements() cache.set(cache_key, rv) return rv
def test_basic_path_matching(): enhancement = Enhancements.from_config_string(""" path:**/test.js +app """) js_rule = enhancement.rules[0] assert bool( js_rule.get_matching_frame_actions( { "abs_path": "http://example.com/foo/test.js", "filename": "/foo/test.js" }, "javascript")) assert not bool( js_rule.get_matching_frame_actions( { "abs_path": "http://example.com/foo/bar.js", "filename": "/foo/bar.js" }, "javascript")) assert bool( js_rule.get_matching_frame_actions( {"abs_path": "http://example.com/foo/test.js"}, "javascript")) assert not bool( js_rule.get_matching_frame_actions({"filename": "/foo/bar.js"}, "javascript")) assert bool( js_rule.get_matching_frame_actions( {"abs_path": "http://example.com/foo/TEST.js"}, "javascript")) assert not bool( js_rule.get_matching_frame_actions( {"abs_path": "http://example.com/foo/bar.js"}, "javascript"))
def test_range_matching(): enhancement = Enhancements.from_config_string(""" [ function:foo ] | function:* | [ function:baz ] category=bar """) (rule, ) = enhancement.rules assert (sorted( dict( rule.get_matching_frame_actions( [ { "function": "main" }, { "function": "foo" }, { "function": "bar" }, { "function": "baz" }, { "function": "abort" }, ], "python", ))) == [2])
def test_basic_path_matching(): enhancement = Enhancements.from_config_string(''' path:**/test.js +app ''') js_rule = enhancement.rules[0] assert bool(js_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/test.js', 'filename': '/foo/test.js', }, 'javascript')) assert not bool(js_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/bar.js', 'filename': '/foo/bar.js', }, 'javascript')) assert bool(js_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/test.js', }, 'javascript')) assert not bool(js_rule.get_matching_frame_actions({ 'filename': '/foo/bar.js', }, 'javascript')) assert bool(js_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/TEST.js', }, 'javascript')) assert not bool(js_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/bar.js', }, 'javascript'))
def test_app_matching(): enhancement = Enhancements.from_config_string(''' family:javascript path:**/test.js app:yes +app family:native path:**/test.c app:no -group ''') app_yes_rule, app_no_rule = enhancement.rules assert bool( app_yes_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/TEST.js', 'in_app': True }, 'javascript')) assert not bool( app_yes_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/TEST.js', 'in_app': False }, 'javascript')) assert bool( app_no_rule.get_matching_frame_actions( { 'abs_path': '/test.c', 'in_app': False }, 'native')) assert not bool( app_no_rule.get_matching_frame_actions( { 'abs_path': '/test.c', 'in_app': True }, 'native'))
def test_mechanism_matching(): enhancement = Enhancements.from_config_string(""" family:other error.mechanism:NSError -app """) (rule, ) = enhancement.rules assert not rule.get_matching_frame_actions([{"function": "foo"}], "python") assert not rule.get_matching_frame_actions([{ "function": "foo" }], "python", None) assert rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"mechanism": { "type": "NSError" }}) assert not rule.get_matching_frame_actions([{ "function": "foo" }], "native", {"mechanism": { "type": "NSError" }}) assert not rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"mechanism": { "type": "fooerror" }})
def create_event(self, grouping_config): grouping_input = dict(self.data) # Customize grouping config from the _grouping config grouping_info = grouping_input.pop("_grouping", None) or {} enhancement_base = grouping_info.get("enhancement_base") enhancements = grouping_info.get("enhancements") if enhancement_base or enhancements: enhancement_bases = [enhancement_base] if enhancement_base else [] e = Enhancements.from_config_string(enhancements or "", bases=enhancement_bases) grouping_config["enhancements"] = e.dumps() # Normalize the event mgr = EventManager(data=grouping_input, grouping_config=grouping_config) mgr.normalize() data = mgr.get_data() # Normalize the stacktrace for grouping. This normally happens in # save() normalize_stacktraces_for_grouping( data, load_grouping_config(grouping_config)) evt = eventstore.create_event(data=data) return evt
def _get_project_enhancements_config(project): enhancements = project.get_option("sentry:grouping_enhancements") enhancements_base = project.get_option( "sentry:grouping_enhancements_base", validate=lambda x: x in ENHANCEMENT_BASES) # Instead of parsing and dumping out config here, we can make a # shortcut from sentry.utils.cache import cache from sentry.utils.hashlib import md5_text cache_key = ("grouping-enhancements:" + md5_text("%s|%s" % (enhancements_base, enhancements)).hexdigest()) rv = cache.get(cache_key) if rv is not None: return rv try: rv = Enhancements.from_config_string(enhancements, bases=[enhancements_base ]).dumps() except InvalidEnhancerConfig: rv = get_default_enhancements() cache.set(cache_key, rv) return rv
def test_app_matching(): enhancement = Enhancements.from_config_string(""" family:javascript path:**/test.js app:yes +app family:native path:**/test.c app:no -group """) app_yes_rule, app_no_rule = enhancement.rules assert bool( app_yes_rule.get_matching_frame_actions([{ "abs_path": "http://example.com/foo/TEST.js", "in_app": True }], "javascript")) assert not bool( app_yes_rule.get_matching_frame_actions([{ "abs_path": "http://example.com/foo/TEST.js", "in_app": False }], "javascript")) assert bool( app_no_rule.get_matching_frame_actions([{ "abs_path": "/test.c", "in_app": False }], "native")) assert not bool( app_no_rule.get_matching_frame_actions([{ "abs_path": "/test.c", "in_app": True }], "native"))
def _get_project_enhancements_config(project): enhancements = project.get_option('sentry:grouping_enhancements') enhancements_base = project.get_option('sentry:grouping_enhancements_base') if not enhancements and not enhancements_base: return DEFAULT_ENHANCEMENTS_CONFIG if enhancements_base is None or enhancements_base not in ENHANCEMENT_BASES: enhancements_base = DEFAULT_ENHANCEMENT_BASE # Instead of parsing and dumping out config here, we can make a # shortcut from sentry.utils.cache import cache from sentry.utils.hashlib import md5_text cache_key = 'grouping-enhancements:' + \ md5_text('%s|%s' % (enhancements_base, enhancements)).hexdigest() rv = cache.get(cache_key) if rv is not None: return rv try: rv = Enhancements.from_config_string(enhancements or '', bases=[enhancements_base ]).dumps() except InvalidEnhancerConfig: rv = DEFAULT_ENHANCEMENTS_CONFIG cache.set(cache_key, rv) return rv
def _get_enhancements(self, project): enhancements = project.get_option("sentry:grouping_enhancements") config_id = self._get_config_id(project) enhancements_base = CONFIGURATIONS[config_id].enhancements_base # Instead of parsing and dumping out config here, we can make a # shortcut from sentry.utils.cache import cache from sentry.utils.hashlib import md5_text cache_prefix = self.cache_prefix cache_prefix += f"{LATEST_VERSION}:" cache_key = cache_prefix + md5_text( f"{enhancements_base}|{enhancements}").hexdigest() rv = cache.get(cache_key) if rv is not None: return rv try: rv = Enhancements.from_config_string(enhancements, bases=[enhancements_base ]).dumps() except InvalidEnhancerConfig: rv = get_default_enhancements() cache.set(cache_key, rv) return rv
def _get_project_enhancements_config(project): enhancements = project.get_option('sentry:grouping_enhancements') enhancements_base = project.get_option('sentry:grouping_enhancements_base') if not enhancements and not enhancements_base: return DEFAULT_ENHANCEMENTS_CONFIG if enhancements_base is None or enhancements_base not in ENHANCEMENT_BASES: enhancements_base = DEFAULT_ENHANCEMENT_BASE # Instead of parsing and dumping out config here, we can make a # shortcut from sentry.utils.cache import cache from sentry.utils.hashlib import md5_text cache_key = 'grouping-enhancements:' + \ md5_text('%s|%s' % (enhancements_base, enhancements)).hexdigest() rv = cache.get(cache_key) if rv is not None: return rv try: rv = Enhancements.from_config_string( enhancements or '', bases=[enhancements_base]).dumps() except InvalidEnhancerConfig: rv = DEFAULT_ENHANCEMENTS_CONFIG cache.set(cache_key, rv) return rv
def test_basic_parsing(insta_snapshot, version): enhancement = Enhancements.from_config_string( """ # This is a config path:*/code/game/whatever/* +app function:panic_handler ^-group -group function:ThreadStartWin32 v-group function:ThreadStartLinux v-group function:ThreadStartMac v-group family:native module:std::* -app module:core::* -app family:javascript path:*/test.js -app family:javascript app:1 path:*/test.js -app family:native max-frames=3 """, bases=["common:v1"], ) enhancement.version = version dumped = enhancement.dumps() insta_snapshot(dump_obj(enhancement)) assert Enhancements.loads(dumped).dumps() == dumped assert Enhancements.loads( dumped)._to_config_structure() == enhancement._to_config_structure() assert isinstance(dumped, str)
def test_package_matching(): # This tests a bunch of different rules from the default in-app logic that # was ported from the former native plugin. enhancement = Enhancements.from_config_string(""" family:native package:/var/**/Frameworks/** -app family:native package:**/*.app/Contents/** +app family:native package:linux-gate.so -app family:native package:?:/Windows/** -app """) bundled_rule, macos_rule, linux_rule, windows_rule = enhancement.rules assert bool( _get_matching_frame_actions( bundled_rule, [{ "package": "/var/containers/MyApp/Frameworks/libsomething" }], "native")) assert bool( _get_matching_frame_actions( macos_rule, [{ "package": "/Applications/MyStuff.app/Contents/MacOS/MyStuff" }], "native")) assert bool( _get_matching_frame_actions(linux_rule, [{ "package": "linux-gate.so" }], "native")) assert bool( _get_matching_frame_actions( windows_rule, [{ "package": "D:\\Windows\\System32\\kernel32.dll" }], "native")) assert bool( _get_matching_frame_actions( windows_rule, [{ "package": "d:\\windows\\System32\\kernel32.dll" }], "native")) assert not bool( _get_matching_frame_actions(bundled_rule, [{ "package": "/var2/containers/MyApp/Frameworks/libsomething" }], "native")) assert not bool( _get_matching_frame_actions( bundled_rule, [{ "package": "/var/containers/MyApp/MacOs/MyApp" }], "native")) assert not bool( _get_matching_frame_actions(bundled_rule, [{ "package": "/usr/lib/linux-gate.so" }], "native"))
def test_family_matching(): enhancement = Enhancements.from_config_string(''' family:javascript path:**/test.js +app family:native function:std::* -app ''') js_rule, native_rule = enhancement.rules assert bool( js_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/TEST.js', }, 'javascript')) assert not bool( js_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/TEST.js', }, 'native')) assert not bool( native_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/TEST.js', 'function': 'std::whatever', }, 'javascript')) assert bool( native_rule.get_matching_frame_actions({ 'function': 'std::whatever', }, 'native'))
def test_family_matching(): enhancement = Enhancements.from_config_string(""" family:javascript path:**/test.js +app family:native function:std::* -app """) js_rule, native_rule = enhancement.rules assert bool( js_rule.get_matching_frame_actions( [{ "abs_path": "http://example.com/foo/TEST.js" }], "javascript")) assert not bool( js_rule.get_matching_frame_actions( [{ "abs_path": "http://example.com/foo/TEST.js" }], "native")) assert not bool( native_rule.get_matching_frame_actions( [{ "abs_path": "http://example.com/foo/TEST.js", "function": "std::whatever" }], "javascript", )) assert bool( native_rule.get_matching_frame_actions([{ "function": "std::whatever" }], "native"))
def test_package_matching(): # This tests a bunch of different rules from the default in-app logic that # was ported from the former native plugin. enhancement = Enhancements.from_config_string(''' family:native package:/var/**/Frameworks/** -app family:native package:**/*.app/Contents/** +app family:native package:linux-gate.so -app family:native package:?:/Windows/** -app ''') bundled_rule, macos_rule, linux_rule, windows_rule = enhancement.rules assert bool( bundled_rule.get_matching_frame_actions( { 'package': '/var/containers/MyApp/Frameworks/libsomething', }, 'native')) assert bool( macos_rule.get_matching_frame_actions( { 'package': '/Applications/MyStuff.app/Contents/MacOS/MyStuff', }, 'native')) assert bool( linux_rule.get_matching_frame_actions({ 'package': 'linux-gate.so', }, 'native')) assert bool( windows_rule.get_matching_frame_actions( { 'package': 'D:\\Windows\\System32\\kernel32.dll', }, 'native')) assert bool( windows_rule.get_matching_frame_actions( { 'package': 'd:\\windows\\System32\\kernel32.dll', }, 'native')) assert not bool( bundled_rule.get_matching_frame_actions( { 'package': '/var2/containers/MyApp/Frameworks/libsomething', }, 'native')) assert not bool( bundled_rule.get_matching_frame_actions( { 'package': '/var/containers/MyApp/MacOs/MyApp', }, 'native')) assert not bool( bundled_rule.get_matching_frame_actions( { 'package': '/usr/lib/linux-gate.so', }, 'native'))
def test_grouping_reset(self): """ Regression test against a specific mutability bug involving grouping, stacktrace normalization and memoized interfaces """ event_data = { "exception": { "values": [ { "type": "Hello", "stacktrace": { "frames": [ { "function": "foo", }, { "function": "bar", }, ] }, } ] }, } enhancement = Enhancements.from_config_string( """ function:foo category=foo_like category:foo_like -group """, ) grouping_config = { "enhancements": enhancement.dumps(), "id": "mobile:2021-02-12", } event1 = Event( event_id="a" * 32, data=event_data, project_id=self.project.id, ) variants1 = event1.get_grouping_variants(grouping_config, normalize_stacktraces=True) event2 = Event( event_id="b" * 32, data=event_data, project_id=self.project.id, ) event2.interfaces # Populate cache variants2 = event2.get_grouping_variants(grouping_config, normalize_stacktraces=True) assert sorted(v.as_dict()["hash"] for v in variants1.values()) == sorted( v.as_dict()["hash"] for v in variants2.values() )
def test_range_matching_direct(): enhancement = Enhancements.from_config_string(""" function:bar | [ function:baz ] -group """) (rule, ) = enhancement.rules assert (sorted( dict( _get_matching_frame_actions( rule, [ { "function": "main" }, { "function": "foo" }, { "function": "bar" }, { "function": "baz" }, { "function": "abort" }, ], "python", ))) == [2]) assert not _get_matching_frame_actions( rule, [ { "function": "main" }, { "function": "foo" }, { "function": "bar" }, { "function": "abort" }, { "function": "baz" }, ], "python", )
def test_basic_parsing(insta_snapshot): enhancement = Enhancements.from_config_string(''' # This is a config path:*/code/game/whatever/* +app function:panic_handler ^-group -group function:ThreadStartWin32 v-group function:ThreadStartLinux v-group function:ThreadStartMac v-group family:native module:std::* -app module:core::* -app family:javascript path:*/test.js -app family:javascript app:1 path:*/test.js -app ''', bases=['common:v1']) dumped = enhancement.dumps() insta_snapshot(dump_obj(enhancement)) assert Enhancements.loads(dumped).dumps() == dumped assert Enhancements.loads(dumped)._to_config_structure() == enhancement._to_config_structure() assert isinstance(dumped, six.string_types)
def test_package_matching(): # This tests a bunch of different rules from the default in-app logic that # was ported from the former native plugin. enhancement = Enhancements.from_config_string(''' family:native package:/var/**/Frameworks/** -app family:native package:**/*.app/Contents/** +app family:native package:linux-gate.so -app family:native package:?:/Windows/** -app ''') bundled_rule, macos_rule, linux_rule, windows_rule = enhancement.rules assert bool(bundled_rule.get_matching_frame_actions({ 'package': '/var/containers/MyApp/Frameworks/libsomething', }, 'native')) assert bool(macos_rule.get_matching_frame_actions({ 'package': '/Applications/MyStuff.app/Contents/MacOS/MyStuff', }, 'native')) assert bool(linux_rule.get_matching_frame_actions({ 'package': 'linux-gate.so', }, 'native')) assert bool(windows_rule.get_matching_frame_actions({ 'package': 'D:\\Windows\\System32\\kernel32.dll', }, 'native')) assert bool(windows_rule.get_matching_frame_actions({ 'package': 'd:\\windows\\System32\\kernel32.dll', }, 'native')) assert not bool(bundled_rule.get_matching_frame_actions({ 'package': '/var2/containers/MyApp/Frameworks/libsomething', }, 'native')) assert not bool(bundled_rule.get_matching_frame_actions({ 'package': '/var/containers/MyApp/MacOs/MyApp', }, 'native')) assert not bool(bundled_rule.get_matching_frame_actions({ 'package': '/usr/lib/linux-gate.so', }, 'native'))
def test_event_hash_variant(insta_snapshot, config_name, test_name, log): with open(os.path.join(_fixture_path, test_name + ".json")) as f: input = json.load(f) # Customize grouping config from the _grouping config grouping_config = get_default_grouping_config_dict(config_name) grouping_info = input.pop("_grouping", None) or {} enhancement_base = grouping_info.get("enhancement_base") enhancements = grouping_info.get("enhancements") if enhancement_base or enhancements: enhancement_bases = [enhancement_base] if enhancement_base else [] e = Enhancements.from_config_string(enhancements or "", bases=enhancement_bases) grouping_config["enhancements"] = e.dumps() # Normalize the event mgr = EventManager(data=input, grouping_config=grouping_config) mgr.normalize() data = mgr.get_data() # Normalize the stacktrace for grouping. This normally happens in # save() normalize_stacktraces_for_grouping(data, load_grouping_config(grouping_config)) evt = eventstore.create_event(data=data) # Make sure we don't need to touch the DB here because this would # break stuff later on. evt.project = None rv = [] for (key, value) in sorted(evt.get_grouping_variants().items()): if rv: rv.append("-" * 74) rv.append("%s:" % key) dump_variant(value, rv, 1) output = "\n".join(rv) log(repr(evt.get_hashes())) assert evt.get_grouping_config() == grouping_config insta_snapshot(output)
def test_basic_path_matching(): enhancement = Enhancements.from_config_string(''' path:**/test.js +app ''') js_rule = enhancement.rules[0] assert bool( js_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/test.js', 'filename': '/foo/test.js', }, 'javascript')) assert not bool( js_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/bar.js', 'filename': '/foo/bar.js', }, 'javascript')) assert bool( js_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/test.js', }, 'javascript')) assert not bool( js_rule.get_matching_frame_actions({ 'filename': '/foo/bar.js', }, 'javascript')) assert bool( js_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/TEST.js', }, 'javascript')) assert not bool( js_rule.get_matching_frame_actions( { 'abs_path': 'http://example.com/foo/bar.js', }, 'javascript'))
def test_basic_parsing(insta_snapshot): enhancement = Enhancements.from_config_string(''' # This is a config path:*/code/game/whatever/* +app function:panic_handler ^-group -group function:ThreadStartWin32 v-group function:ThreadStartLinux v-group function:ThreadStartMac v-group family:native module:std::* -app module:core::* -app family:javascript path:*/test.js -app ''', bases=['common:v1']) dumped = enhancement.dumps() insta_snapshot(dump_obj(enhancement)) assert Enhancements.loads(dumped).dumps() == dumped assert Enhancements.loads( dumped)._to_config_structure() == enhancement._to_config_structure() assert isinstance(dumped, six.string_types)
def test_family_matching(): enhancement = Enhancements.from_config_string(''' family:javascript path:**/test.js +app family:native function:std::* -app ''') js_rule, native_rule = enhancement.rules assert bool(js_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/TEST.js', }, 'javascript')) assert not bool(js_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/TEST.js', }, 'native')) assert not bool(native_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/TEST.js', 'function': 'std::whatever', }, 'javascript')) assert bool(native_rule.get_matching_frame_actions({ 'function': 'std::whatever', }, 'native'))
def test_value_matching(): enhancement = Enhancements.from_config_string(""" family:other error.value:foo -app family:other error.value:Failed* -app """) foo_rule, failed_rule = enhancement.rules assert not foo_rule.get_matching_frame_actions([{ "function": "foo" }], "python") assert not foo_rule.get_matching_frame_actions([{ "function": "foo" }], "python", None) assert not failed_rule.get_matching_frame_actions([{ "function": "foo" }], "python") assert not failed_rule.get_matching_frame_actions([{ "function": "foo" }], "python", None) assert foo_rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"value": "foo"}) assert not foo_rule.get_matching_frame_actions( [{ "function": "foo" }], "native", {"value": "Failed to download"}) assert not failed_rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"value": "foo"}) assert failed_rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"value": "Failed to download"})
def test_app_matching(): enhancement = Enhancements.from_config_string(''' family:javascript path:**/test.js app:yes +app family:native path:**/test.c app:no -group ''') app_yes_rule, app_no_rule = enhancement.rules assert bool(app_yes_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/TEST.js', 'in_app': True }, 'javascript')) assert not bool(app_yes_rule.get_matching_frame_actions({ 'abs_path': 'http://example.com/foo/TEST.js', 'in_app': False }, 'javascript')) assert bool(app_no_rule.get_matching_frame_actions({ 'abs_path': '/test.c', 'in_app': False }, 'native')) assert not bool(app_no_rule.get_matching_frame_actions({ 'abs_path': '/test.c', 'in_app': True }, 'native'))
def test_type_matching(): enhancement = Enhancements.from_config_string(""" family:other error.type:ZeroDivisionError -app family:other error.type:*Error -app """) zero_rule, error_rule = enhancement.rules assert not zero_rule.get_matching_frame_actions([{ "function": "foo" }], "python") assert not zero_rule.get_matching_frame_actions([{ "function": "foo" }], "python", None) assert not error_rule.get_matching_frame_actions([{ "function": "foo" }], "python") assert not error_rule.get_matching_frame_actions([{ "function": "foo" }], "python", None) assert zero_rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"type": "ZeroDivisionError"}) assert not zero_rule.get_matching_frame_actions([{ "function": "foo" }], "native", {"type": "FooError"}) assert error_rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"type": "ZeroDivisionError"}) assert error_rule.get_matching_frame_actions([{ "function": "foo" }], "python", {"type": "FooError"})
def test_callee_recursion(): # Remove this test when CalleeMatch can be applied recursively with pytest.raises(InvalidEnhancerConfig): Enhancements.from_config_string( " category:foo | [ category:bar ] | [ category:baz ] +app")
def test_parsing_errors(): with pytest.raises(InvalidEnhancerConfig): Enhancements.from_config_string("invalid.message:foo -> bar")