def test_recipe_as_remotesettings_record(self, mocked_autograph): """Test that recipes are serialized as expected by our clients.""" recipe = RecipeFactory(name="Test", approver=UserFactory(), enabler=UserFactory(), signed=True) record = exports.recipe_as_record(recipe) assert record == { "id": str(recipe.id), "recipe": { "action": recipe.action.name, "arguments": recipe.arguments, "filter_expression": recipe.filter_expression, "id": recipe.id, "name": recipe.name, "revision_id": str(recipe.revision_id), }, "signature": { "public_key": Whatever.regex(r"[a-zA-Z0-9/+]{160}"), "signature": Whatever.regex(r"[a-f0-9]{40}"), "timestamp": Whatever.iso8601(), "x5u": Whatever.startswith("https://"), }, }
def test_it_works(self, rf): recipe = RecipeFactory(arguments={"foo": "bar"}) approval = ApprovalRequestFactory(revision=recipe.latest_revision) action = recipe.action serializer = RecipeSerializer(recipe, context={"request": rf.get("/")}) assert serializer.data == { "name": recipe.name, "id": recipe.id, "last_updated": Whatever(), "enabled": recipe.enabled, "extra_filter_expression": recipe.extra_filter_expression, "filter_expression": recipe.filter_expression, "revision_id": recipe.revision_id, "action": action.name, "arguments": { "foo": "bar" }, "is_approved": False, "latest_revision_id": recipe.latest_revision.id, "approved_revision_id": None, "approval_request": { "id": approval.id, "created": Whatever(), "creator": Whatever(), "approved": None, "approver": None, "comment": None, }, "identicon_seed": Whatever.startswith("v1:"), "capabilities": sorted(recipe.capabilities), }
def test_update_signatures(self, mocker, mock_logger): # Make sure the test environment is clean. This test is invalid otherwise. assert Recipe.objects.all().count() == 0 # Mock the Autographer mock_autograph = mocker.patch('normandy.recipes.models.Autographer') mock_autograph.return_value.sign_data.return_value = [ { 'signature': 'fake signature 1' }, { 'signature': 'fake signature 2' }, ] # Make and sign two recipes (recipe1, recipe2) = RecipeFactory.create_batch(2) Recipe.objects.all().update_signatures() # Assert that the signature update is logged. mock_logger.info.assert_called_with( Whatever.contains(str(recipe1.id), str(recipe2.id)), extra={ 'code': INFO_REQUESTING_RECIPE_SIGNATURES, 'recipe_ids': Whatever.contains(recipe1.id, recipe2.id) }) # Assert the autographer was used as expected assert mock_autograph.called assert mock_autograph.return_value.sign_data.called_with( [Whatever(), Whatever()]) signatures = list(Recipe.objects.all().values_list( 'signature__signature', flat=True)) assert signatures == ['fake signature 1', 'fake signature 2']
def test_update_signatures(self, mocker, mock_logger): # Make sure the test environment is clean. This test is invalid otherwise. assert Action.objects.all().count() == 0 # Mock the Autographer mock_autograph = mocker.patch('normandy.recipes.models.Autographer') mock_autograph.return_value.sign_data.return_value = [ { 'signature': 'fake signature 1' }, { 'signature': 'fake signature 2' }, ] # Make and sign two actions (action1, action2) = ActionFactory.create_batch(2) Action.objects.all().update_signatures() # Assert that the signature update is logged. mock_logger.info.assert_called_with( Whatever.contains(action1.name, action2.name), extra={ 'code': INFO_REQUESTING_ACTION_SIGNATURES, 'action_names': Whatever.contains(action1.name, action2.name), }) # Assert the autographer was used as expected assert mock_autograph.called assert mock_autograph.return_value.sign_data.called_with( [Whatever(), Whatever()]) signatures = list(Action.objects.all().values_list( 'signature__signature', flat=True)) assert signatures == ['fake signature 1', 'fake signature 2']
def test_forwards(self, migrations): # Get the pre-migration models old_apps = migrations.migrate("recipes", "0006_reciperevision_filter_object_json") Recipe = old_apps.get_model("recipes", "Recipe") Action = old_apps.get_model("recipes", "Action") RecipeRevision = old_apps.get_model("recipes", "RecipeRevision") Channel = old_apps.get_model("recipes", "Channel") Country = old_apps.get_model("recipes", "Country") Locale = old_apps.get_model("recipes", "Locale") # Create test data recipe = Recipe.objects.create() action = Action.objects.create() channel1 = Channel.objects.create(slug="beta") channel2 = Channel.objects.create(slug="release") country1 = Country.objects.create(code="US") country2 = Country.objects.create(code="CA") locale1 = Locale.objects.create(code="en-US") locale2 = Locale.objects.create(code="fr-CA") revision = RecipeRevision.objects.create( recipe=recipe, action=action, name="Test Revision", identicon_seed="v1:test" ) revision.channels.set([channel1, channel2]) revision.countries.set([country1, country2]) revision.locales.set([locale1, locale2]) revision.save() # Apply the migration new_apps = migrations.migrate("recipes", "0007_convert_simple_filters_to_filter_objects") # Get the post-migration models RecipeRevision = new_apps.get_model("recipes", "RecipeRevision") # Fetch the revision revision = RecipeRevision.objects.get() revision.filter_object = json.loads(revision.filter_object_json) # All simple filters should be removed assert revision.channels.count() == 0 assert revision.countries.count() == 0 assert revision.locales.count() == 0 # Order and duplication don't matter, so index the filter by type, and # compare the inner values using sets assert len(revision.filter_object) == 3 filters_by_type = {f["type"]: f for f in revision.filter_object} assert filters_by_type["channel"] == { "type": "channel", "channels": Whatever(lambda v: set(v) == {channel1.slug, channel2.slug}), } assert filters_by_type["country"] == { "type": "country", "countries": Whatever(lambda v: set(v) == {country1.code, country2.code}), } assert filters_by_type["locale"] == { "type": "locale", "locales": Whatever(lambda v: set(v) == {locale1.code, locale2.code}), }
def test_it_works(self): signature = SignatureFactory() serializer = SignatureSerializer(instance=signature) assert serializer.data == { "signature": Whatever.regex(r"[a-f0-9]{40}"), "x5u": Whatever.startswith(signature.x5u), "timestamp": Whatever.iso8601(), "public_key": Whatever.regex(r"[a-zA-Z0-9/+]{160}"), }
def test_it_works(self): signature = SignatureFactory() serializer = SignatureSerializer(instance=signature) assert serializer.data == { "signature": Whatever.regex(r"[a-f0-9]{40}"), "x5u": Whatever.startswith(signature.x5u), "timestamp": Whatever.iso8601(), "public_key": Whatever.regex(r"[a-zA-Z0-9/+]{160}"), }
def test_it_works(self): signature = SignatureFactory() serializer = SignatureSerializer(instance=signature) assert serializer.data == { 'signature': Whatever.regex(r'[a-f0-9]{40}'), 'x5u': Whatever.startswith(signature.x5u), 'timestamp': Whatever.iso8601(), 'public_key': Whatever.regex(r'[a-zA-Z0-9/+]{160}') }
def test_update_signature(self, mock_logger, mocked_autograph): recipe = RecipeFactory(enabler=UserFactory(), approver=UserFactory()) recipe.signature = None recipe.update_signature() mock_logger.info.assert_called_with( Whatever.contains(str(recipe.id)), extra={"code": INFO_REQUESTING_RECIPE_SIGNATURES, "recipe_ids": [recipe.id]}, ) mocked_autograph.return_value.sign_data.assert_called_with( [Whatever(lambda s: json.loads(s)["id"] == recipe.id)] ) assert recipe.signature is not None
def test_bundle_serializer(rf): recipe = RecipeFactory() bundle = BundleFactory(recipes=[recipe]) serializer = BundleSerializer(bundle, context={'request': rf.get('/')}) assert serializer.data['recipes'] == [{ 'name': recipe.name, 'id': recipe.id, 'revision_id': recipe.revision_id, 'action': Whatever(), 'arguments': Whatever(), }]
def test_it_works(self): author = UserFactory() reviewer = UserFactory() recipe = RecipeFactory(name="first", user=author) revision1 = recipe.latest_revision approval_request = revision1.request_approval(author) approval_request.approve(reviewer, "r+") revision1.enable(author) state1 = revision1.enabled_state assert state1 is not None recipe.revise("second", user=author) revision2 = recipe.latest_revision approval_request = revision2.request_approval(author) approval_request.approve(reviewer, "r+") state2 = revision2.enabled_state assert state2 is not None serializer = EnabledStateSerializer(state1) assert serializer.data == { "id": state1.id, "carryover_from": None, "created": Whatever.iso8601(), "creator": { "id": author.id, "first_name": author.first_name, "last_name": author.last_name, "email": author.email, }, "enabled": True, "revision_id": revision1.id, } serializer = EnabledStateSerializer(state2) assert serializer.data == { "id": state2.id, "carryover_from": state1.id, "created": Whatever.iso8601(), "creator": { "id": reviewer.id, "first_name": reviewer.first_name, "last_name": reviewer.last_name, "email": reviewer.email, }, "enabled": True, "revision_id": revision2.id, }
def test_it_works(self): author = UserFactory() reviewer = UserFactory() recipe = RecipeFactory(name="first", user=author) revision1 = recipe.latest_revision approval_request = revision1.request_approval(author) approval_request.approve(reviewer, "r+") revision1.enable(author) state1 = revision1.enabled_state assert state1 is not None recipe.revise("second", user=author) revision2 = recipe.latest_revision approval_request = revision2.request_approval(author) approval_request.approve(reviewer, "r+") state2 = revision2.enabled_state assert state2 is not None serializer = EnabledStateSerializer(state1) assert serializer.data == { "id": state1.id, "carryover_from": None, "created": Whatever.iso8601(), "creator": { "id": author.id, "first_name": author.first_name, "last_name": author.last_name, "email": author.email, }, "enabled": True, "revision_id": revision1.id, } serializer = EnabledStateSerializer(state2) assert serializer.data == { "id": state2.id, "carryover_from": state1.id, "created": Whatever.iso8601(), "creator": { "id": reviewer.id, "first_name": reviewer.first_name, "last_name": reviewer.last_name, "email": reviewer.email, }, "enabled": True, "revision_id": revision2.id, }
def test_it_logs_when_geoip_fails(self, geolocation, mocker, mock_logger): mock_reader = mocker.patch('normandy.recipes.geolocation.geoip_reader') mock_reader.country.side_effect = GeoIP2Error() assert geolocation.get_country_code('207.126.102.129') is None mock_logger.warning.assert_called_with( Whatever(), extra={'code': WARNING_UNKNOWN_GEOIP_ERROR})
def test_it_warns_when_cant_load_database(self, mocker, mock_logger): MockReader = mocker.patch('normandy.recipes.geolocation.Reader') MockReader.side_effect = IOError() load_geoip_database() mock_logger.warning.assert_called_with( Whatever(), extra={'code': WARNING_CANNOT_LOAD_DATABASE})
def test_it_works_with_no_signature(self, rf): recipe = RecipeFactory(signed=False) action = recipe.action serializer = SignedRecipeSerializer(instance=recipe, context={'request': rf.get('/')}) assert serializer.data == { 'signature': None, 'recipe': { 'name': recipe.name, 'id': recipe.id, 'enabled': recipe.enabled, 'extra_filter_expression': recipe.extra_filter_expression, 'filter_expression': recipe.filter_expression, 'revision_id': recipe.revision_id, 'action': action.name, 'arguments': recipe.arguments, 'last_updated': Whatever(), 'channels': [], 'countries': [], 'locales': [], 'is_approved': False, 'latest_revision_id': recipe.latest_revision.id, 'approved_revision_id': recipe.approved_revision_id, 'approval_request': None, } }
def test_it_serves_recipes(self, client): recipe = RecipeFactory(arguments={'foo': 'bar'}) data = ClientParametersFactory() res = client.post('/api/v1/fetch_bundle/', data) action = recipe.action impl_url = reverse('recipes:action-implementation', kwargs={ 'name': action.name, 'impl_hash': action.implementation_hash, }) assert res.status_code == 200 assert res.data['recipes'] == [{ 'name': recipe.name, 'id': recipe.id, 'revision_id': recipe.revision_id, 'action': { 'name': action.name, 'implementation_url': Whatever.endswith(impl_url), 'arguments_schema': action.arguments_schema, }, 'arguments': { 'foo': 'bar', }, }]
def test_it_works(self, rf): recipe = RecipeFactory(arguments={"foo": "bar"}) approval = ApprovalRequestFactory(revision=recipe.latest_revision) action = recipe.action serializer = RecipeSerializer(recipe, context={"request": rf.get("/")}) assert serializer.data == { "name": recipe.name, "id": recipe.id, "last_updated": Whatever(), "enabled": recipe.enabled, "extra_filter_expression": recipe.extra_filter_expression, "filter_expression": recipe.filter_expression, "revision_id": recipe.revision_id, "action": action.name, "arguments": {"foo": "bar"}, "is_approved": False, "latest_revision_id": recipe.latest_revision.id, "approved_revision_id": None, "approval_request": { "id": approval.id, "created": Whatever(), "creator": Whatever(), "approved": None, "approver": None, "comment": None, }, "identicon_seed": Whatever.startswith("v1:"), }
def test_it_works_with_signature(self, rf): recipe = RecipeFactory(approver=UserFactory(), signed=True) context = {"request": rf.get("/")} combined_serializer = SignedRecipeSerializer(instance=recipe, context=context) recipe_serializer = MinimalRecipeSerializer(instance=recipe, context=context) # Testing for shape of data, not contents assert combined_serializer.data == { "signature": { "signature": Whatever(), "timestamp": Whatever(), "x5u": Whatever(), "public_key": Whatever(), }, "recipe": recipe_serializer.data, }
def test_it_works(self, rf): channel = ChannelFactory() country = CountryFactory() locale = LocaleFactory() recipe = RecipeFactory(arguments={'foo': 'bar'}, channels=[channel], countries=[country], locales=[locale]) action = recipe.action serializer = RecipeSerializer(recipe, context={'request': rf.get('/')}) assert serializer.data == { 'name': recipe.name, 'id': recipe.id, 'last_updated': Whatever(), 'enabled': recipe.enabled, 'extra_filter_expression': recipe.extra_filter_expression, 'filter_expression': recipe.filter_expression, 'revision_id': recipe.revision_id, 'action': action.name, 'arguments': { 'foo': 'bar', }, 'channels': [channel.slug], 'countries': [country.code], 'locales': [locale.code] }
def test_it_doesnt_pass_the_api_root_url_to_the_api_root_view( self, mocker): mock_api_view = mocker.Mock() router = MixedViewRouter(view=mock_api_view) router.register_view('view', View, name='standalone-view') router.get_urls() assert mock_api_view.called_once_with( [Whatever(lambda v: v.name == 'standalone-view')])
def test_log_failure_no_username(self, mock_logger, mock_authenticate): mock_authenticate.return_value = None request = RequestFactory().get("/") user = LoggingModelBackend().authenticate(request, password="******") assert user is None mock_logger.warning.assert_called_with( Whatever.contains("no username provided"), extra={"code": WARNING_LOGIN_FAILURE} )
def test_it_works_with_signature(self, rf): recipe = RecipeFactory(signed=True) context = {'request': rf.get('/')} combined_serializer = SignedRecipeSerializer(instance=recipe, context=context) recipe_serializer = RecipeSerializer(instance=recipe, context=context) # Testing for shape of data, not contents assert combined_serializer.data == { 'signature': { 'signature': Whatever(), 'timestamp': Whatever(), 'x5u': Whatever(), 'public_key': Whatever(), }, 'recipe': recipe_serializer.data, }
def test_log_success(self, mock_logger, mock_authenticate): mock_authenticate.return_value = True user = LoggingModelBackend().authenticate(username='******', password='******') assert user mock_logger.info.assert_called_with( Whatever.contains('fakeuser'), extra={'code': INFO_LOGIN_SUCCESS} )
def test_log_failure_no_username(self, mock_logger, mock_authenticate): mock_authenticate.return_value = None user = LoggingModelBackend().authenticate(password='******') assert user is None mock_logger.warning.assert_called_with( Whatever.contains('no username provided'), extra={'code': WARNING_LOGIN_FAILURE} )
def test_log_success(self, mock_logger, mock_authenticate): mock_authenticate.return_value = True request = RequestFactory().get("/") user = LoggingModelBackend().authenticate(request, username="******", password="******") assert user mock_logger.info.assert_called_with(Whatever.contains("fakeuser"), extra={"code": INFO_LOGIN_SUCCESS})
def test_log_failure_no_username(self, mock_logger, mock_authenticate): mock_authenticate.return_value = None request = RequestFactory().get("/") user = LoggingModelBackend().authenticate(request, password="******") assert user is None mock_logger.warning.assert_called_with( Whatever.contains("no username provided"), extra={"code": WARNING_LOGIN_FAILURE})
def test_it_works(self, rf): channel = ChannelFactory() country = CountryFactory() locale = LocaleFactory() recipe = RecipeFactory(arguments={'foo': 'bar'}, channels=[channel], countries=[country], locales=[locale]) approval = ApprovalRequestFactory(revision=recipe.latest_revision) action = recipe.action serializer = RecipeSerializer(recipe, context={'request': rf.get('/')}) assert serializer.data == { 'name': recipe.name, 'id': recipe.id, 'last_updated': Whatever(), 'enabled': recipe.enabled, 'extra_filter_expression': recipe.extra_filter_expression, 'filter_expression': recipe.filter_expression, 'action': { 'arguments_schema': {}, 'id': action.id, 'implementation_url': Whatever(), 'name': action.name, }, 'arguments': { 'foo': 'bar', }, 'channels': [channel.slug], 'countries': [country.code], 'locales': [locale.code], 'is_approved': False, 'latest_revision': RecipeRevisionSerializer(recipe.latest_revision).data, 'approved_revision': None, 'approval_request': { 'id': approval.id, 'created': Whatever(), 'creator': Whatever(), 'approved': None, 'approver': None, 'comment': None, }, }
def test_log_success(self, mock_logger, mock_authenticate): mock_authenticate.return_value = True request = RequestFactory().get("/") user = LoggingModelBackend().authenticate( request, username="******", password="******" ) assert user mock_logger.info.assert_called_with( Whatever.contains("fakeuser"), extra={"code": INFO_LOGIN_SUCCESS} )
def test_it_serves_extensions(self, api_client, storage): extension = ExtensionFactory(name="foo") res = api_client.get("/api/v3/extension/") assert res.status_code == 200 assert res.data["results"] == [{ "id": extension.id, "name": "foo", "xpi": Whatever() }]
def test_it_serves_extensions(self, api_client): extension = ExtensionFactory(name='foo', ) res = api_client.get('/api/v2/extension/') assert res.status_code == 200 assert res.data['results'] == [{ 'id': extension.id, 'name': 'foo', 'xpi': Whatever(), }]
def test_update_locales(self, tmpdir, mock_logger): assert Locale.objects.count() == 0 storage = ProductDetailsRelationalStorage(json_dir=tmpdir.strpath) storage.update('languages.json', LANGUAGES_JSON, '1999-01-01') mock_logger.info.assert_called_with( Whatever(), extra={'code': INFO_UPDATE_PRODUCT_DETAILS}) assert Locale.objects.count() == 12 assert Locale.objects.filter(code='en-US', name='English (US)').exists()
def test_password_not_queryable(self, gql_client): u = UserFactory() res = gql_client.execute(GQ().query.user(id=u.id).fields("password")) assert res == { "errors": [{ "locations": Whatever(), "message": 'Cannot query field "password" on type "UserType".', }] }
def test_update_signature(self, mock_logger, mocked_autograph): recipe = RecipeFactory(enabler=UserFactory(), approver=UserFactory()) recipe.signature = None recipe.update_signature() mock_logger.info.assert_called_with( Whatever.contains(str(recipe.id)), extra={"code": INFO_REQUESTING_RECIPE_SIGNATURES, "recipe_ids": [recipe.id]}, ) mocked_autograph.return_value.sign_data.assert_called_with( [Whatever(lambda s: json.loads(s)["id"] == recipe.id)] ) assert recipe.signature is not None
def test_it_does_not_send_excessive_remote_settings_traffic( self, mocker, settings, mocked_autograph ): # 10 to update recipes = RecipeFactory.create_batch( 10, approver=UserFactory(), enabler=UserFactory(), signed=False ) assert all(not r.approved_revision.uses_only_baseline_capabilities() for r in recipes) # Set up a version of the Remote Settings helper with a mocked out client client_mock = None def rs_with_mocked_client(): nonlocal client_mock assert client_mock is None rs = exports.RemoteSettings() client_mock = mocker.MagicMock() rs.client = client_mock return rs mocker.patch( "normandy.recipes.management.commands.update_recipe_signatures.RemoteSettings", side_effect=rs_with_mocked_client, ) call_command("update_recipe_signatures") # Make sure that our mock was actually used assert client_mock # One signing request to the capabilities collection assert client_mock.patch_collection.mock_calls == [ mocker.call( id=settings.REMOTE_SETTINGS_CAPABILITIES_COLLECTION_ID, data={"status": "to-sign"}, bucket=settings.REMOTE_SETTINGS_WORKSPACE_BUCKET_ID, ) ] # one publish to the capabilities collection per recipe expected_calls = [] for recipe in recipes: expected_calls.append( mocker.call( data=Whatever(lambda r: r["id"] == recipe.id, name=f"Recipe {recipe.id}"), bucket=settings.REMOTE_SETTINGS_WORKSPACE_BUCKET_ID, collection=settings.REMOTE_SETTINGS_CAPABILITIES_COLLECTION_ID, ) ) client_mock.update_record.has_calls(expected_calls, any_order=True) # all expected calls assert client_mock.update_record.call_count == len(expected_calls) # no extra calls
def test_it_works(self, rf): recipe = RecipeFactory(arguments={"foo": "bar"}, filter_object_json=None) approval = ApprovalRequestFactory(revision=recipe.latest_revision) action = recipe.action serializer = RecipeSerializer(recipe, context={"request": rf.get("/")}) assert serializer.data == { "name": recipe.name, "id": recipe.id, "last_updated": Whatever(), "enabled": recipe.enabled, "extra_filter_expression": recipe.extra_filter_expression, "filter_expression": recipe.filter_expression, "filter_object": [], "action": { "arguments_schema": {}, "id": action.id, "implementation_url": Whatever(), "name": action.name, }, "arguments": { "foo": "bar" }, "is_approved": False, "latest_revision": RecipeRevisionSerializer(recipe.latest_revision).data, "approved_revision": None, "approval_request": { "id": approval.id, "created": Whatever(), "creator": Whatever(), "approved": None, "approver": None, "comment": None, }, "identicon_seed": Whatever.startswith("v1:"), }
def test_update_signature(self, mocker, mock_logger): # Mock the Autographer mock_autograph = mocker.patch("normandy.recipes.models.Autographer") mock_autograph.return_value.sign_data.return_value = [{"signature": "fake signature"}] action = ActionFactory(signed=False) action.update_signature() mock_logger.info.assert_called_with( Whatever.contains(action.name), extra={"code": INFO_REQUESTING_ACTION_SIGNATURES, "action_names": [action.name]}, ) action.save() assert action.signature is not None assert action.signature.signature == "fake signature"
def test_update_signature(self, mocker, mock_logger): # Mock the Autographer mock_autograph = mocker.patch("normandy.recipes.models.Autographer") mock_autograph.return_value.sign_data.return_value = [{"signature": "fake signature"}] action = ActionFactory(signed=False) action.update_signature() mock_logger.info.assert_called_with( Whatever.contains(action.name), extra={"code": INFO_REQUESTING_ACTION_SIGNATURES, "action_names": [action.name]}, ) action.save() assert action.signature is not None assert action.signature.signature == "fake signature"
def test_it_serves_actions(self, api_client): action = ActionFactory( name="foo", implementation="foobar", arguments_schema={"type": "object"} ) res = api_client.get("/api/v1/action/") action_url = reverse( "recipes:v1:action-implementation", kwargs={"name": action.name, "impl_hash": action.implementation_hash}, ) assert res.status_code == 200 assert res.data == [ { "name": "foo", "implementation_url": Whatever.endswith(action_url), "arguments_schema": {"type": "object"}, } ]
def test_it_serves_actions(self, api_client): action = ActionFactory( name='foo', implementation='foobar', arguments_schema={'type': 'object'} ) res = api_client.get('/api/v1/action/') action_url = reverse('recipes:action-implementation', kwargs={ 'name': action.name, 'impl_hash': action.implementation_hash, }) assert res.status_code == 200 assert res.data == [ { 'name': 'foo', 'implementation_url': Whatever.endswith(action_url), 'arguments_schema': {'type': 'object'} } ]
def test_it_works(self, rf): recipe = RecipeFactory(arguments={'foo': 'bar'}) action = recipe.action serializer = RecipeSerializer(recipe, context={'request': rf.get('/')}) action_url = reverse('recipes:action-implementation', kwargs={ 'name': action.name, 'impl_hash': action.implementation_hash, }) assert serializer.data == { 'name': recipe.name, 'id': recipe.id, 'revision_id': recipe.revision_id, 'action': { 'name': action.name, 'implementation_url': Whatever.endswith(action_url), 'arguments_schema': action.arguments_schema, }, 'arguments': { 'foo': 'bar', } }
def test_it_serves_recipes(self, client): recipe = RecipeFactory(arguments={'foo': 'bar'}) data = ClientParametersFactory() res = client.post('/api/v1/fetch_bundle/', data) action = recipe.action impl_url = reverse('recipes:action-implementation', kwargs={ 'name': action.name, 'impl_hash': action.implementation_hash, }) assert res.status_code == 200 assert res.data['recipes'] == [{ 'name': recipe.name, 'id': recipe.id, 'revision_id': recipe.revision_id, 'action': { 'name': action.name, 'implementation_url': Whatever.endswith(impl_url), 'arguments_schema': action.arguments_schema, }, 'arguments': { 'foo': 'bar', }, }]
def test_it_interacts_with_autograph_correctly(self, settings, mock_logger): settings.AUTOGRAPH_URL = "https://autograph.example.com" settings.AUTOGRAPH_HAWK_ID = "hawk id" settings.AUTOGRAPH_HAWK_SECRET_KEY = "hawk secret key" autographer = signing.Autographer() autographer.session = MagicMock() autographer.session.post.return_value.json.return_value = [ { "content-signature": ( 'x5u="https://example.com/fake_x5u_1";p384ecdsa=fake_signature_1' ), "x5u": "https://example.com/fake_x5u_1", "hash_algorithm": "sha384", "ref": "fake_ref_1", "signature": "fake_signature_1", "public_key": "fake_pubkey_1", }, { "content-signature": ( 'x5u="https://example.com/fake_x5u_2";p384ecdsa=fake_signature_2' ), "x5u": "https://example.com/fake_x5u_2", "hash_algorithm": "sha384", "ref": "fake_ref_2", "signature": "fake_signature_2", "public_key": "fake_pubkey_2", }, ] url = self.test_settings["URL"] + "sign/data" foo_base64 = base64.b64encode(b"foo").decode("utf8") bar_base64 = base64.b64encode(b"bar").decode("utf8") # Assert the correct data is returned assert autographer.sign_data([b"foo", b"bar"]) == [ { "timestamp": Whatever(), "signature": "fake_signature_1", "x5u": "https://example.com/fake_x5u_1", "public_key": "fake_pubkey_1", }, { "timestamp": Whatever(), "signature": "fake_signature_2", "x5u": "https://example.com/fake_x5u_2", "public_key": "fake_pubkey_2", }, ] # Assert that logging happened mock_logger.info.assert_called_with( Whatever.contains("2"), extra={"code": signing.INFO_RECEIVED_SIGNATURES} ) # Assert the correct request was made assert autographer.session.post.called_once_with( [ url, [ {"template": "content-signature", "input": foo_base64}, {"template": "content-signature", "input": bar_base64}, ], ] )
def test_update_logging(self, mock_logger): recipe = RecipeFactory(name="my name") recipe.revise(name="my name", force=True) mock_logger.info.assert_called_with( Whatever.contains(str(recipe.id)), extra={"code": INFO_CREATE_REVISION} )