Projet

Général

Profil

0002-migrate-issuer-data-56819.patch

Benjamin Dauvergne, 15 septembre 2021 16:56

Télécharger (7,97 ko)

Voir les différences:

Subject: [PATCH 2/3] migrate issuer data (#56819)

 mellon/migrations/0004_migrate_issuer.py | 31 ++++++++++
 tests/conftest.py                        | 51 +++++++++++++++++
 tests/test_migrations.py                 | 73 ++++++++++++++++++++++++
 3 files changed, 155 insertions(+)
 create mode 100644 mellon/migrations/0004_migrate_issuer.py
 create mode 100644 tests/test_migrations.py
mellon/migrations/0004_migrate_issuer.py
1
# Generated by Django 2.2.19 on 2021-09-14 18:54
2

  
3
from django.db import migrations
4
from django.db.models import Count
5

  
6

  
7
def migrate_issuer_forward(apps, schema_editor):
8
    UserSAMLIdentifier = apps.get_model('mellon', 'UserSAMLIdentifier')
9
    Issuer = apps.get_model('mellon', 'Issuer')
10
    issuers = UserSAMLIdentifier.objects.values_list('issuer').annotate(total=Count('id'))
11
    for issuer, total in issuers:
12
        issuer_instance = Issuer.objects.create(entity_id=issuer)
13
        UserSAMLIdentifier.objects.filter(issuer=issuer).update(issuer_fk=issuer_instance)
14

  
15

  
16
def migrate_issuer_backward(apps, schema_editor):
17
    UserSAMLIdentifier = apps.get_model('mellon', 'UserSAMLIdentifier')
18
    Issuer = apps.get_model('mellon', 'Issuer')
19
    for issuer in Issuer.objects.all():
20
        UserSAMLIdentifier.objects.filter(issuer_fk=issuer).update(issuer=issuer.entity_id)
21

  
22

  
23
class Migration(migrations.Migration):
24
    dependencies = [
25
        ('mellon', '0003_add_issuer_model'),
26
    ]
27

  
28
    operations = []
29
    operations = [
30
        migrations.RunPython(migrate_issuer_forward, migrate_issuer_backward),
31
    ]
tests/conftest.py
18 18

  
19 19
import django_webtest
20 20
import pytest
21
from django.core.management import call_command
22
from django.db import connection
23
from django.db.migrations.executor import MigrationExecutor
21 24

  
22 25

  
23 26
@pytest.fixture(autouse=True)
......
82 85
    with metadata_path.open('w') as fd:
83 86
        fd.write(metadata)
84 87
    yield str(metadata_path)
88

  
89

  
90
@pytest.fixture()
91
def migration(request, transactional_db):
92
    # see https://gist.github.com/asfaltboy/b3e6f9b5d95af8ba2cc46f2ba6eae5e2
93
    """
94
    This fixture returns a helper object to test Django data migrations.
95
    The fixture returns an object with two methods;
96
     - `before` to initialize db to the state before the migration under test
97
     - `after` to execute the migration and bring db to the state after the migration
98
    The methods return `old_apps` and `new_apps` respectively; these can
99
    be used to initiate the ORM models as in the migrations themselves.
100
    For example:
101
        def test_foo_set_to_bar(migration):
102
            old_apps = migration.before([('my_app', '0001_inital')])
103
            Foo = old_apps.get_model('my_app', 'foo')
104
            Foo.objects.create(bar=False)
105
            assert Foo.objects.count() == 1
106
            assert Foo.objects.filter(bar=False).count() == Foo.objects.count()
107
            # executing migration
108
            new_apps = migration.apply([('my_app', '0002_set_foo_bar')])
109
            Foo = new_apps.get_model('my_app', 'foo')
110

  
111
            assert Foo.objects.filter(bar=False).count() == 0
112
            assert Foo.objects.filter(bar=True).count() == Foo.objects.count()
113
    Based on: https://gist.github.com/blueyed/4fb0a807104551f103e6
114
    """
115

  
116
    class Migrator:
117
        def before(self, targets, at_end=True):
118
            """Specify app and starting migration names as in:
119
            before([('app', '0001_before')]) => app/migrations/0001_before.py
120
            """
121
            executor = MigrationExecutor(connection)
122
            executor.migrate(targets)
123
            executor.loader.build_graph()
124
            return executor._create_project_state(with_applied_migrations=True).apps
125

  
126
        def apply(self, targets):
127
            """Migrate forwards to the "targets" migration"""
128
            executor = MigrationExecutor(connection)
129
            executor.migrate(targets)
130
            executor.loader.build_graph()
131
            return executor._create_project_state(with_applied_migrations=True).apps
132

  
133
    yield Migrator()
134

  
135
    call_command('migrate', verbosity=0)
tests/test_migrations.py
1
# django-mellon - SAML2 authentication for Django
2
# Copyright (C) 2014-2021 Entr'ouvert
3
# This program is free software: you can redistribute it and/or modify
4
# it under the terms of the GNU Affero General Public License as
5
# published by the Free Software Foundation, either version 3 of the
6
# License, or (at your option) any later version.
7

  
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU Affero General Public License for more details.
12

  
13
# You should have received a copy of the GNU Affero General Public License
14
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
15

  
16
import pytest
17
from django.contrib.auth.models import User
18

  
19
from mellon.models import Issuer, UserSAMLIdentifier
20

  
21

  
22
@pytest.fixture
23
def user_and_issuers(db):
24
    user1 = User.objects.create(username='user1')
25
    user2 = User.objects.create(username='user2')
26
    issuer1 = Issuer.objects.create(entity_id='https://idp1')
27
    issuer2 = Issuer.objects.create(entity_id='https://idp2')
28
    UserSAMLIdentifier.objects.create(user=user1, issuer=issuer1, name_id='xxx')
29
    UserSAMLIdentifier.objects.create(user=user2, issuer=issuer2, name_id='yyy')
30

  
31

  
32
def test_migration_0004_migrate_issuer_back_and_forward(transactional_db, user_and_issuers, migration):
33
    migration.before([('mellon', '0002_sessionindex')])
34
    new_apps = migration.apply([('mellon', '0004_migrate_issuer')])
35

  
36
    UserSAMLIdentifier = new_apps.get_model('mellon', 'UserSAMLIdentifier')
37
    Issuer = new_apps.get_model('mellon', 'Issuer')
38
    User = new_apps.get_model('auth', 'User')
39

  
40
    user1 = User.objects.get(username='user1')
41
    user2 = User.objects.get(username='user2')
42

  
43
    assert UserSAMLIdentifier.objects.count() == 2
44
    assert Issuer.objects.count() == 2
45
    assert UserSAMLIdentifier.objects.get(user=user1, issuer_fk__entity_id='https://idp1', name_id='xxx')
46
    assert UserSAMLIdentifier.objects.get(user=user2, issuer_fk__entity_id='https://idp2', name_id='yyy')
47

  
48

  
49
def test_migration_0004_migrate_issuer(transactional_db, migration):
50
    old_apps = migration.before([('mellon', '0003_add_issuer_model')])
51

  
52
    UserSAMLIdentifier = old_apps.get_model('mellon', 'UserSAMLIdentifier')
53
    User = old_apps.get_model('auth', 'User')
54

  
55
    user1 = User.objects.create(username='user1')
56
    user2 = User.objects.create(username='user2')
57

  
58
    UserSAMLIdentifier.objects.create(user=user1, issuer='https://idp1', name_id='xxx')
59
    UserSAMLIdentifier.objects.create(user=user2, issuer='https://idp2', name_id='yyy')
60

  
61
    new_apps = migration.apply([('mellon', '0004_migrate_issuer')])
62

  
63
    UserSAMLIdentifier = new_apps.get_model('mellon', 'UserSAMLIdentifier')
64
    Issuer = new_apps.get_model('mellon', 'Issuer')
65
    User = new_apps.get_model('auth', 'User')
66

  
67
    user1 = User.objects.get(username='user1')
68
    user2 = User.objects.get(username='user2')
69

  
70
    assert UserSAMLIdentifier.objects.count() == 2
71
    assert Issuer.objects.count() == 2
72
    assert UserSAMLIdentifier.objects.get(user=user1, issuer_fk__entity_id='https://idp1', name_id='xxx')
73
    assert UserSAMLIdentifier.objects.get(user=user2, issuer_fk__entity_id='https://idp2', name_id='yyy')
0
-