test_model_defaults.py
python
sha256:4992098130166d191cefed0a2821d19cd3cdd3cf50867a4e715c2b30636826c7
fix: repair syntax errors from typing annotation cleanup
Sonnet 4.6
21 days ago
| 1 | """ORM model instances must have correct Python-side attribute values at |
| 2 | construction time — no DB flush or refresh required. |
| 3 | |
| 4 | This guards against the class of bug where service code calls a Pydantic |
| 5 | serialiser (e.g. _to_repo_response) on a freshly constructed ORM object and |
| 6 | receives None for columns that have a server_default or column-level default, |
| 7 | because SQLAlchemy's ``default=`` only fires at INSERT time in non-dataclass |
| 8 | models. |
| 9 | """ |
| 10 | from __future__ import annotations |
| 11 | |
| 12 | from datetime import datetime, timezone |
| 13 | |
| 14 | from muse.core.types import fake_id |
| 15 | |
| 16 | from musehub.db.musehub_auth_models import MusehubAuthKey |
| 17 | from musehub.db.musehub_collaborator_models import MusehubCollaborator |
| 18 | from musehub.db.musehub_identity_models import MusehubIdentity |
| 19 | from musehub.db.musehub_intel_models import MusehubSymbolIntel |
| 20 | from musehub.db.musehub_release_models import MusehubRelease |
| 21 | from musehub.db.musehub_repo_models import ( |
| 22 | MusehubBranch, |
| 23 | MusehubCommit, |
| 24 | MusehubMist, |
| 25 | MusehubRepo, |
| 26 | MusehubSession, |
| 27 | MusehubSnapshot, |
| 28 | ) |
| 29 | from musehub.db.musehub_social_models import ( |
| 30 | MusehubIssue, |
| 31 | MusehubIssueEvent, |
| 32 | MusehubProposal, |
| 33 | MusehubProposalComment, |
| 34 | ) |
| 35 | from musehub.db.musehub_webhook_models import MusehubWebhook |
| 36 | |
| 37 | _REPO_ID = fake_id("repo") |
| 38 | _OWNER_ID = fake_id("owner") |
| 39 | _BRANCH_ID = fake_id("branch") |
| 40 | _COMMIT_ID = fake_id("commit") |
| 41 | _SNAP_ID = fake_id("snapshot") |
| 42 | _IDENTITY_ID = fake_id("identity") |
| 43 | _ISSUE_ID = fake_id("issue") |
| 44 | _PROPOSAL_ID = fake_id("proposal") |
| 45 | _KEY_ID = fake_id("key") |
| 46 | _COLLAB_ID = fake_id("collab") |
| 47 | |
| 48 | |
| 49 | def test_musehub_repo_scalar_defaults_at_construction() -> None: |
| 50 | repo = MusehubRepo( |
| 51 | repo_id=_REPO_ID, |
| 52 | name="test", |
| 53 | owner="gabriel", |
| 54 | slug="test", |
| 55 | owner_user_id=_OWNER_ID, |
| 56 | ) |
| 57 | assert repo.default_branch == "main" |
| 58 | assert repo.visibility == "public" |
| 59 | assert repo.description == "" |
| 60 | assert repo.domain_id == "code" |
| 61 | assert repo.training_opt_out is False |
| 62 | assert repo.settings is None |
| 63 | assert repo.pushed_at is None |
| 64 | |
| 65 | |
| 66 | def test_musehub_repo_mutable_defaults_are_isolated() -> None: |
| 67 | """Each instance must get its own list/dict, not a shared mutable object.""" |
| 68 | r1 = MusehubRepo(repo_id=_REPO_ID, name="t", owner="x", slug="t", owner_user_id=_OWNER_ID) |
| 69 | r2 = MusehubRepo(repo_id=_REPO_ID, name="t", owner="x", slug="t", owner_user_id=_OWNER_ID) |
| 70 | assert r1.tags == [] |
| 71 | assert r1.domain_meta == {} |
| 72 | r1.tags.append("jazz") |
| 73 | assert r2.tags == [], "mutable default must not be shared between instances" |
| 74 | |
| 75 | |
| 76 | def test_musehub_repo_timestamps_set_at_construction() -> None: |
| 77 | repo = MusehubRepo( |
| 78 | repo_id=_REPO_ID, |
| 79 | name="t", |
| 80 | owner="x", |
| 81 | slug="t", |
| 82 | owner_user_id=_OWNER_ID, |
| 83 | ) |
| 84 | assert isinstance(repo.created_at, datetime) |
| 85 | assert isinstance(repo.updated_at, datetime) |
| 86 | |
| 87 | |
| 88 | def test_musehub_snapshot_defaults_at_construction() -> None: |
| 89 | snap = MusehubSnapshot( |
| 90 | snapshot_id=_SNAP_ID, |
| 91 | manifest_blob=b"\x80", |
| 92 | ) |
| 93 | assert snap.directories == [] |
| 94 | assert snap.entry_count == 0 |
| 95 | assert isinstance(snap.created_at, datetime) |
| 96 | |
| 97 | |
| 98 | def test_musehub_identity_defaults_at_construction() -> None: |
| 99 | identity = MusehubIdentity( |
| 100 | identity_id=_IDENTITY_ID, |
| 101 | handle="gabriel", |
| 102 | ) |
| 103 | assert identity.identity_type == "human" |
| 104 | assert identity.agent_capabilities == [] |
| 105 | assert identity.is_verified is False |
| 106 | assert identity.pinned_repo_ids == [] |
| 107 | assert isinstance(identity.created_at, datetime) |
| 108 | assert isinstance(identity.updated_at, datetime) |
| 109 | |
| 110 | |
| 111 | def test_musehub_issue_defaults_at_construction() -> None: |
| 112 | issue = MusehubIssue( |
| 113 | issue_id=_ISSUE_ID, |
| 114 | repo_id=_REPO_ID, |
| 115 | number=1, |
| 116 | title="bug: something broken", |
| 117 | ) |
| 118 | assert issue.body == "" |
| 119 | assert issue.state == "open" |
| 120 | assert issue.labels == [] |
| 121 | assert issue.symbol_anchors == [] |
| 122 | assert issue.commit_anchors == [] |
| 123 | assert issue.author == "" |
| 124 | assert issue.assignee is None |
| 125 | assert issue.agent_id == "" |
| 126 | assert issue.model_id == "" |
| 127 | assert isinstance(issue.created_at, datetime) |
| 128 | assert isinstance(issue.updated_at, datetime) |
| 129 | |
| 130 | |
| 131 | def test_musehub_proposal_defaults_at_construction() -> None: |
| 132 | proposal = MusehubProposal( |
| 133 | proposal_id=_PROPOSAL_ID, |
| 134 | repo_id=_REPO_ID, |
| 135 | proposal_number=1, |
| 136 | title="feat: new thing", |
| 137 | from_branch="feat/new-thing", |
| 138 | to_branch="main", |
| 139 | ) |
| 140 | assert proposal.body == "" |
| 141 | assert proposal.state == "open" |
| 142 | assert proposal.merge_commit_id is None |
| 143 | assert proposal.merged_at is None |
| 144 | assert proposal.author == "" |
| 145 | assert isinstance(proposal.created_at, datetime) |
| 146 | assert isinstance(proposal.updated_at, datetime) |
| 147 | assert proposal.domain_diff is None |
| 148 | assert proposal.risk_score is None |
| 149 | assert proposal.blast_delta is None |
| 150 | assert proposal.breakage_count == 0 |
| 151 | assert proposal.test_gap_count == 0 |
| 152 | assert proposal.symbols_changed == 0 |
| 153 | assert proposal.touched_symbols == [] |
| 154 | |
| 155 | |
| 156 | def test_musehub_auth_key_defaults_at_construction() -> None: |
| 157 | key = MusehubAuthKey( |
| 158 | key_id=_KEY_ID, |
| 159 | identity_id=_IDENTITY_ID, |
| 160 | public_key_b64="ed25519:AAAA", |
| 161 | fingerprint=fake_id("fp"), |
| 162 | ) |
| 163 | assert key.algorithm == "ed25519" |
| 164 | assert key.label == "" |
| 165 | assert isinstance(key.created_at, datetime) |
| 166 | assert key.last_used_at is None |
| 167 | |
| 168 | |
| 169 | def test_musehub_collaborator_defaults_at_construction() -> None: |
| 170 | collab = MusehubCollaborator( |
| 171 | id=_COLLAB_ID, |
| 172 | repo_id=_REPO_ID, |
| 173 | identity_handle="alice", |
| 174 | ) |
| 175 | assert collab.permission == "write" |
| 176 | assert isinstance(collab.invited_at, datetime) |
| 177 | assert collab.invited_by_handle is None |
| 178 | assert collab.accepted_at is None |
| 179 | |
| 180 | |
| 181 | def test_musehub_branch_defaults_at_construction() -> None: |
| 182 | branch = MusehubBranch( |
| 183 | branch_id=_BRANCH_ID, |
| 184 | repo_id=_REPO_ID, |
| 185 | name="main", |
| 186 | ) |
| 187 | assert branch.head_commit_id is None |
| 188 | |
| 189 | |
| 190 | def test_musehub_label_defaults_at_construction() -> None: |
| 191 | from musehub.db.musehub_label_models import MusehubLabel |
| 192 | |
| 193 | label = MusehubLabel( |
| 194 | id=fake_id("label"), |
| 195 | repo_id=_REPO_ID, |
| 196 | name="bug", |
| 197 | color="#d73a4a", |
| 198 | ) |
| 199 | assert label.description is None |
| 200 | assert isinstance(label.created_at, datetime) |
| 201 | |
| 202 | |
| 203 | def test_musehub_commit_defaults_at_construction() -> None: |
| 204 | commit = MusehubCommit( |
| 205 | commit_id=_COMMIT_ID, |
| 206 | branch="main", |
| 207 | message="feat: test", |
| 208 | author="gabriel", |
| 209 | timestamp=datetime.now(tz=timezone.utc), |
| 210 | ) |
| 211 | assert commit.parent_ids == [] |
| 212 | assert commit.snapshot_id is None |
| 213 | assert commit.agent_id == "" |
| 214 | assert commit.model_id == "" |
| 215 | assert commit.toolchain_id == "" |
| 216 | assert commit.commit_branch is None |
| 217 | assert commit.signature == "" |
| 218 | assert commit.signer_public_key == "" |
| 219 | assert commit.signer_key_id == "" |
| 220 | assert commit.sem_ver_bump == "none" |
| 221 | assert commit.breaking_changes == [] |
| 222 | assert commit.reviewed_by == [] |
| 223 | assert commit.test_runs == 0 |
| 224 | assert commit.prompt_hash == "" |
| 225 | assert commit.structured_delta is None |
| 226 | assert isinstance(commit.created_at, datetime) |
| 227 | |
| 228 | |
| 229 | # --------------------------------------------------------------------------- |
| 230 | # Phase 3 — classes that need MappedAsDataclass (mutable callable defaults) |
| 231 | # --------------------------------------------------------------------------- |
| 232 | |
| 233 | _EVENT_ID = fake_id("event") |
| 234 | _WEBHOOK_ID = fake_id("webhook") |
| 235 | _SESSION_ID = fake_id("session") |
| 236 | _COMMENT_ID = fake_id("pcomment") |
| 237 | _RELEASE_ID = fake_id("release") |
| 238 | _MIST_ID = fake_id("mist") |
| 239 | |
| 240 | |
| 241 | def test_musehub_issue_event_defaults_at_construction() -> None: |
| 242 | event = MusehubIssueEvent( |
| 243 | event_id=_EVENT_ID, |
| 244 | issue_id=_ISSUE_ID, |
| 245 | repo_id=_REPO_ID, |
| 246 | event_type="opened", |
| 247 | ) |
| 248 | assert event.actor == "" |
| 249 | assert event.payload == {} |
| 250 | assert isinstance(event.created_at, datetime) |
| 251 | |
| 252 | |
| 253 | def test_musehub_issue_event_payload_is_isolated() -> None: |
| 254 | e1 = MusehubIssueEvent(event_id=_EVENT_ID, issue_id=_ISSUE_ID, repo_id=_REPO_ID, event_type="opened") |
| 255 | e2 = MusehubIssueEvent(event_id=_EVENT_ID, issue_id=_ISSUE_ID, repo_id=_REPO_ID, event_type="opened") |
| 256 | e1.payload["key"] = "value" |
| 257 | assert e2.payload == {}, "payload must not be shared between instances" |
| 258 | |
| 259 | |
| 260 | def test_musehub_webhook_defaults_at_construction() -> None: |
| 261 | wh = MusehubWebhook( |
| 262 | webhook_id=_WEBHOOK_ID, |
| 263 | repo_id=_REPO_ID, |
| 264 | url="https://ci.example.com/hook", |
| 265 | ) |
| 266 | assert wh.events == [] |
| 267 | assert wh.secret == "" |
| 268 | assert wh.active is True |
| 269 | assert isinstance(wh.created_at, datetime) |
| 270 | assert isinstance(wh.updated_at, datetime) |
| 271 | |
| 272 | |
| 273 | def test_musehub_webhook_events_is_isolated() -> None: |
| 274 | w1 = MusehubWebhook(webhook_id=_WEBHOOK_ID, repo_id=_REPO_ID, url="https://a.example.com") |
| 275 | w2 = MusehubWebhook(webhook_id=_WEBHOOK_ID, repo_id=_REPO_ID, url="https://b.example.com") |
| 276 | w1.events.append("push") |
| 277 | assert w2.events == [], "events must not be shared between instances" |
| 278 | |
| 279 | |
| 280 | def test_musehub_session_defaults_at_construction() -> None: |
| 281 | session = MusehubSession( |
| 282 | session_id=_SESSION_ID, |
| 283 | repo_id=_REPO_ID, |
| 284 | started_at=datetime.now(tz=timezone.utc), |
| 285 | ) |
| 286 | assert session.participants == [] |
| 287 | assert session.commits == [] |
| 288 | assert session.schema_version == "1" |
| 289 | assert session.location == "" |
| 290 | assert session.intent == "" |
| 291 | assert session.notes == "" |
| 292 | assert session.is_active is False |
| 293 | assert isinstance(session.created_at, datetime) |
| 294 | |
| 295 | |
| 296 | def test_musehub_session_lists_are_isolated() -> None: |
| 297 | now = datetime.now(tz=timezone.utc) |
| 298 | s1 = MusehubSession(session_id=_SESSION_ID, repo_id=_REPO_ID, started_at=now) |
| 299 | s2 = MusehubSession(session_id=_SESSION_ID, repo_id=_REPO_ID, started_at=now) |
| 300 | s1.participants.append("gabriel") |
| 301 | assert s2.participants == [], "participants must not be shared between instances" |
| 302 | |
| 303 | |
| 304 | def test_musehub_proposal_comment_defaults_at_construction() -> None: |
| 305 | comment = MusehubProposalComment( |
| 306 | comment_id=_COMMENT_ID, |
| 307 | proposal_id=_PROPOSAL_ID, |
| 308 | repo_id=_REPO_ID, |
| 309 | author="gabriel", |
| 310 | body="looks good", |
| 311 | ) |
| 312 | assert comment.dimension_ref == {} |
| 313 | assert comment.symbol_address is None |
| 314 | assert comment.parent_comment_id is None |
| 315 | assert isinstance(comment.created_at, datetime) |
| 316 | |
| 317 | |
| 318 | def test_musehub_proposal_comment_dimension_ref_is_isolated() -> None: |
| 319 | kwargs = dict(comment_id=_COMMENT_ID, proposal_id=_PROPOSAL_ID, repo_id=_REPO_ID, author="x", body="y") |
| 320 | c1 = MusehubProposalComment(**kwargs) |
| 321 | c2 = MusehubProposalComment(**kwargs) |
| 322 | c1.dimension_ref["dim"] = "harmony" |
| 323 | assert c2.dimension_ref == {}, "dimension_ref must not be shared between instances" |
| 324 | |
| 325 | |
| 326 | def test_musehub_release_defaults_at_construction() -> None: |
| 327 | release = MusehubRelease( |
| 328 | release_id=_RELEASE_ID, |
| 329 | repo_id=_REPO_ID, |
| 330 | tag="v1.0.0", |
| 331 | ) |
| 332 | assert release.download_urls == {} |
| 333 | assert release.title == "" |
| 334 | assert release.body == "" |
| 335 | assert release.is_draft is False |
| 336 | assert release.channel == "stable" |
| 337 | assert release.semver_major == 0 |
| 338 | assert isinstance(release.created_at, datetime) |
| 339 | assert isinstance(release.updated_at, datetime) |
| 340 | |
| 341 | |
| 342 | def test_musehub_release_download_urls_is_isolated() -> None: |
| 343 | r1 = MusehubRelease(release_id=_RELEASE_ID, repo_id=_REPO_ID, tag="v1.0.0") |
| 344 | r2 = MusehubRelease(release_id=_RELEASE_ID, repo_id=_REPO_ID, tag="v1.0.0") |
| 345 | r1.download_urls["linux"] = "https://example.com/linux.tar.gz" |
| 346 | assert r2.download_urls == {}, "download_urls must not be shared between instances" |
| 347 | |
| 348 | |
| 349 | def test_musehub_mist_defaults_at_construction() -> None: |
| 350 | mist = MusehubMist( |
| 351 | mist_id=_MIST_ID, |
| 352 | repo_id=_REPO_ID, |
| 353 | owner="gabriel", |
| 354 | filename="track.mid", |
| 355 | content="<binary>", |
| 356 | ) |
| 357 | assert mist.tags == [] |
| 358 | assert mist.symbol_anchors == [] |
| 359 | assert mist.artifact_type == "unknown" |
| 360 | assert mist.visibility == "public" |
| 361 | assert mist.fork_depth == 0 |
| 362 | assert mist.view_count == 0 |
| 363 | assert isinstance(mist.created_at, datetime) |
| 364 | assert isinstance(mist.updated_at, datetime) |
| 365 | |
| 366 | |
| 367 | def test_musehub_mist_lists_are_isolated() -> None: |
| 368 | kwargs = dict(mist_id=_MIST_ID, repo_id=_REPO_ID, owner="gabriel", filename="f.mid", content="x") |
| 369 | m1 = MusehubMist(**kwargs) |
| 370 | m2 = MusehubMist(**kwargs) |
| 371 | m1.tags.append("jazz") |
| 372 | assert m2.tags == [], "tags must not be shared between instances" |
| 373 | |
| 374 | |
| 375 | def test_musehub_symbol_intel_defaults_at_construction() -> None: |
| 376 | intel = MusehubSymbolIntel( |
| 377 | repo_id=_REPO_ID, |
| 378 | address="src/engine.py::AudioEngine", |
| 379 | ) |
| 380 | assert intel.blast_top == [] |
| 381 | assert intel.weekly == [] |
| 382 | assert intel.churn == 0 |
| 383 | assert intel.blast == 0 |
| 384 | assert intel.gravity == 0.0 |
| 385 | |
| 386 | |
| 387 | def test_musehub_symbol_intel_arrays_are_isolated() -> None: |
| 388 | i1 = MusehubSymbolIntel(repo_id=_REPO_ID, address="a.py::Foo") |
| 389 | i2 = MusehubSymbolIntel(repo_id=_REPO_ID, address="b.py::Bar") |
| 390 | i1.blast_top.append("x.py::Dep") |
| 391 | assert i2.blast_top == [], "blast_top must not be shared between instances" |
File History
2 commits
sha256:4992098130166d191cefed0a2821d19cd3cdd3cf50867a4e715c2b30636826c7
fix: repair syntax errors from typing annotation cleanup
Sonnet 4.6
21 days ago
sha256:ef10830ce231e0a20efcb0e2586cb879471247e916616e6fdd0d51df459e2595
fix: typing audit — 0 violations, 0 untyped defs across all…
Sonnet 4.6
minor
⚠
21 days ago