| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:test/test.dart'; | |
| import 'package:unified_analytics/src/asserts.dart'; | |
| void main() { | |
| test('Failure if client_id top level key is missing', () { | |
| final body = <String, Object?>{}; | |
| final expectedErrorMessage = 'client_id missing from top level keys'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure if events top level key is missing', () { | |
| final body = <String, Object?>{'client_id': 'xxxxxxx'}; | |
| final expectedErrorMessage = 'events missing from top level keys'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure if user_properties top level key is missing', () { | |
| final body = <String, Object?>{'client_id': 'xxxxxxx', 'events': []}; | |
| final expectedErrorMessage = 'user_properties missing from top level keys'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure if more than 25 events found in events list', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| // Add more than the 25 allowed events | |
| for (var i = 0; i < 30; i++) { | |
| (body['events'] as List).add({'name': i}); | |
| } | |
| final expectedErrorMessage = '25 is the max number of events'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure when event name is greater than 40 chars', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': | |
| 'hot_reload_timehot_reload_timehot_reload_timehot_reload_time', | |
| 'params': {'time_ms': 133, 'count': 1999000}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Limit event names to 40 chars or less\n' | |
| 'Event name: ' | |
| '"hot_reload_timehot_reload_timehot_reload_timehot_reload_time"' | |
| ' is too long'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure when event name has invalid chars', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time!!', | |
| 'params': {'time_ms': 133, 'count': 1999000}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Event name can only have alphanumeric chars and underscores\n' | |
| 'Event name: "hot_reload_time!!" contains invalid characters'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure when event name does not start with alphabetic char', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': '2hot_reload_time', | |
| 'params': {'time_ms': 133, 'count': 1999000}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Event name first char must be alphabetic char\n' | |
| 'Event name: "2hot_reload_time" must begin with a valid character'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure when an event has more than 25 event params', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| // 'params': {...} SUBBING THIS VALUE OUT 30 Maps | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final params = <String, Object?>{}; | |
| for (var i = 0; i < 30; i++) { | |
| params['$i'] = i; | |
| } | |
| // Add the params to the first event in the body | |
| ((body['events'] as List).first as Map)['params'] = params; | |
| final expectedErrorMessage = | |
| 'Limit params for each event to less than 25\n' | |
| 'Event: "hot_reload_time" has too many parameters'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test( | |
| 'Failure when an value for event params is not a supported type (list)', | |
| () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': { | |
| 'time_ms': 133, | |
| // Lists and Maps are not supported | |
| 'count': <int>[1999000], | |
| }, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Values for event params have to be String, int, double, or bool\n' | |
| 'Value for "count" is not a valid type for event: "hot_reload_time"'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }, | |
| ); | |
| test( | |
| 'Failure when an value for event params is not a supported type (map)', | |
| () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': { | |
| 'time_ms': 133, | |
| // Lists and Maps are not supported | |
| 'count': <int, int>{5: 20}, | |
| }, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Values for event params have to be String, int, double, or bool\n' | |
| 'Value for "count" is not a valid type for event: "hot_reload_time"'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }, | |
| ); | |
| test('Failure when event param name is more than 40 chars', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': {'time_mstime_mstime_mstime_mstime_mstime_ms': 133}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Limit event param names to 40 chars or less\n' | |
| 'The key: "time_mstime_mstime_mstime_mstime_mstime_ms" ' | |
| 'under the event: "hot_reload_time" is too long'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure for event param name that has invalid chars', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': {'time_ns!': 133}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Event param name can only have alphanumeric chars and underscores\n' | |
| 'The key: "time_ns!" under the event: "hot_reload_time" contains ' | |
| 'invalid characters'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test( | |
| 'Failure for event param name that does not start with an alphabetic char', | |
| () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': {'22time_ns': 133}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Event param name first char must be alphabetic char\n' | |
| 'The key: "22time_ns" under the event: "hot_reload_time" must begin ' | |
| 'in a valid character'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }, | |
| ); | |
| test('Failure for event param values that are greater than 100 chars', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': { | |
| 'time_ns': | |
| 'dsfjlksdjfajlfdsfjlks' | |
| 'djfajlfdsfjlksdjfajlfdsfjlksdjfaj' | |
| 'lfdsfjlksdjfajlfdsfjlksdjfajlfdsf' | |
| 'jlksdjfajlfdsfjlksdjfajlf', | |
| }, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| final expectedErrorMessage = | |
| 'Limit characters in event param value to 100 chars or less\n' | |
| 'Value for "time_ns" is too long, value="' | |
| 'dsfjlksdjfajlfdsfjlks' | |
| 'djfajlfdsfjlksdjfajlfdsfjlksdjfaj' | |
| 'lfdsfjlksdjfajlfdsfjlksdjfajlfdsf' | |
| 'jlksdjfajlfdsfjlksdjfajlf"'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure when body has more than 25 user properties', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': {'time_ns': 123}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{}, | |
| }; | |
| for (var i = 0; i < 30; i++) { | |
| (body['user_properties']! as Map<String, Object?>)['$i'] = i; | |
| } | |
| final expectedErrorMessage = 'Limit user properties to 25 or less'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure when user properties names are greater than 24 chars', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': {'time_ns': 123}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{ | |
| 'testtesttesttesttesttesttest': <String, Object?>{}, // TOO LONG | |
| }, | |
| }; | |
| final expectedErrorMessage = | |
| 'Limit user property names to 24 chars or less\n' | |
| 'The user property key: "testtesttesttesttesttesttest" is too long'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Failure when user properties values are greater than 36 chars', () { | |
| final body = <String, Object?>{ | |
| 'client_id': 'xxxxxxx', | |
| 'events': <Map<String, Object?>>[ | |
| { | |
| 'name': 'hot_reload_time', | |
| 'params': {'time_ns': 123}, | |
| }, | |
| ], | |
| 'user_properties': <String, Object?>{ | |
| 'test': <String, Object?>{ | |
| 'value': 'testtesttesttesttesttesttesttesttesttest', // TOO LONG | |
| }, | |
| }, | |
| }; | |
| final expectedErrorMessage = | |
| 'Limit user property values to 36 chars or less\n' | |
| 'For the user property key "test", the value ' | |
| '"testtesttesttesttesttesttesttesttesttest" is too long'; | |
| expect( | |
| () => checkBody(body), | |
| throwsA( | |
| predicate( | |
| (AnalyticsException e) => e.message == expectedErrorMessage, | |
| expectedErrorMessage, | |
| ), | |
| ), | |
| ); | |
| }); | |
| test('Successful body passes all asserts', () { | |
| final body = <String, Object?>{ | |
| 'client_id': '46cc0ba6-f604-4fd9-aa2f-8a20beb24cd4', | |
| 'events': [ | |
| { | |
| 'name': 'testing', | |
| 'params': {'time_ns': 345}, | |
| }, | |
| ], | |
| 'user_properties': { | |
| 'session_id': {'value': 1673466750423}, | |
| 'flutter_channel': {'value': 'ey-test-channel'}, | |
| 'host': {'value': 'macos'}, | |
| 'flutter_version': {'value': 'Flutter 3.6.0-7.0.pre.47'}, | |
| 'dart_version': {'value': 'Dart 2.19.0'}, | |
| 'tool': {'value': 'flutter-tools'}, | |
| 'local_time': {'value': '2023-01-11 14:53:31.471816 -0500'}, | |
| }, | |
| }; | |
| expect(() => checkBody(body), returnsNormally); | |
| }); | |
| } |