Skip to content

Commit bf0a091

Browse files
fricklerhandwerkadekoder
authored andcommitted
refactor: split metadata creation in fixture
This allows more clearly expressing that we're modeling the fixtures after how ingestion works. Note how `make_drv` now gets "parsed data" including the metadata. It adds the separate homepage and description rows, and then completes the derivation.
1 parent 9444cbf commit bf0a091

8 files changed

Lines changed: 109 additions & 93 deletions

File tree

nix/tests/default.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ pkgs.testers.runNixOSTest {
186186
NixEvaluation,
187187
NixDerivation,
188188
NixDerivationMeta,
189+
NixDerivationHomepage,
190+
NixDerivationDescription,
189191
NixMaintainer,
190192
NixLicense,
191193
)
@@ -195,6 +197,8 @@ pkgs.testers.runNixOSTest {
195197
for model, count in [
196198
(NixDerivation, 3),
197199
(NixDerivationMeta, 3),
200+
(NixDerivationHomepage, 1),
201+
(NixDerivationDescription, 1),
198202
(NixMaintainer, 1),
199203
(NixLicense, 1),
200204
]:

src/shared/cache_suggestions.py

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from django.db.models import Prefetch, Q
1111
from pydantic import BaseModel, field_serializer
1212

13-
from shared.models import NixDerivation, NixDerivationDescription, NixMaintainer
13+
from shared.models import NixDerivation, NixMaintainer
1414
from shared.models.cached import CachedSuggestions
1515
from shared.models.cve import AffectedProduct, Metric, Reference, Version
1616
from shared.models.linkage import (
@@ -20,7 +20,6 @@
2020
ReferenceUrlOverlay,
2121
)
2222
from shared.models.nix_evaluation import (
23-
NixDerivationMeta,
2423
get_major_channel,
2524
)
2625

@@ -304,23 +303,6 @@ def get_src_position(derivation: NixDerivation) -> str | None:
304303
return None
305304

306305

307-
def _get_description(metadata: NixDerivationMeta) -> str | None:
308-
"""
309-
Return the description for a derivation metadata record.
310-
Falls back to the legacy ``NixDerivationMeta.description`` column so that
311-
rows created before the migration continue to work until ``--null-after``
312-
has been run.
313-
"""
314-
315-
description: str | None = None
316-
try:
317-
description = metadata.description_obj.text
318-
except NixDerivationDescription.DoesNotExist:
319-
description = metadata.description
320-
321-
return description
322-
323-
324306
def channel_structure(
325307
version_constraints: list[Version], derivations: list[NixDerivation]
326308
) -> dict[str, CachedSuggestion.Package]:
@@ -334,9 +316,9 @@ def channel_structure(
334316
if attribute_path not in packages:
335317
packages[attribute_path] = CachedSuggestion.Package()
336318
if derivation.metadata:
337-
description = _get_description(derivation.metadata)
338-
if description:
339-
packages[attribute_path].description = description
319+
packages[
320+
attribute_path
321+
].description = derivation.metadata.get_description()
340322
packages[attribute_path].maintainers = [
341323
CachedSuggestion.Maintainer.model_validate(to_dict(m))
342324
for m in derivation.metadata.prefetched_maintainers

src/shared/models/nix_evaluation.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,22 @@ class Meta: # type: ignore[override]
132132
)
133133
]
134134

135+
def get_description(self) -> str | None:
136+
"""
137+
Return the description for a derivation metadata record.
138+
Falls back to the legacy ``NixDerivationMeta.description`` column so that
139+
rows created before the migration continue to work until ``--null-after``
140+
has been run.
141+
"""
142+
143+
description: str | None = None
144+
try:
145+
description = self.description_obj.text
146+
except NixDerivationDescription.DoesNotExist:
147+
description = self.description
148+
149+
return description
150+
135151

136152
class NixDerivationHomepage(models.Model):
137153
"""

src/shared/tests/conftest.py

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,36 @@ def maintainer(make_maintainer: Callable[..., NixMaintainer]) -> NixMaintainer:
233233
return make_maintainer()
234234

235235

236+
@pytest.fixture
237+
def make_metadata(maintainer: NixMaintainer) -> Callable[..., NixDerivationMeta]:
238+
def wrapped(
239+
maintainer: NixMaintainer = maintainer,
240+
) -> NixDerivationMeta:
241+
meta = NixDerivationMeta.objects.create(
242+
homepage="https://example.com",
243+
description="My package",
244+
insecure=False,
245+
available=True,
246+
broken=False,
247+
unfree=False,
248+
unsupported=False,
249+
)
250+
meta.maintainers.add(maintainer)
251+
return meta
252+
253+
return wrapped
254+
255+
256+
@pytest.fixture
257+
def metadata(make_metadata: Callable[..., NixDerivationMeta]) -> NixDerivationMeta:
258+
return make_metadata()
259+
260+
236261
@pytest.fixture
237262
def make_drv(
238-
maintainer: NixMaintainer,
239263
evaluation: NixEvaluation,
264+
maintainer: NixMaintainer,
265+
make_metadata: Callable[..., NixDerivationMeta],
240266
) -> Callable[..., NixDerivation]:
241267
def wrapped(
242268
# Like `pname` in `mkDerivation`
@@ -245,22 +271,21 @@ def wrapped(
245271
system: str = "x86_64-linux",
246272
attribute: str | None = None,
247273
evaluation: NixEvaluation = evaluation,
274+
meta: NixDerivationMeta | None = None,
248275
maintainer: NixMaintainer = maintainer,
249276
) -> NixDerivation:
250-
description = "Dummy derivation"
251-
homepage = "https://nix.example.com"
252-
meta = NixDerivationMeta.objects.create(
253-
description=description,
254-
homepage=homepage,
255-
insecure=False,
256-
available=True,
257-
broken=False,
258-
unfree=False,
259-
unsupported=False,
277+
278+
if meta is None:
279+
meta = make_metadata(maintainer=maintainer)
280+
281+
# get_or_create so callers can safely pass an existing meta that already
282+
# has description/homepage rows (e.g. from make_metadata or a previous call).
283+
NixDerivationDescription.objects.get_or_create(
284+
metadata=meta, defaults={"text": meta.description}
285+
)
286+
NixDerivationHomepage.objects.get_or_create(
287+
metadata=meta, defaults={"url": meta.homepage}
260288
)
261-
NixDerivationDescription.objects.create(metadata=meta, text=description)
262-
NixDerivationHomepage.objects.create(metadata=meta, url=homepage)
263-
meta.maintainers.add(maintainer)
264289

265290
if attribute is None:
266291
attribute = pname
@@ -459,17 +484,3 @@ def wrapped(
459484
return create_package_subscription_notifications(suggestion)
460485

461486
return wrapped
462-
463-
464-
@pytest.fixture
465-
def legacy_meta(db: None) -> NixDerivationMeta:
466-
"""Create a NixDerivationMeta with only the legacy columns set, no new-table rows."""
467-
return NixDerivationMeta.objects.create(
468-
homepage="https://example.com",
469-
description="My package",
470-
insecure=False,
471-
available=True,
472-
broken=False,
473-
unfree=False,
474-
unsupported=False,
475-
)

src/shared/tests/test_auth.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ def test_ismaintainer_respects_channel_tip(
2323
channel=channel,
2424
commit_sha1=channel.head_sha1_commit if at_tip else None, # otherwise random
2525
)
26-
make_drv(evaluation=evaluation, maintainer=make_maintainer_from_user(user))
26+
drv = make_drv(evaluation=evaluation)
27+
assert drv.metadata
28+
drv.metadata.maintainers.add(make_maintainer_from_user(user))
2729
assert ismaintainer(user) is expected
2830

2931

src/shared/tests/test_migrate_homepage_description.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,40 @@
1111

1212

1313
def test_migrates_homepage_and_description_to_new_table(
14-
legacy_meta: NixDerivationMeta,
14+
metadata: NixDerivationMeta,
1515
) -> None:
1616

17+
pre_migration_description = metadata.description
18+
pre_migration_homepage = metadata.homepage
1719
call_command("migrate_homepage_description", stdout=StringIO())
1820

1921
assert NixDerivationHomepage.objects.filter(
20-
metadata=legacy_meta, url="https://example.com"
22+
metadata=metadata, url=pre_migration_homepage
2123
).exists()
2224
assert NixDerivationDescription.objects.filter(
23-
metadata=legacy_meta, text="My package"
25+
metadata=metadata, text=pre_migration_description
2426
).exists()
2527

26-
legacy_meta.refresh_from_db()
27-
assert legacy_meta.homepage == "https://example.com"
28-
assert legacy_meta.description == "My package"
28+
metadata.refresh_from_db()
29+
assert metadata.homepage == pre_migration_homepage
30+
assert metadata.description == pre_migration_description
2931

3032

3133
def test_null_after_clears_homepage_and_description_column(
32-
legacy_meta: NixDerivationMeta,
34+
metadata: NixDerivationMeta,
3335
) -> None:
3436

3537
call_command("migrate_homepage_description", "--null-after", stdout=StringIO())
3638

37-
legacy_meta.refresh_from_db()
38-
assert legacy_meta.homepage is None
39-
assert legacy_meta.description is None
39+
metadata.refresh_from_db()
40+
assert metadata.homepage is None
41+
assert metadata.description is None
4042

41-
assert NixDerivationHomepage.objects.filter(metadata=legacy_meta).exists()
42-
assert NixDerivationDescription.objects.filter(metadata=legacy_meta).exists()
43+
assert NixDerivationHomepage.objects.filter(metadata=metadata).exists()
44+
assert NixDerivationDescription.objects.filter(metadata=metadata).exists()
4345

4446

45-
def test_command_is_idempotent_homepage(legacy_meta: NixDerivationMeta) -> None:
47+
def test_command_is_idempotent(metadata: NixDerivationMeta) -> None:
4648

4749
call_command("migrate_homepage_description", stdout=StringIO())
4850
call_command("migrate_homepage_description", stdout=StringIO())

src/shared/tests/test_notifications.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22

33
from django.contrib.auth.models import User
44

5+
from shared.models.linkage import CVEDerivationClusterProposal, ProvenanceFlags
56
from shared.models.nix_evaluation import (
67
NixDerivation,
78
NixMaintainer,
89
)
9-
from webview.models import Notification
10+
from shared.notify_users import create_package_subscription_notifications
1011

1112

1213
def test_github_username_changed(
1314
user: User,
14-
make_maintainer_from_user: Callable[..., NixMaintainer],
1515
make_drv: Callable[..., NixDerivation],
16-
make_package_notification: Callable[..., list[Notification]],
16+
make_cached_suggestion: Callable[..., CVEDerivationClusterProposal],
17+
make_maintainer_from_user: Callable[..., NixMaintainer],
1718
) -> None:
1819
"""
1920
Check that notifications get sent even if the user's GitHub handle changed after first login.
@@ -23,5 +24,7 @@ def test_github_username_changed(
2324
# GitHub username could have changed between evaluations.
2425
maintainer.github = maintainer.github + "-new"
2526
maintainer.save()
26-
notifications = make_package_notification(drv)
27+
28+
suggestion = make_cached_suggestion(drvs={drv: ProvenanceFlags.PACKAGE_NAME_MATCH})
29+
notifications = create_package_subscription_notifications(suggestion)
2730
assert notifications

src/shared/tests/test_suggestion_caching.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -86,52 +86,48 @@ def test_cache_stale_check(
8686
assert cached.is_stale is True
8787

8888

89-
def test_cache_description_from_new_table(cve: Container, drv: NixDerivation) -> None:
89+
def test_cache_description_from_new_table(
90+
suggestion: CVEDerivationClusterProposal,
91+
) -> None:
9092
"""
91-
When a NixDerivationDescription row exists for the derivation's metadata,
93+
When a NixDerivationDescription row exists for the derivatipon's metadata,
9294
the cached payload must use that value as the package description.
9395
"""
96+
drv = suggestion.derivations.first()
97+
98+
assert drv
99+
assert drv.metadata
100+
101+
description_obj = drv.metadata.description_obj
102+
description_obj.text = "New Package"
103+
description_obj.save()
94104

95-
suggestion = CVEDerivationClusterProposal.objects.create(
96-
status="pending",
97-
cve=cve.cve,
98-
)
99-
DerivationClusterProposalLink.objects.create(
100-
proposal=suggestion,
101-
derivation=drv,
102-
provenance_flags=ProvenanceFlags.PACKAGE_NAME_MATCH,
103-
)
104105
cache_new_suggestions(suggestion)
105106

106107
cached = CachedSuggestions.objects.get(proposal=suggestion)
107108
pkg = cached.payload["packages"][drv.attribute]
108-
assert pkg["description"] == "Dummy derivation"
109+
assert pkg["description"] == description_obj.text
109110

110111

111112
def test_cache_description_falls_back_when_no_new_table_entry(
112-
cve: Container,
113-
drv: NixDerivation,
113+
suggestion: CVEDerivationClusterProposal,
114114
) -> None:
115115
"""
116116
When no NixDerivationDescription row exists (unmigrated derivation),
117117
the cached payload must fall back to NixDerivationMeta.description.
118118
"""
119+
120+
drv = suggestion.derivations.first()
121+
assert drv
122+
assert drv.metadata
123+
119124
NixDerivationDescription.objects.filter(metadata=drv.metadata).delete()
120125
# Confirm the legacy column still holds the value
121126
assert drv.metadata is not None
122-
assert drv.metadata.description == "Dummy derivation"
127+
assert drv.metadata.description
123128

124-
suggestion = CVEDerivationClusterProposal.objects.create(
125-
status="pending",
126-
cve=cve.cve,
127-
)
128-
DerivationClusterProposalLink.objects.create(
129-
proposal=suggestion,
130-
derivation=drv,
131-
provenance_flags=ProvenanceFlags.PACKAGE_NAME_MATCH,
132-
)
133129
cache_new_suggestions(suggestion)
134130

135131
cached = CachedSuggestions.objects.get(proposal=suggestion)
136132
pkg = cached.payload["packages"][drv.attribute]
137-
assert pkg["description"] == "Dummy derivation"
133+
assert pkg["description"] == drv.metadata.description

0 commit comments

Comments
 (0)