krz/hutch-stats
Server-side utility for calculating contributions for sourcehut users.
clone: git clone https://gitbay.org/krz/hutch-stats.git
main: tests/test_migrations.py · raw
1from pathlib import Path
2
3from alembic import command
4from alembic.config import Config
5from sqlalchemy import create_engine, inspect, text
6
7from srht_contrib.config import Settings
8
9
10def test_alembic_upgrade_creates_schema(tmp_path) -> None:
11 database_path = tmp_path / "migrated.db"
12 database_url = f"sqlite:///{database_path}"
13 config = Config(str(Path(__file__).resolve().parent.parent / "alembic.ini"))
14 config.set_main_option("script_location", str(Path(__file__).resolve().parent.parent / "alembic"))
15 config.set_main_option("sqlalchemy.url", database_url)
16
17 command.upgrade(config, "head")
18
19 inspector = inspect(create_engine(database_url))
20 assert sorted(inspector.get_table_names()) == [
21 "actor_aliases",
22 "alembic_version",
23 "contribution_events",
24 "discovered_repositories",
25 "service_backfill_states",
26 "sync_states",
27 "tracked_actors",
28 "tracked_repositories",
29 ]
30
31
32def test_alembic_upgrade_adopts_legacy_schema(tmp_path) -> None:
33 database_path = tmp_path / "legacy.db"
34 database_url = f"sqlite:///{database_path}"
35 engine = create_engine(database_url)
36 with engine.begin() as connection:
37 connection.execute(
38 text(
39 """
40 CREATE TABLE contribution_events (
41 id INTEGER NOT NULL PRIMARY KEY,
42 service VARCHAR(32) NOT NULL,
43 event_type VARCHAR(64) NOT NULL,
44 actor VARCHAR(255) NOT NULL,
45 repo_name VARCHAR(255),
46 resource_id VARCHAR(255) NOT NULL,
47 external_uid VARCHAR(255) NOT NULL,
48 occurred_at DATETIME NOT NULL,
49 weight FLOAT NOT NULL,
50 raw_payload_json JSON,
51 CONSTRAINT uq_contribution_event_service_uid UNIQUE (service, external_uid)
52 )
53 """
54 )
55 )
56 connection.execute(text("CREATE INDEX ix_contribution_events_actor_occurred_at ON contribution_events (actor, occurred_at)"))
57 connection.execute(text("CREATE INDEX ix_contribution_events_service_occurred_at ON contribution_events (service, occurred_at)"))
58 connection.execute(
59 text(
60 """
61 CREATE TABLE sync_states (
62 id INTEGER NOT NULL PRIMARY KEY,
63 service VARCHAR(32) NOT NULL,
64 actor VARCHAR(255) NOT NULL,
65 cursor_value TEXT,
66 updated_at DATETIME NOT NULL,
67 CONSTRAINT uq_sync_state_service_actor UNIQUE (service, actor)
68 )
69 """
70 )
71 )
72 connection.execute(
73 text(
74 """
75 CREATE TABLE tracked_repositories (
76 id INTEGER NOT NULL PRIMARY KEY,
77 service VARCHAR(32) NOT NULL,
78 repo_name VARCHAR(255) NOT NULL,
79 actor VARCHAR(255),
80 CONSTRAINT uq_tracked_repository_service_name UNIQUE (service, repo_name)
81 )
82 """
83 )
84 )
85 connection.execute(
86 text(
87 """
88 INSERT INTO tracked_repositories (id, service, repo_name, actor)
89 VALUES (1, 'git', 'Hutch', NULL)
90 """
91 )
92 )
93 connection.execute(
94 text(
95 """
96 CREATE TABLE actor_aliases (
97 id INTEGER NOT NULL PRIMARY KEY,
98 canonical_actor VARCHAR(255) NOT NULL,
99 alias VARCHAR(255) NOT NULL,
100 CONSTRAINT uq_actor_alias_alias UNIQUE (alias)
101 )
102 """
103 )
104 )
105
106 config = Config(str(Path(__file__).resolve().parent.parent / "alembic.ini"))
107 config.set_main_option("script_location", str(Path(__file__).resolve().parent.parent / "alembic"))
108 config.set_main_option("sqlalchemy.url", database_url)
109
110 command.upgrade(config, "head")
111
112 inspector = inspect(create_engine(database_url))
113 columns = {column["name"]: column for column in inspector.get_columns("tracked_repositories")}
114 tracked_actor_columns = {column["name"] for column in inspector.get_columns("tracked_actors")}
115 unique_constraints = {constraint["name"] for constraint in inspector.get_unique_constraints("tracked_repositories")}
116 with create_engine(database_url).connect() as connection:
117 actor = connection.execute(text("SELECT actor FROM tracked_repositories WHERE id = 1")).scalar_one()
118
119 assert columns["actor"]["nullable"] is False
120 assert "uq_tracked_repository_service_actor_name" in unique_constraints
121 assert actor == Settings().default_actor
122 assert "discovered_repositories" in inspector.get_table_names()
123 assert "tracked_actors" in inspector.get_table_names()
124 assert "service_backfill_states" in inspector.get_table_names()
125 assert {
126 "discovery_state",
127 "queued_for_discovery_at",
128 "priority_boosted_at",
129 "next_poll_after",
130 "last_claimed_at",
131 "poll_attempts",
132 } <= tracked_actor_columns
133
134
135def test_alembic_prefers_database_url_from_environment(tmp_path, monkeypatch) -> None:
136 database_path = tmp_path / "env-selected.db"
137 database_url = f"sqlite:///{database_path}"
138 config = Config(str(Path(__file__).resolve().parent.parent / "alembic.ini"))
139 config.set_main_option("script_location", str(Path(__file__).resolve().parent.parent / "alembic"))
140 monkeypatch.setenv("DATABASE_URL", database_url)
141
142 command.upgrade(config, "head")
143
144 inspector = inspect(create_engine(database_url))
145 assert "actor_aliases" in inspector.get_table_names()
146 assert "discovered_repositories" in inspector.get_table_names()
147 assert "tracked_actors" in inspector.get_table_names()
148 assert "service_backfill_states" in inspector.get_table_names()