Skip to content

urls & post_smoke

The two HTTP-level checks. Both spin up the Django test client with force_login, both wrap requests in override_settings(ALLOWED_HOSTS=…) so testserver is always accepted, both fail CRITICAL on HTTP 5xx.

They differ on method: urls only hits GET, post_smoke submits POST payloads it generates from each view's form_class.

urls

Crawls urlpatterns depth-first, skips routes with URL kwargs (<int:pk>, <uuid:id>, …), and probes the rest under every role configured:

[tool.django-doctor.urls]
roles = ["anonymous", "authenticated", "staff", "superuser"]
skip  = ["admin:*", "djdt:*", "rp-initiated-logout"]
timeout = 10

Findings:

  • urls.get.5xx — HTTP 500+ returned.
  • urls.get.exception — view raised before returning a response.

Creating the probe users

urls creates three users if they don't exist: doctor_user, doctor_staff, doctor_superuser. On production snapshots you want to point at a dev database — user rows (with generated passwords) do persist.

post_smoke

Discovers every CreateView / UpdateView reachable without URL kwargs, extracts the form_class (or falls back to modelform_factory(model, fields=fields)), auto-generates a minimal valid payload via make_form_fixture, and submits it.

Safety

Every request runs inside transaction.atomic() + savepoint_rollback. Even when the view reaches .save() and commits would happen, the savepoint is rolled back afterwards, so the database is not mutated.

You still want to point this at a dev database — it writes a transient superuser (doctor_post_smoke) and some views may trigger external side effects (file uploads to S3, email sends, webhook calls) that the savepoint doesn't reach.

Skip list

[tool.django-doctor.post_smoke]
skip = ["legacy:*", "file-upload-special"]

Findings

  • post_smoke.5xx — HTTP 500+ after the POST.
  • post_smoke.exception — view raised Python exception.
  • post_smoke.fixture_error (WARNING) — could not build a payload.
  • post_smoke.partial_fixture (INFO) — required field could not be auto-filled (FileField, empty ModelChoice queryset, …).
  • post_smoke.stale_db.create (since 0.9) — CreateView returned 2xx/3xx but Model.objects.count() did not increase. The view reports success while silently dropping the submission. Common causes: a signal raised and was swallowed by outer middleware, form_valid() forgot to call super(), or an atomic wrapper rolled back.
  • post_smoke.stale_db.update (since 0.9) — UpdateView returned 2xx/3xx but the target row did not reflect the POSTed values. The runtime twin of the static update_fields.drops_computed check — catches cases static analysis can't resolve (dynamic receivers, helper-function indirection, …).

Real bug this caught on altiusone

[post_smoke:post_smoke.exception] operation-create (OperationTVACreateView)
  POST raised IntegrityError: null value in column "montant_ttc" of
  relation "operations_tva" violates not-null constraint

Root cause: OperationTVAForm.clean() assigned cleaned_data["montant_ttc"] but the key wasn't in Meta.fields. Django silently dropped it.