datanommer.models 1.0.2__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@
14
14
  # You should have received a copy of the GNU General Public License along
15
15
  # with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  import datetime
17
+ import importlib.metadata
17
18
  import json
18
19
  import logging
19
20
  import math
@@ -21,7 +22,6 @@ import traceback
21
22
  import uuid
22
23
  from warnings import warn
23
24
 
24
- import pkg_resources
25
25
  from sqlalchemy import (
26
26
  and_,
27
27
  between,
@@ -31,11 +31,14 @@ from sqlalchemy import (
31
31
  DDL,
32
32
  event,
33
33
  ForeignKey,
34
+ func,
34
35
  Integer,
35
36
  not_,
36
37
  or_,
38
+ select,
37
39
  String,
38
40
  Table,
41
+ text,
39
42
  TypeDecorator,
40
43
  Unicode,
41
44
  UnicodeText,
@@ -61,6 +64,9 @@ except ImportError: # pragma: no cover
61
64
  UniqueViolation = lookup_error("23505")
62
65
 
63
66
 
67
+ __version__ = importlib.metadata.version("datanommer.models")
68
+
69
+
64
70
  log = logging.getLogger("datanommer")
65
71
 
66
72
  maker = sessionmaker()
@@ -80,7 +86,7 @@ def init(uri=None, alembic_ini=None, engine=None, create=False):
80
86
  raise ValueError("One of uri or engine must be specified")
81
87
 
82
88
  if uri and not engine:
83
- engine = create_engine(uri)
89
+ engine = create_engine(uri, future=True)
84
90
 
85
91
  # We need to hang our own attribute on the sqlalchemy session to stop
86
92
  # ourselves from initializing twice. That is only a problem if the code
@@ -90,11 +96,12 @@ def init(uri=None, alembic_ini=None, engine=None, create=False):
90
96
  return
91
97
  session._datanommer_initialized = True
92
98
 
93
- session.configure(bind=engine)
99
+ maker.configure(bind=engine)
94
100
  DeclarativeBase.query = session.query_property()
95
101
 
96
102
  if create:
97
- session.execute("CREATE EXTENSION IF NOT EXISTS timescaledb")
103
+ with engine.begin() as connection:
104
+ connection.execute(text("CREATE EXTENSION IF NOT EXISTS timescaledb"))
98
105
  DeclarativeBase.metadata.create_all(engine)
99
106
  # Loads the alembic configuration and generates the version table, with
100
107
  # the most recent revision stamped as head
@@ -122,7 +129,7 @@ def add(message):
122
129
  log.exception("Failed to parse sent-at timestamp value")
123
130
  return
124
131
  else:
125
- sent_at = datetime.datetime.utcnow()
132
+ sent_at = datetime.datetime.now(tz=datetime.timezone.utc)
126
133
 
127
134
  # Workaround schemas misbehaving
128
135
  try:
@@ -158,11 +165,6 @@ def add(message):
158
165
  session.commit()
159
166
 
160
167
 
161
- def source_version_default(context):
162
- dist = pkg_resources.get_distribution("datanommer.models")
163
- return dist.version
164
-
165
-
166
168
  # https://docs.sqlalchemy.org/en/14/core/custom_types.html#marshal-json-strings
167
169
 
168
170
 
@@ -224,7 +226,7 @@ class Message(DeclarativeBase):
224
226
  username = Column(Unicode)
225
227
  crypto = Column(UnicodeText)
226
228
  source_name = Column(Unicode, default="datanommer")
227
- source_version = Column(Unicode, default=source_version_default)
229
+ source_version = Column(Unicode, default=lambda context: __version__)
228
230
  msg = Column(JSONEncodedDict, nullable=False)
229
231
  headers = Column(postgresql.JSONB(none_as_null=True))
230
232
  users = relationship(
@@ -314,7 +316,7 @@ class Message(DeclarativeBase):
314
316
 
315
317
  @classmethod
316
318
  def from_msg_id(cls, msg_id):
317
- return cls.query.filter(cls.msg_id == msg_id).first()
319
+ return session.execute(select(cls).where(cls.msg_id == msg_id)).scalar_one_or_none()
318
320
 
319
321
  def as_dict(self, request=None):
320
322
  return dict(
@@ -337,13 +339,12 @@ class Message(DeclarativeBase):
337
339
  def as_fedora_message_dict(self):
338
340
  headers = self.headers or {}
339
341
  if "sent-at" not in headers:
340
- headers["sent-at"] = self.timestamp.astimezone(
341
- datetime.timezone.utc
342
- ).isoformat()
342
+ headers["sent-at"] = self.timestamp.astimezone(datetime.timezone.utc).isoformat()
343
343
  return dict(
344
344
  body=self.msg,
345
345
  headers=headers,
346
346
  id=self.msg_id,
347
+ priority=headers.get("priority", 0),
347
348
  queue=None,
348
349
  topic=self.topic,
349
350
  )
@@ -353,6 +354,7 @@ class Message(DeclarativeBase):
353
354
  "The __json__() method has been renamed to as_dict(), and will be removed "
354
355
  "in the next major version",
355
356
  DeprecationWarning,
357
+ stacklevel=2,
356
358
  )
357
359
  return self.as_dict(request)
358
360
 
@@ -419,69 +421,55 @@ class Message(DeclarativeBase):
419
421
  not_topics = not_topics or []
420
422
  contains = contains or []
421
423
 
422
- query = Message.query
424
+ Message = cls
425
+ query = select(Message)
423
426
 
424
427
  # A little argument validation. We could provide some defaults in
425
428
  # these mixed cases.. but instead we'll just leave it up to our caller.
426
429
  if (start is not None and end is None) or (end is not None and start is None):
427
430
  raise ValueError(
428
- "Either both start and end must be specified "
429
- "or neither must be specified"
431
+ "Either both start and end must be specified or neither must be specified"
430
432
  )
431
433
 
432
434
  if start and end:
433
- query = query.filter(between(Message.timestamp, start, end))
435
+ query = query.where(between(Message.timestamp, start, end))
434
436
 
435
437
  if msg_id:
436
- query = query.filter(Message.msg_id == msg_id)
438
+ query = query.where(Message.msg_id == msg_id)
437
439
 
438
440
  # Add the four positive filters as necessary
439
441
  if users:
440
- query = query.filter(
441
- or_(*(Message.users.any(User.name == u) for u in users))
442
- )
442
+ query = query.where(or_(*(Message.users.any(User.name == u) for u in users)))
443
443
 
444
444
  if packages:
445
- query = query.filter(
446
- or_(*(Message.packages.any(Package.name == p) for p in packages))
447
- )
445
+ query = query.where(or_(*(Message.packages.any(Package.name == p) for p in packages)))
448
446
 
449
447
  if categories:
450
- query = query.filter(
451
- or_(*(Message.category == category for category in categories))
452
- )
448
+ query = query.where(or_(*(Message.category == category for category in categories)))
453
449
 
454
450
  if topics:
455
- query = query.filter(or_(*(Message.topic == topic for topic in topics)))
451
+ query = query.where(or_(*(Message.topic == topic for topic in topics)))
456
452
 
457
453
  if contains:
458
- query = query.filter(
459
- or_(*(Message.msg.like(f"%{contain}%") for contain in contains))
460
- )
454
+ query = query.where(or_(*(Message.msg.like(f"%{contain}%") for contain in contains)))
461
455
 
462
456
  # And then the four negative filters as necessary
463
457
  if not_users:
464
- query = query.filter(
465
- not_(or_(*(Message.users.any(User.name == u) for u in not_users)))
466
- )
458
+ query = query.where(not_(or_(*(Message.users.any(User.name == u) for u in not_users))))
467
459
 
468
460
  if not_packs:
469
- query = query.filter(
461
+ query = query.where(
470
462
  not_(or_(*(Message.packages.any(Package.name == p) for p in not_packs)))
471
463
  )
472
464
 
473
465
  if not_cats:
474
- query = query.filter(
475
- not_(or_(*(Message.category == category for category in not_cats)))
476
- )
466
+ query = query.where(not_(or_(*(Message.category == category for category in not_cats))))
477
467
 
478
468
  if not_topics:
479
- query = query.filter(
480
- not_(or_(*(Message.topic == topic for topic in not_topics)))
481
- )
469
+ query = query.where(not_(or_(*(Message.topic == topic for topic in not_topics))))
482
470
 
483
471
  # Finally, tag on our pagination arguments
484
- total = query.count()
472
+ total = session.scalar(query.with_only_columns(func.count(Message.id)))
485
473
  query = query.order_by(getattr(Message.timestamp, order)())
486
474
 
487
475
  if not rows_per_page:
@@ -494,12 +482,11 @@ class Message(DeclarativeBase):
494
482
  return total, page, query
495
483
  else:
496
484
  # Execute!
497
- messages = query.all()
485
+ messages = session.scalars(query).all()
498
486
  return total, pages, messages
499
487
 
500
488
 
501
489
  class NamedSingleton:
502
-
503
490
  id = Column(Integer, primary_key=True, autoincrement=True)
504
491
  name = Column(UnicodeText, index=True, unique=True)
505
492
 
@@ -513,8 +500,8 @@ class NamedSingleton:
513
500
  if name in cls._cache:
514
501
  # If we cache the instance, SQLAlchemy will run this query anyway because the instance
515
502
  # will be from a different transaction. So just cache the id.
516
- return cls.query.get(cls._cache[name])
517
- obj = cls.query.filter_by(name=name).one_or_none()
503
+ return session.get(cls, cls._cache[name])
504
+ obj = session.execute(select(cls).where(cls.name == name)).scalar_one_or_none()
518
505
  if obj is None:
519
506
  obj = cls(name=name)
520
507
  session.add(obj)
@@ -546,16 +533,3 @@ def _setup_hypertable(table_class):
546
533
 
547
534
 
548
535
  _setup_hypertable(Message)
549
-
550
-
551
- # Set the version
552
- try: # pragma: no cover
553
- import importlib.metadata
554
-
555
- __version__ = importlib.metadata.version("datanommer.models")
556
- except ImportError: # pragma: no cover
557
- try:
558
- __version__ = pkg_resources.get_distribution("datanommer.models").version
559
- except pkg_resources.DistributionNotFound:
560
- # The app is not installed, but the flask dev server can run it nonetheless.
561
- __version__ = None
@@ -0,0 +1,93 @@
1
+ # This file is a part of datanommer, a message sink for fedmsg.
2
+ # Copyright (C) 2014, Red Hat, Inc.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify it under
5
+ # the terms of the GNU General Public License as published by the Free Software
6
+ # Foundation, either version 3 of the License, or (at your option) any later
7
+ # version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful, but WITHOUT
10
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ # details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License along
15
+ # with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ from logging.config import fileConfig
18
+
19
+ from alembic import context
20
+ from sqlalchemy import engine_from_config, pool
21
+
22
+ # add your model's MetaData object here
23
+ # for 'autogenerate' support
24
+ # from myapp import mymodel
25
+ # target_metadata = mymodel.Base.metadata
26
+ from datanommer.models import DeclarativeBase
27
+
28
+
29
+ target_metadata = DeclarativeBase.metadata
30
+
31
+
32
+ # this is the Alembic Config object, which provides
33
+ # access to the values within the .ini file in use.
34
+ config = context.config
35
+
36
+ # Interpret the config file for Python logging.
37
+ # This line sets up loggers basically.
38
+ fileConfig(config.config_file_name)
39
+
40
+ # other values from the config, defined by the needs of env.py,
41
+ # can be acquired:
42
+ # my_important_option = config.get_main_option("my_important_option")
43
+ # ... etc.
44
+
45
+
46
+ def run_migrations_offline():
47
+ """Run migrations in 'offline' mode.
48
+
49
+ This configures the context with just a URL
50
+ and not an Engine, though an Engine is acceptable
51
+ here as well. By skipping the Engine creation
52
+ we don't even need a DBAPI to be available.
53
+
54
+ Calls to context.execute() here emit the given string to the
55
+ script output.
56
+
57
+ """
58
+ # TODO: Pull this from datanommer's fedmsg.d config isntead of using
59
+ # the alembic.ini
60
+ url = config.get_main_option("sqlalchemy.url")
61
+ context.configure(url=url)
62
+
63
+ with context.begin_transaction():
64
+ context.run_migrations()
65
+
66
+
67
+ def run_migrations_online():
68
+ """Run migrations in 'online' mode.
69
+
70
+ In this scenario we need to create an Engine
71
+ and associate a connection with the context.
72
+
73
+ """
74
+ engine = engine_from_config(
75
+ config.get_section(config.config_ini_section),
76
+ prefix="sqlalchemy.",
77
+ poolclass=pool.NullPool,
78
+ )
79
+
80
+ connection = engine.connect()
81
+ context.configure(connection=connection, target_metadata=target_metadata)
82
+
83
+ try:
84
+ with context.begin_transaction():
85
+ context.run_migrations()
86
+ finally:
87
+ connection.close()
88
+
89
+
90
+ if context.is_offline_mode():
91
+ run_migrations_offline()
92
+ else:
93
+ run_migrations_online()
@@ -0,0 +1,22 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+
9
+ # revision identifiers, used by Alembic.
10
+ revision = ${repr(up_revision)}
11
+ down_revision = ${repr(down_revision)}
12
+
13
+ from alembic import op
14
+ import sqlalchemy as sa
15
+ ${imports if imports else ""}
16
+
17
+ def upgrade():
18
+ ${upgrades if upgrades else "pass"}
19
+
20
+
21
+ def downgrade():
22
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,19 @@
1
+ """Initial revision
2
+
3
+ Revision ID: 5db25abc63be
4
+ Revises: None
5
+ Create Date: 2021-09-15 16:15:37.188484
6
+
7
+ """
8
+
9
+ # revision identifiers, used by Alembic.
10
+ revision = "5db25abc63be"
11
+ down_revision = None
12
+
13
+
14
+ def upgrade():
15
+ pass
16
+
17
+
18
+ def downgrade():
19
+ pass
@@ -0,0 +1,27 @@
1
+ """Add a unique index on packages and users
2
+
3
+ Revision ID: 951c40020acc
4
+ Revises: 5db25abc63be
5
+ Create Date: 2021-09-22 15:38:57.339646
6
+ """
7
+
8
+ from alembic import op
9
+
10
+
11
+ # revision identifiers, used by Alembic.
12
+ revision = "951c40020acc"
13
+ down_revision = "5db25abc63be"
14
+
15
+
16
+ def upgrade():
17
+ op.drop_index("ix_packages_name", table_name="packages")
18
+ op.create_index(op.f("ix_packages_name"), "packages", ["name"], unique=True)
19
+ op.drop_index("ix_users_name", table_name="users")
20
+ op.create_index(op.f("ix_users_name"), "users", ["name"], unique=True)
21
+
22
+
23
+ def downgrade():
24
+ op.drop_index(op.f("ix_users_name"), table_name="users")
25
+ op.create_index("ix_users_name", "users", ["name"], unique=False)
26
+ op.drop_index(op.f("ix_packages_name"), table_name="packages")
27
+ op.create_index("ix_packages_name", "packages", ["name"], unique=False)
@@ -1,46 +1,48 @@
1
- import os
2
-
3
1
  import pytest
2
+ import sqlalchemy as sa
4
3
  from pytest_postgresql import factories
4
+ from pytest_postgresql.janitor import DatabaseJanitor
5
5
  from sqlalchemy.orm import scoped_session
6
6
 
7
7
  import datanommer.models as dm
8
8
 
9
9
 
10
- @pytest.fixture(scope="session")
11
- def postgresql_proc_with_timescaledb(postgresql_proc):
12
- with open(os.path.join(postgresql_proc.datadir, "postgresql.conf"), "a") as pgconf:
13
- pgconf.write("\nshared_preload_libraries = 'timescaledb'\n")
14
- pgconf.write("timescaledb.telemetry_level=off\n")
15
- postgresql_proc.stop()
16
- postgresql_proc.start()
17
- yield postgresql_proc
18
-
19
-
20
- _inital_sql = os.path.abspath(os.path.join(os.path.dirname(__file__), "startup.sql"))
21
- pgsql = factories.postgresql(
22
- "postgresql_proc_with_timescaledb",
23
- load=[_inital_sql],
10
+ postgresql_proc = factories.postgresql_proc(
11
+ postgres_options="-c shared_preload_libraries=timescaledb -c timescaledb.telemetry_level=off",
24
12
  )
25
13
 
26
14
 
27
- @pytest.fixture()
28
- def datanommer_db_url(pgsql):
15
+ @pytest.fixture(scope="session")
16
+ def datanommer_db_url(postgresql_proc):
29
17
  return (
30
- f"postgresql+psycopg2://{pgsql.info.user}:@"
31
- f"{pgsql.info.host}:{pgsql.info.port}"
32
- f"/{pgsql.info.dbname}"
18
+ f"postgresql+psycopg2://{postgresql_proc.user}:@"
19
+ f"{postgresql_proc.host}:{postgresql_proc.port}"
20
+ f"/{postgresql_proc.dbname}"
33
21
  )
34
22
 
35
23
 
36
24
  @pytest.fixture()
37
- def datanommer_models(datanommer_db_url):
38
- dm.session = scoped_session(dm.maker)
39
- dm.init(datanommer_db_url, create=True)
25
+ def datanommer_db(postgresql_proc, datanommer_db_url):
26
+ with DatabaseJanitor(
27
+ user=postgresql_proc.user,
28
+ host=postgresql_proc.host,
29
+ port=postgresql_proc.port,
30
+ dbname=postgresql_proc.dbname,
31
+ # Don't use a template database
32
+ # template_dbname=postgresql_proc.template_dbname,
33
+ version=postgresql_proc.version,
34
+ ):
35
+ engine = sa.create_engine(datanommer_db_url, future=True, poolclass=sa.pool.NullPool)
36
+ # Renew the global object, dm.init checks a custom attribute
37
+ dm.session = scoped_session(dm.maker)
38
+ dm.init(engine=engine, create=True)
39
+ yield engine
40
+ dm.session.close()
41
+
42
+
43
+ @pytest.fixture()
44
+ def datanommer_models(datanommer_db):
40
45
  dm.User.clear_cache()
41
46
  dm.Package.clear_cache()
42
47
  yield dm.session
43
48
  dm.session.rollback()
44
- # engine = dm.session.get_bind()
45
- dm.session.close()
46
- # dm.DeclarativeBase.metadata.drop_all(engine)
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.1
2
+ Name: datanommer.models
3
+ Version: 1.2.0
4
+ Summary: SQLAlchemy models for datanommer
5
+ Home-page: https://github.com/fedora-infra/datanommer
6
+ License: GPL-3.0-or-later
7
+ Author: Fedora Infrastructure
8
+ Author-email: admin@fedoraproject.org
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Provides-Extra: schemas
16
+ Requires-Dist: SQLAlchemy (>=1.3.24,<3.0.0)
17
+ Requires-Dist: alembic (>=1.6.5,<2.0.0)
18
+ Requires-Dist: anitya-schema ; extra == "schemas"
19
+ Requires-Dist: bodhi-messages ; extra == "schemas"
20
+ Requires-Dist: bugzilla2fedmsg-schema ; extra == "schemas"
21
+ Requires-Dist: ci-messages ; extra == "schemas"
22
+ Requires-Dist: copr-messaging ; extra == "schemas"
23
+ Requires-Dist: discourse2fedmsg-messages ; extra == "schemas"
24
+ Requires-Dist: fedocal-messages ; extra == "schemas"
25
+ Requires-Dist: fedora-elections-messages ; extra == "schemas"
26
+ Requires-Dist: fedora-messaging (>=2.1.0)
27
+ Requires-Dist: fedora-messaging-git-hook-messages ; extra == "schemas"
28
+ Requires-Dist: fedora-messaging-the-new-hotness-schema ; extra == "schemas"
29
+ Requires-Dist: fedora-planet-messages ; extra == "schemas"
30
+ Requires-Dist: fedorainfra-ansible-messages ; extra == "schemas"
31
+ Requires-Dist: fmn-messages ; extra == "schemas"
32
+ Requires-Dist: kerneltest-messages (>=1.0.0,<2.0.0) ; extra == "schemas"
33
+ Requires-Dist: koji-fedoramessaging-messages (>=1.2.2,<2.0.0) ; extra == "schemas"
34
+ Requires-Dist: koschei-messages ; extra == "schemas"
35
+ Requires-Dist: mdapi-messages ; extra == "schemas"
36
+ Requires-Dist: mediawiki-messages ; extra == "schemas"
37
+ Requires-Dist: meetbot-messages ; extra == "schemas"
38
+ Requires-Dist: noggin-messages ; extra == "schemas"
39
+ Requires-Dist: nuancier-messages ; extra == "schemas"
40
+ Requires-Dist: pagure-messages ; extra == "schemas"
41
+ Requires-Dist: psycopg2 (>=2.9.1,<3.0.0)
42
+ Requires-Dist: tahrir-messages ; extra == "schemas"
43
+ Project-URL: Repository, https://github.com/fedora-infra/datanommer
44
+ Description-Content-Type: text/x-rst
45
+
46
+ datanommer.models
47
+ =================
48
+
49
+ This package contains the SQLAlchemy data model for datanommer.
50
+
51
+ Datanommer is a storage consumer for the Fedora Infrastructure Message Bus
52
+ (fedmsg). It is comprised of a `fedmsg <http://fedmsg.com>`_ consumer that
53
+ stuffs every message into a sqlalchemy database.
54
+
@@ -0,0 +1,10 @@
1
+ datanommer/models/__init__.py,sha256=ImCJYqPGAPBjx5OoVu6Lbo0mC6Zu9buGfrdgbectsTc,16874
2
+ datanommer/models/alembic/env.py,sha256=WNTimgnH70CakhvuV5QCilCnOcjTy7kcx0nD7hryYx0,2793
3
+ datanommer/models/alembic/script.py.mako,sha256=D8kFI44_9vBJZrAYSkZxDTX2-S5Y-oEetEd9BKlo9S8,412
4
+ datanommer/models/alembic/versions/5db25abc63be_init.py,sha256=xMD7WGCOqeVNFroCZds_aS_jta2yTrAHc_XhtmZLZRs,249
5
+ datanommer/models/alembic/versions/951c40020acc_unique.py,sha256=GwKDhppKW7y5BUV8BqILZCAiR7GsNyIvoTXUu2A1ZMI,843
6
+ datanommer/models/testing/__init__.py,sha256=T_uy2jBp5TG507WAVTx4A96_SsFgy4UrY6WKOuG_l1Q,1453
7
+ datanommer_models-1.2.0.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
8
+ datanommer_models-1.2.0.dist-info/METADATA,sha256=KxGFi0ZMyjdK7oslLs8ImIlD9oHogCWF4MXOj2Yyg0k,2502
9
+ datanommer_models-1.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
10
+ datanommer_models-1.2.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry 1.0.3
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1 +0,0 @@
1
- CREATE EXTENSION IF NOT EXISTS timescaledb;
@@ -1,41 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: datanommer.models
3
- Version: 1.0.2
4
- Summary: SQLAlchemy models for datanommer
5
- Home-page: https://github.com/fedora-infra/datanommer
6
- License: GPL-3.0-or-later
7
- Author: Fedora Infrastructure
8
- Author-email: admin@fedoraproject.org
9
- Requires-Python: >=3.7,<4.0
10
- Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.7
13
- Classifier: Programming Language :: Python :: 3.8
14
- Classifier: Programming Language :: Python :: 3.9
15
- Provides-Extra: schemas
16
- Requires-Dist: SQLAlchemy (>=1.0.9,<1.4.23)
17
- Requires-Dist: alembic (>=1.6.5,<2.0.0)
18
- Requires-Dist: anitya-schema; extra == "schemas"
19
- Requires-Dist: bodhi-messages; extra == "schemas"
20
- Requires-Dist: copr-messaging; extra == "schemas"
21
- Requires-Dist: discourse2fedmsg-messages; extra == "schemas"
22
- Requires-Dist: fedocal-messages; extra == "schemas"
23
- Requires-Dist: fedora-messaging (>=2.1.0,<3.0.0)
24
- Requires-Dist: fedora-messaging-the-new-hotness-schema; extra == "schemas"
25
- Requires-Dist: fedora-planet-messages; extra == "schemas"
26
- Requires-Dist: fedorainfra-ansible-messages; extra == "schemas"
27
- Requires-Dist: noggin-messages; extra == "schemas"
28
- Requires-Dist: nuancier-messages; extra == "schemas"
29
- Requires-Dist: psycopg2 (>=2.9.1,<3.0.0)
30
- Project-URL: Repository, https://github.com/fedora-infra/datanommer
31
- Description-Content-Type: text/x-rst
32
-
33
- datanommer.models
34
- =================
35
-
36
- This package contains the SQLAlchemy data model for datanommer.
37
-
38
- Datanommer is a storage consumer for the Fedora Infrastructure Message Bus
39
- (fedmsg). It is comprised of a `fedmsg <http://fedmsg.com>`_ consumer that
40
- stuffs every message into a sqlalchemy database.
41
-
@@ -1,11 +0,0 @@
1
- datanommer/models/__init__.py,sha256=4smdWjO0DSxKQeOn0UAfH93eUKQPtTKklqnBdWbGP0Q,17280
2
- datanommer/models/testing/__init__.py,sha256=4aNMycVGrYA6Ljo74ePweDrzSr9oaA7K_W2gtn0YZ74,1271
3
- datanommer/models/testing/startup.sql,sha256=tdjQMZcM7bVDoLnfSg1-fpKKiAPihiVPy-syF0H8mvo,44
4
- tests/conftest.py,sha256=W9WzREc28ksZGv2m1IFgHcrffKG_V1PFPqnRgxsxTSs,45
5
- tests/test_jsonencodeddict.py,sha256=C3cEFTx-hQHoHZUczAQ2LDFVC6bvPV92DR17Ig2VnX4,1928
6
- tests/test_model.py,sha256=NWV5Z7guirls57A73HUsRxiyOnpWAOYIDUh0Afk4nX8,18950
7
- tox.ini,sha256=5ichc5c0HCb16Du3ZB9Kv4tDmI9VGjAfRLJB7Vo_XVs,260
8
- datanommer.models-1.0.2.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
9
- datanommer.models-1.0.2.dist-info/WHEEL,sha256=V7iVckP-GYreevsTDnv1eAinQt_aArwnAxmnP0gygBY,83
10
- datanommer.models-1.0.2.dist-info/METADATA,sha256=zGoHqTFLbwPrHB0gCuHOGKj6sk75UYv2SHJkckmMYDw,1734
11
- datanommer.models-1.0.2.dist-info/RECORD,,
tests/conftest.py DELETED
@@ -1 +0,0 @@
1
- pytest_plugins = "datanommer.models.testing"
@@ -1,61 +0,0 @@
1
- import pytest
2
- from sqlalchemy import Column, create_engine, Integer, MetaData, Table
3
- from sqlalchemy.sql import select
4
-
5
- from datanommer.models import JSONEncodedDict
6
-
7
-
8
- @pytest.fixture
9
- def connection():
10
- engine = create_engine("sqlite:///:memory:")
11
- with engine.connect() as connection:
12
- yield connection
13
-
14
-
15
- @pytest.fixture
16
- def table(connection):
17
- metadata = MetaData()
18
- table = Table(
19
- "test_table",
20
- metadata,
21
- Column("id", Integer, primary_key=True),
22
- Column("data", JSONEncodedDict),
23
- )
24
- metadata.create_all(connection)
25
- yield table
26
- metadata.drop_all(connection)
27
-
28
-
29
- def test_jsonencodeddict(connection, table):
30
- connection.execute(table.insert().values(data={"foo": "bar"}))
31
- # Check that it's stored as a string
32
- for row in connection.execute("SELECT data FROM test_table"):
33
- assert row["data"] == '{"foo": "bar"}'
34
- # Check that SQLAlchemy retrieves it as a dict
35
- for row in connection.execute(select(table.c.data)):
36
- assert row["data"] == {"foo": "bar"}
37
-
38
-
39
- def test_jsonencodeddict_null(connection, table):
40
- # Make sure NULL values are supported
41
- connection.execute(table.insert().values(data=None))
42
- for row in connection.execute(select(table.c.data)):
43
- assert row["data"] is None
44
-
45
-
46
- def test_jsonencodeddict_compare(connection, table):
47
- # Make sure NULL values are supported
48
- connection.execute(table.insert().values(data={"foo": "bar"}))
49
- for row in connection.execute(
50
- select(table.c.data).filter(table.c.data == {"foo": "bar"})
51
- ):
52
- assert row["data"] == {"foo": "bar"}
53
-
54
-
55
- def test_jsonencodeddict_compare_like(connection, table):
56
- # Make sure NULL values are supported
57
- connection.execute(table.insert().values(data={"foo": "bar"}))
58
- for row in connection.execute(
59
- select(table.c.data).filter(table.c.data.like("%foo%"))
60
- ):
61
- assert row["data"] == {"foo": "bar"}
tests/test_model.py DELETED
@@ -1,628 +0,0 @@
1
- # This file is a part of datanommer, a message sink for fedmsg.
2
- # Copyright (C) 2014, Red Hat, Inc.
3
- #
4
- # This program is free software: you can redistribute it and/or modify it under
5
- # the terms of the GNU General Public License as published by the Free Software
6
- # Foundation, either version 3 of the License, or (at your option) any later
7
- # version.
8
- #
9
- # This program is distributed in the hope that it will be useful, but WITHOUT
10
- # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
- # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
- # details.
13
- #
14
- # You should have received a copy of the GNU General Public License along
15
- # with this program. If not, see <http://www.gnu.org/licenses/>.
16
- import datetime
17
- import json
18
- import logging
19
-
20
- import pytest
21
- from bodhi.messages.schemas.update import UpdateCommentV1
22
- from fedora_messaging import message as fedora_message
23
- from sqlalchemy import create_engine
24
- from sqlalchemy.exc import IntegrityError
25
- from sqlalchemy.orm.query import Query
26
-
27
- from datanommer.models import add, init, Message, Package, session, User
28
-
29
-
30
- def generate_message(
31
- topic="org.fedoraproject.test.a.nice.message",
32
- body={"encouragement": "You're doing great!"},
33
- headers=None,
34
- ):
35
- return fedora_message.Message(topic=topic, body=body, headers=headers)
36
-
37
-
38
- def generate_bodhi_update_complete_message(text="testing testing"):
39
- msg = UpdateCommentV1(
40
- body={
41
- "comment": {
42
- "karma": -1,
43
- "text": text,
44
- "timestamp": "2019-03-18 16:54:48",
45
- "update": {
46
- "alias": "FEDORA-EPEL-2021-f2d195dada",
47
- "builds": [
48
- {"nvr": "abrt-addon-python3-2.1.11-50.el7"},
49
- {"nvr": "kernel-10.4.0-2.el7"},
50
- ],
51
- "status": "pending",
52
- "release": {"name": "F35"},
53
- "request": "testing",
54
- "user": {"name": "ryanlerch"},
55
- },
56
- "user": {"name": "dudemcpants"},
57
- }
58
- }
59
- )
60
- msg.topic = f"org.fedoraproject.stg.{msg.topic}"
61
- return msg
62
-
63
-
64
- def test_init_uri_and_engine():
65
- uri = "sqlite:///db.db"
66
- engine = create_engine(uri)
67
-
68
- with pytest.raises(ValueError, match="uri and engine cannot both be specified"):
69
- init(uri, engine=engine)
70
-
71
-
72
- def test_init_no_uri_and_no_engine():
73
- with pytest.raises(ValueError, match="One of uri or engine must be specified"):
74
- init()
75
-
76
-
77
- def test_init_with_engine(caplog):
78
- uri = "sqlite:///db.db"
79
- engine = create_engine(uri)
80
-
81
- init(engine=engine)
82
-
83
- assert not caplog.records
84
-
85
- # if the init with just the engine worked, trying it again will fail
86
- init(engine=engine)
87
- assert caplog.records[0].message == "Session already initialized. Bailing"
88
-
89
-
90
- def test_init_no_init_twice(datanommer_models, mocker, caplog):
91
- init("sqlite:///db.db")
92
- assert caplog.records[0].message == "Session already initialized. Bailing"
93
-
94
-
95
- def test_unclassified_category(datanommer_models):
96
- example_message = generate_message(topic="too.short")
97
- add(example_message)
98
- dbmsg = Message.query.first()
99
-
100
- assert dbmsg.category == "Unclassified"
101
-
102
-
103
- def test_from_msg_id(datanommer_models):
104
- example_message = generate_message()
105
- example_message.id = "ACUSTOMMESSAGEID"
106
- add(example_message)
107
- dbmsg = Message.from_msg_id("ACUSTOMMESSAGEID")
108
-
109
- assert dbmsg.msg_id == "ACUSTOMMESSAGEID"
110
-
111
-
112
- def test_add_missing_msg_id(datanommer_models, caplog):
113
- caplog.set_level(logging.INFO)
114
- example_message = generate_message()
115
- example_message._properties.message_id = None
116
- add(example_message)
117
- dbmsg = Message.query.first()
118
- assert (
119
- "Message on org.fedoraproject.test.a.nice.message was received without a msg_id"
120
- in caplog.records[-1].message
121
- )
122
- assert dbmsg.msg_id is not None
123
-
124
-
125
- def test_add_missing_timestamp(datanommer_models):
126
- example_message = generate_message()
127
- example_message._properties.headers["sent-at"] = None
128
-
129
- add(example_message)
130
-
131
- dbmsg = Message.query.first()
132
- timediff = datetime.datetime.utcnow() - dbmsg.timestamp
133
- # 10 seconds between adding the message and checking
134
- # the timestamp should be more than enough.
135
- assert timediff < datetime.timedelta(seconds=10)
136
-
137
-
138
- def test_add_timestamp_with_Z(datanommer_models):
139
- example_message = generate_message()
140
- example_message._properties.headers["sent-at"] = "2021-07-27T04:22:42Z"
141
-
142
- add(example_message)
143
-
144
- dbmsg = Message.query.first()
145
- assert dbmsg.timestamp.astimezone(datetime.timezone.utc) == datetime.datetime(
146
- 2021, 7, 27, 4, 22, 42, tzinfo=datetime.timezone.utc
147
- )
148
-
149
-
150
- def test_add_timestamp_with_junk(datanommer_models, caplog):
151
- example_message = generate_message()
152
- example_message._properties.headers["sent-at"] = "2021-07-27T04:22:42JUNK"
153
-
154
- add(example_message)
155
-
156
- assert "Failed to parse sent-at timestamp value" in caplog.records[0].message
157
-
158
- assert Message.query.count() == 0
159
-
160
-
161
- def test_add_and_check_for_others(datanommer_models):
162
-
163
- # There are no users or packages at the start
164
- assert User.query.count() == 0
165
- assert Package.query.count() == 0
166
-
167
- # Then add a message
168
- add(generate_bodhi_update_complete_message())
169
-
170
- # There should now be two of each
171
- assert User.query.count() == 2
172
- assert Package.query.count() == 2
173
-
174
- # If we add it again, there should be no duplicates
175
- add(generate_bodhi_update_complete_message())
176
- assert User.query.count() == 2
177
- assert Package.query.count() == 2
178
-
179
- # Add a new username
180
- add(generate_bodhi_update_complete_message(text="this is @abompard in a comment"))
181
- assert User.query.count() == 3
182
- assert Package.query.count() == 2
183
-
184
-
185
- def test_add_nothing(datanommer_models):
186
- assert Message.query.count() == 0
187
-
188
-
189
- def test_add_and_check(datanommer_models):
190
- add(generate_message())
191
- session.flush()
192
- assert Message.query.count() == 1
193
-
194
-
195
- def test_categories(datanommer_models):
196
- add(generate_bodhi_update_complete_message())
197
- session.flush()
198
- obj = Message.query.first()
199
- assert obj.category == "bodhi"
200
-
201
-
202
- def test_categories_with_umb(datanommer_models):
203
- add(generate_message(topic="/topic/VirtualTopic.eng.brew.task.closed"))
204
- session.flush()
205
- obj = Message.query.first()
206
- assert obj.category == "brew"
207
-
208
-
209
- def test_grep_all(datanommer_models):
210
- example_message = generate_message()
211
- add(example_message)
212
- session.flush()
213
- t, p, r = Message.grep()
214
- assert t == 1
215
- assert p == 1
216
- assert len(r) == 1
217
- assert r[0].msg == example_message.body
218
-
219
-
220
- def test_grep_category(datanommer_models):
221
- example_message = generate_message(topic="org.fedoraproject.prod.bodhi.newupdate")
222
- add(example_message)
223
- session.flush()
224
- t, p, r = Message.grep(categories=["bodhi"])
225
- assert t == 1
226
- assert p == 1
227
- assert len(r) == 1
228
- assert r[0].msg == example_message.body
229
-
230
-
231
- def test_grep_not_category(datanommer_models):
232
- example_message = generate_message(topic="org.fedoraproject.prod.bodhi.newupdate")
233
- add(example_message)
234
- session.flush()
235
- t, p, r = Message.grep(not_categories=["bodhi"])
236
- assert t == 0
237
- assert p == 0
238
- assert len(r) == 0
239
-
240
-
241
- def test_add_headers(datanommer_models):
242
- example_headers = {"foo": "bar", "baz": 1, "wibble": ["zork", "zap"]}
243
- example_message = generate_message(
244
- topic="org.fedoraproject.prod.bodhi.newupdate", headers=example_headers
245
- )
246
- add(example_message)
247
- dbmsg = Message.query.first()
248
- assert dbmsg.headers["foo"] == "bar"
249
- assert dbmsg.headers["baz"] == 1
250
- assert dbmsg.headers["wibble"] == ["zork", "zap"]
251
-
252
-
253
- def test_grep_topics(datanommer_models):
254
- example_message = generate_message(topic="org.fedoraproject.prod.bodhi.newupdate")
255
- add(example_message)
256
- session.flush()
257
- t, p, r = Message.grep(topics=["org.fedoraproject.prod.bodhi.newupdate"])
258
- assert t == 1
259
- assert p == 1
260
- assert len(r) == 1
261
- assert r[0].msg == example_message.body
262
-
263
-
264
- def test_grep_not_topics(datanommer_models):
265
- example_message = generate_message(topic="org.fedoraproject.prod.bodhi.newupdate")
266
- add(example_message)
267
- session.flush()
268
- t, p, r = Message.grep(not_topics=["org.fedoraproject.prod.bodhi.newupdate"])
269
- assert t == 0
270
- assert p == 0
271
- assert len(r) == 0
272
-
273
-
274
- def test_grep_start_end_validation(datanommer_models):
275
- with pytest.raises(
276
- ValueError,
277
- match="Either both start and end must be specified or neither must be specified",
278
- ):
279
- Message.grep(start="2020-03-26")
280
- with pytest.raises(
281
- ValueError,
282
- match="Either both start and end must be specified or neither must be specified",
283
- ):
284
- Message.grep(end="2020-03-26")
285
-
286
-
287
- def test_grep_start_end(datanommer_models):
288
- example_message = generate_message()
289
- example_message._properties.headers["sent-at"] = "2021-04-01T00:00:01"
290
- add(example_message)
291
-
292
- bodhi_example_message = generate_bodhi_update_complete_message()
293
- bodhi_example_message._properties.headers["sent-at"] = "2021-06-01T00:00:01"
294
- add(bodhi_example_message)
295
-
296
- session.flush()
297
- total, pages, messages = Message.grep(start="2021-04-01", end="2021-05-01")
298
- assert total == 1
299
- assert pages == 1
300
- assert len(messages) == 1
301
- assert messages[0].msg == example_message.body
302
-
303
- total, pages, messages = Message.grep(start="2021-06-01", end="2021-07-01")
304
- assert total == 1
305
- assert pages == 1
306
- assert len(messages) == 1
307
- assert messages[0].msg == bodhi_example_message.body
308
-
309
-
310
- def test_grep_msg_id(datanommer_models):
311
- example_message = generate_message()
312
- add(example_message)
313
-
314
- bodhi_example_message = generate_bodhi_update_complete_message()
315
- add(bodhi_example_message)
316
-
317
- session.flush()
318
- total, pages, messages = Message.grep(msg_id=example_message.id)
319
- assert total == 1
320
- assert pages == 1
321
- assert len(messages) == 1
322
- assert messages[0].msg == example_message.body
323
-
324
- total, pages, messages = Message.grep(msg_id=bodhi_example_message.id)
325
- assert total == 1
326
- assert pages == 1
327
- assert len(messages) == 1
328
- assert messages[0].msg == bodhi_example_message.body
329
-
330
- total, pages, messages = Message.grep(msg_id="NOTAMESSAGEID")
331
- assert total == 0
332
- assert pages == 0
333
- assert len(messages) == 0
334
-
335
-
336
- def test_grep_users(datanommer_models):
337
- example_message = generate_message()
338
- add(example_message)
339
-
340
- bodhi_example_message = generate_bodhi_update_complete_message()
341
- add(bodhi_example_message)
342
-
343
- session.flush()
344
-
345
- total, pages, messages = Message.grep(users=["dudemcpants"])
346
-
347
- assert total == 1
348
- assert pages == 1
349
- assert len(messages) == 1
350
-
351
- assert messages[0].msg == bodhi_example_message.body
352
-
353
-
354
- def test_grep_not_users(datanommer_models):
355
- example_message = generate_message()
356
- add(example_message)
357
-
358
- bodhi_example_message = generate_bodhi_update_complete_message()
359
- add(bodhi_example_message)
360
-
361
- session.flush()
362
-
363
- total, pages, messages = Message.grep(not_users=["dudemcpants"])
364
-
365
- assert total == 1
366
- assert pages == 1
367
- assert len(messages) == 1
368
-
369
- assert messages[0].msg == example_message.body
370
-
371
-
372
- def test_grep_packages(datanommer_models):
373
- example_message = generate_message()
374
- add(example_message)
375
-
376
- bodhi_example_message = generate_bodhi_update_complete_message()
377
- add(bodhi_example_message)
378
-
379
- session.flush()
380
-
381
- total, pages, messages = Message.grep(packages=["kernel"])
382
-
383
- assert total == 1
384
- assert pages == 1
385
- assert len(messages) == 1
386
-
387
- assert messages[0].msg == bodhi_example_message.body
388
-
389
-
390
- def test_grep_not_packages(datanommer_models):
391
- example_message = generate_message()
392
- add(example_message)
393
-
394
- bodhi_example_message = generate_bodhi_update_complete_message()
395
- add(bodhi_example_message)
396
-
397
- session.flush()
398
-
399
- total, pages, messages = Message.grep(not_packages=["kernel"])
400
-
401
- assert total == 1
402
- assert pages == 1
403
- assert len(messages) == 1
404
-
405
- assert messages[0].msg == example_message.body
406
-
407
-
408
- def test_grep_contains(datanommer_models):
409
- example_message = generate_message(topic="org.fedoraproject.prod.bodhi.newupdate")
410
- add(example_message)
411
- session.flush()
412
- t, p, r = Message.grep(contains=["doing"])
413
- assert t == 1
414
- assert p == 1
415
- assert len(r) == 1
416
- assert r[0].msg == example_message.body
417
-
418
-
419
- def test_grep_rows_per_page_none(datanommer_models):
420
- for x in range(0, 200):
421
- example_message = generate_message()
422
- example_message.id = f"{x}"
423
- add(example_message)
424
-
425
- session.flush()
426
-
427
- total, pages, messages = Message.grep()
428
- assert total == 200
429
- assert pages == 2
430
- assert len(messages) == 100
431
-
432
- total, pages, messages = Message.grep(rows_per_page=None)
433
- assert total == 200
434
- assert pages == 1
435
- assert len(messages) == 200
436
-
437
-
438
- def test_grep_rows_per_page_zero(datanommer_models):
439
- for x in range(0, 200):
440
- example_message = generate_message()
441
- example_message.id = f"{x}"
442
- add(example_message)
443
- session.flush()
444
-
445
- try:
446
- total, pages, messages = Message.grep(rows_per_page=0)
447
- except ZeroDivisionError as e:
448
- assert False, e
449
- assert total == 200
450
- assert pages == 1
451
- assert len(messages) == 200
452
-
453
-
454
- def test_grep_defer(datanommer_models):
455
- example_message = generate_message()
456
- add(example_message)
457
-
458
- session.flush()
459
-
460
- total, pages, query = Message.grep(defer=True)
461
- assert isinstance(query, Query)
462
-
463
- assert query.all() == Message.grep()[2]
464
-
465
-
466
- def test_add_duplicate(datanommer_models, caplog):
467
- example_message = generate_message()
468
- add(example_message)
469
- add(example_message)
470
- # if no exception was thrown, then we successfully ignored the
471
- # duplicate message
472
- assert Message.query.count() == 1
473
- assert (
474
- "Skipping message from org.fedoraproject.test.a.nice.message"
475
- in caplog.records[0].message
476
- )
477
-
478
-
479
- def test_add_integrity_error(datanommer_models, mocker, caplog):
480
- mock_session_add = mocker.patch("datanommer.models.session.add")
481
- mock_session_add.side_effect = IntegrityError("asdf", "asd", "asdas")
482
- example_message = generate_message()
483
- add(example_message)
484
- assert "Unknown Integrity Error: message" in caplog.records[0].message
485
- assert Message.query.count() == 0
486
-
487
-
488
- def test_add_duplicate_package(datanommer_models):
489
- # Define a special message schema and register it
490
- class MessageWithPackages(fedora_message.Message):
491
- @property
492
- def packages(self):
493
- return ["pkg", "pkg"]
494
-
495
- fedora_message._schema_name_to_class["MessageWithPackages"] = MessageWithPackages
496
- fedora_message._class_to_schema_name[MessageWithPackages] = "MessageWithPackages"
497
- example_message = MessageWithPackages(
498
- topic="org.fedoraproject.test.a.nice.message",
499
- body={"encouragement": "You're doing great!"},
500
- headers=None,
501
- )
502
- try:
503
- add(example_message)
504
- except IntegrityError as e:
505
- assert False, e
506
- assert Message.query.count() == 1
507
- dbmsg = Message.query.first()
508
- assert len(dbmsg.packages) == 1
509
- assert dbmsg.packages[0].name == "pkg"
510
-
511
-
512
- def test_add_message_with_error_on_packages(datanommer_models, caplog):
513
- # Define a special message schema and register it
514
- class CustomMessage(fedora_message.Message):
515
- @property
516
- def packages(self):
517
- raise KeyError
518
-
519
- def _filter_headers(self):
520
- return {}
521
-
522
- fedora_message._schema_name_to_class["CustomMessage"] = CustomMessage
523
- fedora_message._class_to_schema_name[CustomMessage] = "CustomMessage"
524
- example_message = CustomMessage(
525
- topic="org.fedoraproject.test.a.nice.message",
526
- body={"encouragement": "You're doing great!"},
527
- headers=None,
528
- )
529
- try:
530
- add(example_message)
531
- except KeyError as e:
532
- assert False, e
533
- assert Message.query.count() == 1
534
- assert caplog.records[0].message == (
535
- f"Could not get the list of packages from a message on "
536
- f"org.fedoraproject.test.a.nice.message with id {example_message.id}"
537
- )
538
-
539
-
540
- def test_as_fedora_message_dict(datanommer_models):
541
- example_message = generate_message()
542
- add(example_message)
543
-
544
- dbmsg = Message.query.first()
545
-
546
- message_json = json.dumps(dbmsg.as_fedora_message_dict())
547
-
548
- # this should be the same as if we use the fedora_messaging dump function
549
- assert json.loads(fedora_message.dumps(example_message)) == json.loads(message_json)
550
-
551
-
552
- def test_as_fedora_message_dict_old_headers(datanommer_models):
553
- # Messages received with fedmsg don't have the sent-at header
554
- example_message = generate_message()
555
- add(example_message)
556
-
557
- dbmsg = Message.query.first()
558
- del dbmsg.headers["sent-at"]
559
-
560
- message_dict = dbmsg.as_fedora_message_dict()
561
- print(message_dict)
562
- print(json.loads(fedora_message.dumps(example_message)))
563
-
564
- # this should be the same as if we use the fedora_messaging dump function
565
- assert json.loads(fedora_message.dumps(example_message)) == message_dict
566
-
567
-
568
- def test_as_fedora_message_dict_no_headers(datanommer_models):
569
- # Messages can have no headers
570
- example_message = generate_message()
571
- add(example_message)
572
-
573
- dbmsg = Message.query.first()
574
- assert len(dbmsg.headers.keys()) == 3
575
-
576
- # Clear the headers
577
- dbmsg.headers = None
578
-
579
- try:
580
- message_dict = dbmsg.as_fedora_message_dict()
581
- except TypeError as e:
582
- assert False, e
583
-
584
- assert list(message_dict["headers"].keys()) == ["sent-at"]
585
-
586
-
587
- def test_as_dict(datanommer_models):
588
- add(generate_message())
589
- dbmsg = Message.query.first()
590
- message_dict = dbmsg.as_dict()
591
-
592
- # we should have 14 keys in this dict
593
- assert len(message_dict) == 14
594
- assert message_dict["msg"] == {"encouragement": "You're doing great!"}
595
- assert message_dict["topic"] == "org.fedoraproject.test.a.nice.message"
596
-
597
-
598
- def test_as_dict_with_users_and_packages(datanommer_models):
599
- add(generate_bodhi_update_complete_message())
600
- dbmsg = Message.query.first()
601
- message_dict = dbmsg.as_dict()
602
-
603
- assert message_dict["users"] == ["dudemcpants", "ryanlerch"]
604
- assert message_dict["packages"] == ["abrt-addon-python3", "kernel"]
605
-
606
-
607
- def test___json__deprecated(datanommer_models, caplog, mocker):
608
- mock_as_dict = mocker.patch("datanommer.models.Message.as_dict")
609
-
610
- add(generate_message())
611
-
612
- with pytest.warns(DeprecationWarning):
613
- Message.query.first().__json__()
614
-
615
- mock_as_dict.assert_called_once()
616
-
617
-
618
- def test_singleton_create(datanommer_models):
619
- Package.get_or_create("foobar")
620
- assert [p.name for p in Package.query.all()] == ["foobar"]
621
-
622
-
623
- def test_singleton_get_existing(datanommer_models):
624
- p1 = Package.get_or_create("foobar")
625
- # Clear the in-memory cache
626
- Package._cache.clear()
627
- p2 = Package.get_or_create("foobar")
628
- assert p1.id == p2.id
tox.ini DELETED
@@ -1,15 +0,0 @@
1
- [tox]
2
- envlist = py{37,38,39},licenses
3
- skipsdist = True
4
- isolated_build = true
5
- requires =
6
- poetry
7
- tox-poetry
8
-
9
- [testenv]
10
- commands = pytest -c ../pyproject.toml {posargs}
11
-
12
- [testenv:licenses]
13
- deps = poetry
14
- commands =
15
- {toxinidir}/../tools/run-liccheck.sh