fix(campaigns): let the connection test run on an unfunded account
Running the diagnostics against the real account returned exactly two blockers — no payment method, and no operator token — and both of them disabled the connection test, because the button was gated on `ready`. That is backwards. The connection test creates PAUSED objects and deletes them; Meta does not require a funding source for that, and an approval identity has nothing to do with it. Gating on full readiness put the check out of reach of the person who most needs it: someone with an unfunded account trying to find out whether Meta accepts what MaskanX sends at all. That question is the reason this feature exists — a live sync failed with subcode 4834011 and nobody could tell why. `can_smoke_test` reports the weaker condition, and `smoke_test_blocking` names what would actually stop it, so the UI can say which check to fix rather than "all of them". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -238,11 +238,32 @@ async def run_diagnostics(meta_factory, ad_account_id: str | None) -> dict[str,
|
||||
return _summarise(checks)
|
||||
|
||||
|
||||
# The checks the smoke test actually depends on.
|
||||
#
|
||||
# It creates PAUSED objects and deletes them, so it needs a token, an ad
|
||||
# account it can write to, and a Page to attach the creative to — but not a
|
||||
# payment method, and not an operator identity. Gating it on full readiness
|
||||
# would put it out of reach of the person who most needs it: someone whose
|
||||
# account has no funding yet, trying to find out whether Meta accepts what
|
||||
# MaskanX sends at all.
|
||||
_SMOKE_TEST_CHECKS = frozenset(
|
||||
{"Meta access token", "Token is valid", "Ad account", "Account status",
|
||||
"Facebook Page"},
|
||||
)
|
||||
|
||||
|
||||
def _summarise(checks: list[Check]) -> dict[str, Any]:
|
||||
failed = [c for c in checks if c.status == FAILED]
|
||||
blocked_names = {c.name for c in failed}
|
||||
return {
|
||||
"ready": not failed,
|
||||
"blocking": [c.name for c in failed],
|
||||
# Separate from `ready`: a launch needs everything, a connection
|
||||
# test needs only what it touches.
|
||||
"can_smoke_test": not (blocked_names & _SMOKE_TEST_CHECKS),
|
||||
"smoke_test_blocking": [
|
||||
c.name for c in failed if c.name in _SMOKE_TEST_CHECKS
|
||||
],
|
||||
"checks": [asdict(c) for c in checks],
|
||||
}
|
||||
|
||||
|
||||
@@ -362,3 +362,59 @@ def test_every_env_var_a_remedy_names_is_offered_in_settings():
|
||||
f"Environments, but they are not in _KEY_REGISTRY so no field is "
|
||||
f"shown for them."
|
||||
)
|
||||
|
||||
|
||||
# --- what blocks a launch is not what blocks a connection test ---
|
||||
|
||||
|
||||
async def test_a_missing_payment_method_does_not_block_the_connection_test(
|
||||
configured,
|
||||
):
|
||||
"""The smoke test creates PAUSED objects. Meta does not need a funding
|
||||
source for that, and someone with an unfunded account is exactly who
|
||||
needs to find out whether the connection works at all."""
|
||||
meta = FakeMeta(account={
|
||||
"id": "act_1", "name": "Maskan-MetaAds", "currency": "INR",
|
||||
"account_status": 1, "funding_source": None, "min_daily_budget": 9709,
|
||||
})
|
||||
|
||||
report = await run_diagnostics(lambda: meta, None)
|
||||
|
||||
assert report["ready"] is False
|
||||
assert "Payment method" in report["blocking"]
|
||||
assert report["can_smoke_test"] is True
|
||||
assert report["smoke_test_blocking"] == []
|
||||
|
||||
|
||||
async def test_missing_operator_tokens_do_not_block_the_connection_test(
|
||||
monkeypatch,
|
||||
):
|
||||
monkeypatch.setenv("META_ADS_ACCOUNT_ID", "act_1")
|
||||
monkeypatch.setenv("META_PAGE_ID", "page_1")
|
||||
monkeypatch.delenv("MASKANX_OPERATOR_TOKENS", raising=False)
|
||||
|
||||
report = await run_diagnostics(lambda: FakeMeta(), None)
|
||||
|
||||
assert "Operator identity" in report["blocking"]
|
||||
assert report["can_smoke_test"] is True
|
||||
|
||||
|
||||
async def test_an_unreachable_page_does_block_the_connection_test(configured):
|
||||
"""Sync attaches the creative to a Page, so this one it genuinely needs."""
|
||||
meta = FakeMeta()
|
||||
meta.fail_page = True
|
||||
|
||||
report = await run_diagnostics(lambda: meta, None)
|
||||
|
||||
assert report["can_smoke_test"] is False
|
||||
assert report["smoke_test_blocking"] == ["Facebook Page"]
|
||||
|
||||
|
||||
async def test_no_token_blocks_the_connection_test(monkeypatch):
|
||||
def no_token():
|
||||
raise MetaNotConfiguredError("META_ADS_ACCESS_TOKEN is not set.")
|
||||
|
||||
report = await run_diagnostics(no_token, None)
|
||||
|
||||
assert report["can_smoke_test"] is False
|
||||
assert report["smoke_test_blocking"] == ["Meta access token"]
|
||||
|
||||
Reference in New Issue
Block a user