diff --git a/docker-compose.yml b/docker-compose.yml index 4aab988..95486a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,8 +2,8 @@ services: db: image: postgres:12-alpine environment: - - POSTGRES_USER=objecttypes - - POSTGRES_PASSWORD=objecttypes + POSTGRES_USER: objecttypes + POSTGRES_PASSWORD: objecttypes command: postgres -c max_connections=300 -c log_min_messages=LOG redis: diff --git a/docker/setup_configuration/data.yaml b/docker/setup_configuration/data.yaml index 8a2180a..a4586fb 100644 --- a/docker/setup_configuration/data.yaml +++ b/docker/setup_configuration/data.yaml @@ -9,7 +9,7 @@ objecttypes_tokens: application: Application 1 administration: Administration 1 -objecttypes_site_config_enable: true +objecttypes_sites_config_enable: true objecttypes_sites: items: - domain: example.com diff --git a/requirements/base.in b/requirements/base.in index 8e476de..657f868 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -7,7 +7,7 @@ furl # Framework libraries django-jsonsuit sharing-configs -django-setup-configuration +django-setup-configuration>=0.4.0 mozilla-django-oidc-db[django-setup-configuration] # API libraries diff --git a/src/objecttypes/setup_configuration/steps.py b/src/objecttypes/setup_configuration/steps.py index 6cbe2ef..363b9bc 100644 --- a/src/objecttypes/setup_configuration/steps.py +++ b/src/objecttypes/setup_configuration/steps.py @@ -20,7 +20,7 @@ class TokenAuthConfigurationStep( BaseConfigurationStep[TokenAuthGroupConfigurationModel] ): """ - Configure configuration groups for the Objects API backend + Configure tokens for other applications to access Objecttypes API """ namespace = "objecttypes_tokens" @@ -79,7 +79,7 @@ class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]) """ namespace = "objecttypes_sites" - enable_setting = "objecttypes_site_config_enable" + enable_setting = "objecttypes_sites_config_enable" verbose_name = "Configuration to set up Sites for ObjectTypes" config_model = SiteGroupConfigurationModel diff --git a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml index 1c377cf..52fa558 100644 --- a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml @@ -1,3 +1,3 @@ -objecttypes_site_config_enable: true +objecttypes_sites_config_enable: true objecttypes_sites: items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml index 9034cb6..1999a90 100644 --- a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml +++ b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml @@ -1,4 +1,4 @@ -objecttypes_site_config_enable: true +objecttypes_sites_config_enable: true objecttypes_sites: items: - domain: example-1.com diff --git a/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup.yaml diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py index 7a10bd2..170e0dd 100644 --- a/src/objecttypes/setup_configuration/tests/test_site_config.py +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -4,7 +4,7 @@ from django.test import TestCase from django_setup_configuration.exceptions import PrerequisiteFailed -from django_setup_configuration.test_utils import build_step_config_from_sources +from django_setup_configuration.test_utils import execute_single_step from objecttypes.setup_configuration.steps import SitesConfigurationStep @@ -13,78 +13,46 @@ class SitesConfigurationStepTests(TestCase): def test_valid_setup_default(self): - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") + self.assertTrue( + Site.objects.filter(domain="example.com", name="example.com").exists() + ) - setup_config = build_step_config_from_sources( - SitesConfigurationStep, - str(DIR_FILES / "valid_setup.yaml"), + execute_single_step( + SitesConfigurationStep, yaml_source=str(DIR_FILES / "valid_setup.yaml") ) - step = SitesConfigurationStep() - step.execute(setup_config) - sites = Site.objects.order_by("pk") + sites = Site.objects.all() self.assertEqual(sites.count(), 3) - - site = sites[1] - self.assertEqual(site.domain, "example-1.com") - self.assertEqual(site.name, "example-1") - - site = sites[2] - self.assertEqual(site.domain, "example-2.com") - self.assertEqual(site.name, "example-2") + self.assertTrue(sites.filter(domain="example-1.com", name="example-1").exists()) + self.assertTrue(sites.filter(domain="example-2.com", name="example-2").exists()) def test_valid_update_existing_sites(self): - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") + self.assertTrue( + Site.objects.filter(domain="example.com", name="example.com").exists() + ) Site.objects.create(domain="example-2.com", name="example-3") - sites = Site.objects.order_by("pk") - self.assertEqual(sites.count(), 2) + self.assertEqual(Site.objects.count(), 2) - setup_config = build_step_config_from_sources( - SitesConfigurationStep, - str(DIR_FILES / "valid_setup.yaml"), + execute_single_step( + SitesConfigurationStep, yaml_source=str(DIR_FILES / "valid_setup.yaml") ) - step = SitesConfigurationStep() - step.execute(setup_config) - sites = Site.objects.order_by("pk") + sites = Site.objects.all() self.assertEqual(sites.count(), 3) + self.assertTrue(sites.filter(domain="example-2.com", name="example-2").exists()) + self.assertTrue(sites.filter(domain="example-1.com", name="example-1").exists()) - site = sites[1] - self.assertEqual(site.domain, "example-2.com") - self.assertEqual(site.name, "example-2") - - site = sites[2] - self.assertEqual(site.domain, "example-1.com") - self.assertEqual(site.name, "example-1") - - def test_invalid_setup_empty(self): - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") + def test_invalid_setup(self): + self.assertTrue( + Site.objects.filter(domain="example.com", name="example.com").exists() + ) with self.assertRaises(PrerequisiteFailed) as command_error: - setup_config = build_step_config_from_sources( + execute_single_step( SitesConfigurationStep, - str(DIR_FILES / "invalid_setup.yaml"), + yaml_source=str(DIR_FILES / "invalid_setup.yaml"), ) - step = SitesConfigurationStep() - step.execute(setup_config) self.assertTrue("Input should be a valid list" in str(command_error.exception)) - - sites = Site.objects.order_by("pk") - site = sites[0] - self.assertEqual(sites.count(), 1) - self.assertEqual(site.domain, "example.com") - self.assertEqual(site.name, "example.com") + self.assertEqual(Site.objects.count(), 1) diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index f6e87d2..caee4ec 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -1,14 +1,12 @@ -from io import StringIO from pathlib import Path -from django.core.management import CommandError, call_command from django.test import TestCase from django_setup_configuration.exceptions import ( ConfigurationRunFailed, PrerequisiteFailed, ) -from django_setup_configuration.test_utils import build_step_config_from_sources +from django_setup_configuration.test_utils import execute_single_step from objecttypes.setup_configuration.steps import TokenAuthConfigurationStep from objecttypes.token.models import TokenAuth @@ -19,18 +17,15 @@ class TokenAuthConfigurationStepTests(TestCase): def test_valid_setup_default(self): - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_default.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_default.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) - token = tokens[0] - self.assertEqual(token.identifier, "token-1") + token = tokens.get(identifier="token-1") self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(token.contact_person, "Person 1") self.assertEqual(token.email, "person-1@example.com") @@ -38,8 +33,7 @@ def test_valid_setup_default(self): self.assertEqual(token.application, "") self.assertEqual(token.administration, "") - token = tokens[1] - self.assertEqual(token.identifier, "token-2") + token = tokens.get(identifier="token-2") self.assertEqual(token.contact_person, "Person 2") self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") self.assertEqual(token.email, "person-2@example.com") @@ -48,19 +42,16 @@ def test_valid_setup_default(self): self.assertEqual(token.administration, "") def test_valid_setup_complete(self): - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) # Same as configuration - token = tokens[0] - self.assertEqual(token.identifier, "token-1") + token = tokens.get(identifier="token-1") self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(token.contact_person, "Person 1") self.assertEqual(token.email, "person-1@example.com") @@ -69,8 +60,7 @@ def test_valid_setup_complete(self): self.assertEqual(token.administration, "Administration 1") # Token data updated - token = tokens[1] - self.assertEqual(token.identifier, "token-2") + token = tokens.get(identifier="token-2") self.assertEqual(token.contact_person, "Person 2") self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") self.assertEqual(token.email, "person-2@example.com") @@ -99,20 +89,16 @@ def test_valid_update_existing_tokens(self): contact_person="Person 3", email="person-3@example.com", ) - - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) # Same as configuration - token = tokens[0] - self.assertEqual(token.identifier, "token-1") + token = tokens.get(identifier="token-1") self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(token.contact_person, "Person 1") self.assertEqual(token.email, "person-1@example.com") @@ -121,8 +107,7 @@ def test_valid_update_existing_tokens(self): self.assertEqual(token.administration, "Administration 1") # Token data updated - token = tokens[1] - self.assertEqual(token.identifier, "token-2") + token = tokens.get(identifier="token-2") self.assertEqual(token.contact_person, "Person 2") self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") self.assertEqual(token.email, "person-2@example.com") @@ -135,17 +120,15 @@ def test_valid_update_existing_tokens(self): self.assertNotEqual(token.email, "person-3@example.com") def test_valid_idempotent_step(self): - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) - tokens = TokenAuth.objects.order_by("created") + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) - old_token_a = tokens[0] + old_token_a = tokens.get(identifier="token-1") self.assertEqual(old_token_a.identifier, "token-1") self.assertEqual(old_token_a.token, "18b2b74ef994314b84021d47b9422e82b685d82f") self.assertEqual(old_token_a.contact_person, "Person 1") @@ -154,7 +137,7 @@ def test_valid_idempotent_step(self): self.assertEqual(old_token_a.application, "Application 1") self.assertEqual(old_token_a.administration, "Administration 1") - old_token_b = tokens[1] + old_token_b = tokens.get(identifier="token-2") self.assertEqual(old_token_b.identifier, "token-2") self.assertEqual(old_token_b.contact_person, "Person 2") self.assertEqual(old_token_b.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") @@ -163,17 +146,15 @@ def test_valid_idempotent_step(self): self.assertEqual(old_token_b.application, "Application 2") self.assertEqual(old_token_b.administration, "Administration 2") - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "valid_setup_complete.yaml"), + yaml_source=str(DIR_FILES / "valid_setup_complete.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) + tokens = TokenAuth.objects.all() self.assertEqual(tokens.count(), 2) - tokens = TokenAuth.objects.order_by("created") - new_token_a = tokens[0] + new_token_a = tokens.get(identifier="token-1") self.assertEqual(new_token_a.identifier, old_token_a.identifier) self.assertEqual(new_token_a.token, old_token_a.token) self.assertEqual(new_token_a.contact_person, old_token_a.contact_person) @@ -182,7 +163,7 @@ def test_valid_idempotent_step(self): self.assertEqual(new_token_a.application, old_token_a.application) self.assertEqual(new_token_a.administration, old_token_a.administration) - new_token_b = tokens[1] + new_token_b = tokens.get(identifier="token-2") self.assertEqual(new_token_b.identifier, old_token_b.identifier) self.assertEqual(new_token_b.contact_person, old_token_b.contact_person) self.assertEqual(new_token_b.token, old_token_b.token) @@ -191,14 +172,12 @@ def test_valid_idempotent_step(self): self.assertEqual(new_token_b.application, old_token_b.application) self.assertEqual(new_token_b.administration, old_token_b.administration) - def test_invalid_setup_empty(self): + def test_invalid_setup(self): with self.assertRaises(PrerequisiteFailed) as command_error: - setup_config = build_step_config_from_sources( + execute_single_step( TokenAuthConfigurationStep, - str(DIR_FILES / "invalid_setup_empty.yaml"), + yaml_source=str(DIR_FILES / "invalid_setup.yaml"), ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) self.assertTrue("Input should be a valid list" in str(command_error.exception)) self.assertEqual(TokenAuth.objects.count(), 0) @@ -220,13 +199,8 @@ def test_invalid_setup_email(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Validation error(s) occured for token-1" in str(command_error.exception) @@ -250,13 +224,33 @@ def test_invalid_setup_token(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, + with self.assertRaises(ConfigurationRunFailed) as command_error: + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) + + self.assertTrue( + "Validation error(s) occured for token-1" in str(command_error.exception) ) + self.assertEqual(TokenAuth.objects.count(), 0) + + def test_invalid_empty_token(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "token": "", + "contact_person": "Person 1", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + ], + }, + } with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Validation error(s) occured for token-1" in str(command_error.exception) @@ -280,12 +274,7 @@ def test_invalid_setup_token_missing(self): }, } with self.assertRaises(PrerequisiteFailed) as command_error: - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue("Field required" in str(command_error.exception)) self.assertEqual(TokenAuth.objects.count(), 0) @@ -317,12 +306,7 @@ def test_invalid_setup_token_unique(self): }, } with self.assertRaises(ConfigurationRunFailed) as command_error: - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Failed configuring token token-2" in str(command_error.exception) @@ -346,13 +330,8 @@ def test_invalid_setup_contact_person(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) self.assertTrue( "Validation error(s) occured for token-1" in str(command_error.exception) @@ -376,66 +355,11 @@ def test_invalid_setup_identifier(self): ], }, } - setup_config = build_step_config_from_sources( - TokenAuthConfigurationStep, - object_source=object_source, - ) with self.assertRaises(ConfigurationRunFailed) as command_error: - step = TokenAuthConfigurationStep() - step.execute(setup_config) - self.assertTrue( - "Validation error(s) occured for invalid identifier" - in str(command_error.exception) - ) - self.assertEqual(TokenAuth.objects.count(), 0) + execute_single_step(TokenAuthConfigurationStep, object_source=object_source) - def test_valid_call_command(self): - stdout = StringIO() - self.assertEqual(TokenAuth.objects.count(), 0) - call_command( - "setup_configuration", - "--yaml-file", - str(DIR_FILES / "valid_setup_default.yaml"), - stdout=stdout, - ) self.assertTrue( - "Successfully executed step: Configuration to set up authentication tokens for ObjectTypes" - in stdout.getvalue() - ) - self.assertEqual(TokenAuth.objects.count(), 2) - - tokens = TokenAuth.objects.order_by("created") - self.assertEqual(tokens.count(), 2) - - token = tokens[0] - self.assertEqual(token.identifier, "token-1") - self.assertEqual(token.token, "18b2b74ef994314b84021d47b9422e82b685d82f") - self.assertEqual(token.contact_person, "Person 1") - self.assertEqual(token.email, "person-1@example.com") - self.assertEqual(token.organization, "") - self.assertEqual(token.application, "") - self.assertEqual(token.administration, "") - - token = tokens[1] - self.assertEqual(token.identifier, "token-2") - self.assertEqual(token.contact_person, "Person 2") - self.assertEqual(token.token, "e882642bd0ec2482adcdc97258c2e6f98cb06d85") - self.assertEqual(token.email, "person-2@example.com") - self.assertEqual(token.organization, "") - self.assertEqual(token.application, "") - self.assertEqual(token.administration, "") - - def test_invalid_call_command(self): - self.assertEqual(TokenAuth.objects.count(), 0) - with self.assertRaises(CommandError) as command_error: - call_command( - "setup_configuration", - "--yaml-file", - str(DIR_FILES / "invalid_setup_empty.yaml"), - ) - - self.assertTrue( - "Failed to load config model for Configuration to set up authentication tokens for ObjectTypes" + "Validation error(s) occured for invalid identifier" in str(command_error.exception) ) self.assertEqual(TokenAuth.objects.count(), 0) diff --git a/src/objecttypes/token/tests/test_migrations.py b/src/objecttypes/token/tests/test_migrations.py new file mode 100644 index 0000000..0597b31 --- /dev/null +++ b/src/objecttypes/token/tests/test_migrations.py @@ -0,0 +1,88 @@ +from django.core.management import call_command +from django.db import connection +from django.db.migrations.executor import MigrationExecutor +from django.db.migrations.state import StateApps +from django.test import TransactionTestCase + + +class BaseMigrationTest(TransactionTestCase): + app: str + migrate_from: str # The migration before the one we want to test + migrate_to: str # The migration we want to test + + setting_overrides: dict = {} + + old_app_state: StateApps + app_state: StateApps + + def setUp(self) -> None: + """ + Setup the migration test by reversing to `migrate_from` state, + then applying the `migrate_to` state. + """ + assert self.app is not None, "You must define the `app` attribute" + assert self.migrate_from is not None, "You must define `migrate_from`" + assert self.migrate_to is not None, "You must define `migrate_to`" + + # Step 1: Set up the MigrationExecutor + executor = MigrationExecutor(connection) + + # Step 2: Reverse to the starting migration state + migrate_from = [(self.app, self.migrate_from)] + old_migrate_state = executor.migrate(migrate_from) + + self.old_app_state = old_migrate_state.apps + + def _perform_migration(self) -> None: + migrate_to = [(self.app, self.migrate_to)] + + executor = MigrationExecutor(connection) + executor.loader.build_graph() # reload the graph in case of dependency changes + executor.migrate(migrate_to) + + self.apps = executor.loader.project_state(migrate_to).apps + + @classmethod + def tearDownClass(cls) -> None: + super().tearDownClass() + + # reset to latest migration + call_command("migrate", verbosity=0, database=connection._alias) + + +class TestTokenAuthUniqueness(BaseMigrationTest): + app = "token" + migrate_from = "0008_alter_tokenauth_token" + migrate_to = "0009_tokenauth_identifier_alter_tokenauth_token" + + def test_migrate_tokens_check_attr(self): + TokenAuth = self.old_app_state.get_model("token", "TokenAuth") + self.assertFalse(hasattr(TokenAuth, "identifier")) + + self._perform_migration() + + TokenAuth = self.apps.get_model("token", "TokenAuth") + self.assertTrue(hasattr(TokenAuth, "identifier")) + + def test_migrate_tokens_to_unique_identifiers(self): + TokenAuth = self.old_app_state.get_model("token", "TokenAuth") + TokenAuth.objects.create( + token="aa018d1c576c9dae33be1e549f739f2834ebc811", + contact_person="Person 1", + email="test@example.com", + ) + TokenAuth.objects.create( + token="ab700d6bf906c2b4b42a961c529657314c6a8246", + contact_person="Other person", + email="somebody@else.com", + ) + + self._perform_migration() + + TokenAuth = self.apps.get_model("token", "TokenAuth") + tokens = TokenAuth.objects.all() + self.assertEqual(tokens.count(), 2) + + first_token = tokens.get(token="aa018d1c576c9dae33be1e549f739f2834ebc811") + second_token = tokens.get(token="ab700d6bf906c2b4b42a961c529657314c6a8246") + self.assertNotEqual(first_token.identifier, second_token.identifier)