def test_create_invalid_decisioning_method(self): options = { "client": "clientId", "organization_id": "orgId", "decisioning_method": "bad decisions" } with self.assertRaises(Exception) as err: TargetClient.create(options) self.assertEqual( str(err.exception), "Invalid Decisioning Method. Must be set to one of: hybrid,on-device,server-side" )
def test_get_offers_server_side(self): expected_result = deepcopy(EXPECTED_PREFETCH_RESULT) expected_result["response"]["edgeHost"] = "mboxedge28.tt.omtrdc.net" expected_result["target_location_hint_cookie"] = { "name": "mboxEdgeCluster", "value": "28", "maxAge": 1860 } expected_result["meta"] = { "decisioning_method": DecisioningMethod.SERVER_SIDE.value } get_offers_opts = { "request": PREFETCH_REQUEST, "session_id": "dummy_session" } client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.SERVER_SIDE.value with patch.object(DeliveryApi, "execute", return_value=create_delivery_response(DELIVERY_API_RESPONSE)): client = TargetClient.create(client_opts) result = client.get_offers(get_offers_opts) result_dict = to_dict(result) expect_to_match_object(result_dict, expected_result)
def test_get_offers_async_error(self): setup_mock("invalid_request", responses, 400) shared = {"has_response": False} client_options = {"client": "bad", "organization_id": "bad"} client = TargetClient.create(client_options) def callback(): self.fail("Error callback should have been invoked") def err_callback(error): self.assertEqual(error.status, 400) self.assertEqual( json.loads(error.body), { "status": 400, "message": "Invalid request: Invalid imsOrg - bad" }) shared["has_response"] = True async_opts = deepcopy(self.get_offers_options) async_opts["request"] = create_delivery_request(async_opts["request"]) async_opts["callback"] = callback async_opts["err_callback"] = err_callback thread = client.get_offers(async_opts) try: thread.get( timeout=5) # Blocks current thread to keep test runner alive except multiprocessing.context.TimeoutError: self.fail("Test case timed out waiting for callback to be invoked") except delivery_api_client.exceptions.ApiException: pass # thread.get re-throws exception, but we"ve already handled inside err_callback self.assertTrue(shared.get("has_response"))
def test_get_offers_on_device_decisioning_emits_notifications(self): get_offers_opts = { "request": EXECUTE_REQUEST, "session_id": "dummy_session" } client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value client_opts["target_location_hint"] = "28" # send_notifications call with patch.object(DeliveryApi, "execute", return_value=create_delivery_response({})) \ as mock_delivery_api: with patch("target_decisioning_engine.artifact_provider.urllib3.PoolManager") as mock_artifact_provider: artifact_instance = mock_artifact_provider.return_value artifact_response = HTTPResponse(status=OK, body=json.dumps(ARTIFACT_AB_SIMPLE)) artifact_instance.request.return_value = artifact_response client = TargetClient.create(client_opts) self.assertEqual(mock_artifact_provider.call_count, 1) result = client.get_offers(get_offers_opts) self.assertIsNotNone(result.get("response")) self.assertEqual(result.get("response").status, OK) self.assertIsNotNone(result.get("response").execute) time.sleep(1) # notifications sent async self.assertEqual(mock_delivery_api.call_count, 1) notification_request = to_dict(mock_delivery_api.call_args[0][2]) expect_to_match_object(notification_request, EXPECTED_NOTIFICATION_REQUEST)
def test_get_offers_does_remote_request_if_hybrid_request_and_decisioning_engine_not_ready( self): get_offers_opts = { "request": TARGET_REQUEST_DICT, "session_id": "dummy_session", "decisioning_method": DecisioningMethod.HYBRID.value } get_offers_opts["request"]["prefetch"] = { "mboxes": [{ "name": "mbox-feature-flags", "index": 1 }] } get_offers_opts["request"] = create_delivery_request( get_offers_opts["request"]) client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.SERVER_SIDE.value with patch.object(DeliveryApi, "execute", return_value=create_delivery_response(DELIVERY_RESPONSE)) \ as mock_delivery_api: client = TargetClient.create(client_opts) result = client.get_offers(get_offers_opts) self.assertEqual(mock_delivery_api.call_count, 1) self.assertEqual(result["response"].status, OK) self.assertIsNotNone(result["response"].prefetch) self.assertEqual(result["meta"]["decisioning_method"], DecisioningMethod.SERVER_SIDE.value) self.assertEqual(result["meta"]["remote_mboxes"], []) self.assertEqual(result["meta"]["remote_views"], [])
def test_create_decisioning_method_server_side(self): client_ready_mock = Mock() client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.SERVER_SIDE.value client_opts["events"] = { "client_ready": client_ready_mock } client = TargetClient.create(client_opts) time.sleep(1) self.assertIsNotNone(client) self.assertIsNone(client.decisioning_engine) self.assertEqual(client_ready_mock.call_count, 1)
def test_get_offers_sync_error(self): setup_mock("invalid_request", responses, 400) client_options = {"client": "bad", "organization_id": "bad"} client = TargetClient.create(client_options) get_offers_opts = deepcopy(self.get_offers_options) get_offers_opts["request"] = create_delivery_request( get_offers_opts["request"]) with self.assertRaises( delivery_api_client.exceptions.ApiException) as err: client.get_offers(get_offers_opts) self.assertEqual(json.loads(err.exception.body), { "status": 400, "message": "Invalid request: Invalid imsOrg - bad" })
def setUp(self): client_options = { "client": "someClientId", "organization_id": "someOrgId" } self.get_attributes_options = { "request": { "context": {"channel": ChannelType.WEB}, "execute": { "mboxes": [{"name": "one"}, {"name": "two"}] }, "prefetch": { "mboxes": [{"name": "three"}, {"name": "four"}] } } } self.client = TargetClient.create(client_options)
def test_get_offers_hybrid_does_remote_request_if_necessary(self): get_offers_opts = { "request": TARGET_REQUEST_DICT, "session_id": "dummy_session" } get_offers_opts["request"]["prefetch"] = { "mboxes": [{ "name": "mbox-feature-flags", "index": 1 }, { "name": "remote-only-mbox-a", "index": 2 }, { "name": "remote-only-mbox-b", "index": 2 }] } get_offers_opts["request"] = create_delivery_request( get_offers_opts["request"]) client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.HYBRID.value with patch.object(DeliveryApi, "execute", return_value=create_delivery_response(DELIVERY_RESPONSE)) \ as mock_delivery_api: with patch( "target_decisioning_engine.artifact_provider.urllib3.PoolManager" ) as mock_artifact_provider: artifact_instance = mock_artifact_provider.return_value artifact_response = HTTPResponse( status=OK, body=json.dumps(FEATURE_FLAG_ARTIFACT)) artifact_instance.request.return_value = artifact_response client = TargetClient.create(client_opts) self.assertEqual(mock_artifact_provider.call_count, 1) result = client.get_offers(get_offers_opts) self.assertEqual(mock_delivery_api.call_count, 1) self.assertEqual(result["response"].status, OK) self.assertIsNotNone(result["response"].prefetch) self.assertEqual(result["meta"]["decisioning_method"], DecisioningMethod.HYBRID.value) self.assertSetEqual( set(result["meta"]["remote_mboxes"]), set(["remote-only-mbox-a", "remote-only-mbox-b"])) self.assertEqual(result["meta"]["remote_views"], [])
def setUp(self): client_options = { "client": "someClientId", "organization_id": "someOrgId" } self.get_attributes_options = { "request": { "id": { "tntId": "338e3c1e51f7416a8e1ccba4f81acea0.28_0", "marketingCloudVisitorId": "07327024324407615852294135870030620007" }, "context": { "channel": ChannelType.WEB, "mobilePlatform": None, "application": None, "screen": None, "window": None, "browser": None, "address": { "url": "http://adobe.com", "referringUrl": None }, "geo": None, "timeOffsetInMinutes": None, "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0", "beacon": False }, "prefetch": { "mboxes": [{ "name": "feature-flag-a", "index": 1 }] }, "execute": { "mboxes": [{ "index": 1, "name": "feature-flag-b" }] } } } self.client = TargetClient.create(client_options)
def test_create_decisioning_method_on_device(self): client_ready_mock = Mock() client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value client_opts["events"] = { "client_ready": client_ready_mock } with patch("target_decisioning_engine.artifact_provider.urllib3.PoolManager") as mock_pool_manager: instance = mock_pool_manager.return_value mock_response = HTTPResponse(status=OK, body=json.dumps(ARTIFACT_BLANK)) instance.request.return_value = mock_response with patch.object(DeliveryApi, "execute", return_value=create_delivery_response(LOCATION_HINT_RESPONSE)): client = TargetClient.create(client_opts) self.assertIsNotNone(client.decisioning_engine) self.assertTrue(callable(client.decisioning_engine.get_offers)) self.assertEqual(client_ready_mock.call_count, 1)
def test_get_offers_on_device_partial_returns_206_and_remote_mbox_names( self): get_offers_opts = { "request": TARGET_REQUEST_DICT, "session_id": "dummy_session" } get_offers_opts["request"]["prefetch"] = { "mboxes": [{ "name": "mbox-feature-flags", "index": 1 }, { "name": "remote-only-mbox-a", "index": 2 }, { "name": "remote-only-mbox-b", "index": 2 }] } get_offers_opts["request"] = create_delivery_request( get_offers_opts["request"]) client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value with patch( "target_decisioning_engine.artifact_provider.urllib3.PoolManager" ) as mock_artifact_provider: artifact_instance = mock_artifact_provider.return_value artifact_response = HTTPResponse( status=OK, body=json.dumps(FEATURE_FLAG_ARTIFACT)) artifact_instance.request.return_value = artifact_response client = TargetClient.create(client_opts) self.assertEqual(mock_artifact_provider.call_count, 1) result = client.get_offers(get_offers_opts) self.assertEqual(result["response"].status, PARTIAL_CONTENT) self.assertIsNotNone(result["response"].prefetch) self.assertEqual(result["meta"]["decisioning_method"], DecisioningMethod.ON_DEVICE.value) self.assertSetEqual( set(result["meta"]["remote_mboxes"]), set(["remote-only-mbox-a", "remote-only-mbox-b"])) self.assertEqual(result["meta"]["remote_views"], [])
def test_get_offers_valid_on_device_decisioning_response(self): get_offers_opts = { "request": PREFETCH_REQUEST, "session_id": "dummy_session" } client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value client_opts["target_location_hint"] = "28" with patch("target_decisioning_engine.artifact_provider.urllib3.PoolManager") as mock_artifact_provider: artifact_instance = mock_artifact_provider.return_value artifact_response = HTTPResponse(status=OK, body=json.dumps(ARTIFACT_AB_SIMPLE)) artifact_instance.request.return_value = artifact_response with patch.object(DeliveryApi, "execute", return_value=create_delivery_response(LOCATION_HINT_RESPONSE)): client = TargetClient.create(client_opts) result = client.get_offers(get_offers_opts) result_dict = to_dict(result) expect_to_match_object(result_dict, EXPECTED_PREFETCH_RESULT)
def setUp(self): client_options = get_client_options() self.send_notifications_options = { "request": { "id": { "tnt_id": "123" }, "context": { "channel": ChannelType.WEB }, "notifications": [{ "id": "SummerOfferNotification", "timestamp": 1611954531 * 1000, "type": "display", "mbox": { "name": "SummerOffer" }, "tokens": [ "GcvBXDhdJFNR9E9r1tgjfmqipfsIHvVzTQxHolz2IpSCnQ9Y9OaLL2gsdrWQTvE54PwSz67rmXWmSnkXpSSS2Q" ] }, { "id": "SummerShoesOfferNotification", "timestamp": 1611954531 * 1000, "type": "display", "mbox": { "name": "SummerShoesOffer" }, "tokens": [ "GcvBXDhdJFNR9E9r1tgjfmqipfsIHvVzTQxHolz2IpSCnQ9Y9OaLL2gsdrWQTvE54PwSz67rmXWmSnkXpSSS2Q" ] }] } } self.client = TargetClient.create(client_options)
def test_client_preemptively_fetches_target_location_hint(self): get_offers_opts = { "request": TARGET_REQUEST, "session_id": "dummy_session" } client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value with patch("target_decisioning_engine.artifact_provider.urllib3.PoolManager") as mock_artifact_provider: artifact_instance = mock_artifact_provider.return_value artifact_response = HTTPResponse(status=OK, body=json.dumps(ARTIFACT_AB_SIMPLE)) artifact_instance.request.return_value = artifact_response with patch.object(DeliveryApi, "execute", return_value=create_delivery_response(LOCATION_HINT_RESPONSE)): client = TargetClient.create(client_opts) result = client.get_offers(get_offers_opts) # on-device self.assertEqual(result.get("target_location_hint_cookie"), { "name": "mboxEdgeCluster", "value": "28", "maxAge": 1860 })
def test_client_recovers_if_location_hint_request_fails(self): get_offers_opts = { "request": TARGET_REQUEST, "session_id": "dummy_session" } client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value with patch("delivery_api_client.rest.urllib3.PoolManager") as mock_delivery_api: delivery_instance = mock_delivery_api.return_value delivery_response = HTTPResponse(status=503) delivery_instance.request.return_value = delivery_response with patch("target_decisioning_engine.artifact_provider.urllib3.PoolManager") as mock_artifact_provider: artifact_instance = mock_artifact_provider.return_value artifact_response = HTTPResponse(status=OK, body=json.dumps(ARTIFACT_AB_SIMPLE)) artifact_instance.request.return_value = artifact_response client = TargetClient.create(client_opts) result = client.get_offers(get_offers_opts) # on-device self.assertIsNone(result.get("target_location_hint_cookie"))
def test_client_uses_location_hint_from_config(self): get_offers_opts = { "request": TARGET_REQUEST, "session_id": "dummy_session" } client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value client_opts["target_location_hint"] = "28" with patch("target_decisioning_engine.artifact_provider.urllib3.PoolManager") as mock_artifact_provider: artifact_instance = mock_artifact_provider.return_value artifact_response = HTTPResponse(status=OK, body=json.dumps(ARTIFACT_AB_SIMPLE)) artifact_instance.request.return_value = artifact_response client = TargetClient.create(client_opts) result = client.get_offers(get_offers_opts) self.assertEqual(result.get("target_location_hint_cookie"), { "name": "mboxEdgeCluster", "value": "28", "maxAge": 1860 })
def test_get_offers_raises_exception_if_client_configured_as_server_side_but_request_made_with_on_device( self): get_offers_opts = { "request": TARGET_REQUEST_DICT, "session_id": "dummy_session", "decisioning_method": DecisioningMethod.ON_DEVICE.value } get_offers_opts["request"]["prefetch"] = { "mboxes": [{ "name": "mbox-feature-flags", "index": 1 }] } get_offers_opts["request"] = create_delivery_request( get_offers_opts["request"]) client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.SERVER_SIDE.value client = TargetClient.create(client_opts) with self.assertRaises(Exception) as err: client.get_offers(get_offers_opts) self.assertEqual(str(err.exception), DECISIONING_ENGINE_NOT_READY)
def test_get_offers_artifact_retrieval_failed(self): client_ready_mock = Mock() artifact_failed_mock = Mock() artifact_success_mock = Mock() client_opts = dict(CONFIG) client_opts["decisioning_method"] = DecisioningMethod.ON_DEVICE.value client_opts["events"] = { "client_ready": client_ready_mock, "artifact_download_failed": artifact_failed_mock, "artifact_download_succeeded": artifact_success_mock } with patch("target_decisioning_engine.artifact_provider.urllib3.PoolManager") as mock_pool_manager: instance = mock_pool_manager.return_value mock_response = HTTPResponse(status=403) instance.request.return_value = mock_response with patch.object(DeliveryApi, "execute", return_value=create_delivery_response(LOCATION_HINT_RESPONSE)): client = TargetClient.create(client_opts) self.assertEqual(client_ready_mock.call_count, 0) self.assertEqual(artifact_success_mock.call_count, 0) self.assertEqual(artifact_failed_mock.call_count, 1) self.assertEqual(artifact_failed_mock.call_args[0][0].get("type"), "artifact_download_failed") self.assertIsNotNone(artifact_failed_mock.call_args[0][0].get("artifact_location")) self.assertIsNotNone(artifact_failed_mock.call_args[0][0].get("error")) get_offers_opts = { "request": TARGET_REQUEST, "session_id": "dummy_session" } with self.assertRaises(Exception) as err: client.get_offers(get_offers_opts) self.assertEqual(str(err.exception), DECISIONING_ENGINE_NOT_READY)
def setUp(self): client_options = get_client_options() self.get_offers_options = { "request": { "id": { "tnt_id": "123" }, "execute": { "mboxes": [{ "name": "cart", "index": 1 }] }, "prefetch": { "mboxes": [{ "name": "homepage", "index": 1 }] }, "context": { "channel": ChannelType.WEB, "application": { "id": "123", "name": "unit testing python" }, "screen": { "width": 512, "height": 512, "color_depth": 100, "orientation": ScreenOrientationType.PORTRAIT }, "window": { "width": 512, "height": 512 }, "browser": { "host": "targetpythonsdk" }, "address": { "url": "http://www.targetpythonsdk.com" }, "geo": { "latitude": 38.8, "longitude": -77.0 } }, "property": { "token": "08b62abd-c3e7-dfb2-da93-96b3aa724d81" }, "experience_cloud": { "analytics": { "supplemental_data_id": "234234987325982342342349873259823", "tracking_server": "ags041.sc.omtrdc.net", "logging": "server_side" }, "audience_manager": { "location_hint": 9, "blob": "32fdghkjh34kj5h43" } } } } self.client = TargetClient.create(client_options)
def test_create_return_client(self): options = {"client": "clientId", "organization_id": "orgId"} client = TargetClient.create(options) self.assertIsNotNone(client)
def test_create_missing_organization_id(self): options = {"client": "clientId"} with self.assertRaises(Exception) as err: TargetClient.create(options) self.assertEqual(str(err.exception), "Organization Id is required")
def test_create_missing_options(self): with self.assertRaises(Exception) as err: TargetClient.create() self.assertEqual(str(err.exception), "Options are required")
def client_ready_callback(ready_event): print("Server is now ready to start handling requests") client_options = { "client": "acmeclient", "organization_id": "1234567890@AdobeOrg", "decisioning_method": "on-device", "events": { "client_ready": client_ready_callback } } target_client = TargetClient.create(client_options) target_service = TargetClientService(target_client) results = {} @app.route('/executePageLoad', methods=['GET']) def execute_page_load(): """ Make an execute pageload request to get all initial offers at page load time, get cookies for request if they exist, and on response set Target cookie for use with next request """ get_offers_options = initialize_options(request) target_delivery_response = target_service.get_page_load_target_delivery_response(request, get_offers_options) response = Response(target_delivery_response.get("response").to_str(), status=200, mimetype='application/json') set_target_cookies(response, target_delivery_response) return response