intent/tests/test_implementations.py

129 lines
5 KiB
Python
Raw Permalink Normal View History

import pytest
from django.core.exceptions import PermissionDenied, ValidationError
from django.urls import reverse
from intent.features.services import create_feature, update_feature
from intent.implementations import services
from intent.implementations.models import FeatureImplStatus, FeatureStatus
P = {"org_slug": "catbriar", "project_slug": "intent"}
def iurl(name, impl=None):
kwargs = dict(P)
if impl is not None:
kwargs["impl_id"] = impl.pk
return reverse(f"implementations:{name}", kwargs=kwargs)
@pytest.fixture
def impl(open_project, owner):
return services.create_implementation(
open_project, owner, name="Web app", url="https://forge.example/acme/web"
)
@pytest.fixture
def features(open_project, owner):
return [create_feature(open_project, owner, title=t) for t in ("Sign in", "Sign out")]
def test_only_editors_create_and_urls_must_be_http(open_project, collaborator, owner, impl):
with pytest.raises(PermissionDenied):
services.create_implementation(
open_project, collaborator, name="X", url="https://x.example"
)
with pytest.raises(ValidationError):
services.create_implementation(open_project, owner, name="X", url="javascript:alert(1)")
with pytest.raises(ValidationError, match="already"):
services.create_implementation(open_project, owner, name="web APP", url="https://x.example")
def test_status_is_stamped_and_goes_behind(impl, features, owner, editor):
sign_in, sign_out = features
changed = services.set_statuses(
impl,
editor,
{sign_in: (FeatureStatus.SHIPPED, False), sign_out: (FeatureStatus.NOT_STARTED, False)},
)
assert changed == 1 # "Not started" is the default, so no row is written for it.
row = FeatureImplStatus.objects.get()
assert (row.status, row.feature_revision, row.is_behind) == ("shipped", 1, False)
update_feature(sign_in, owner, note="Add 2FA", description="Now with 2FA")
row.refresh_from_db()
assert row.is_behind
# Unchanged and unconfirmed: nothing happens. Confirmed: re-stamped at the current revision.
sign_in.refresh_from_db()
assert services.set_statuses(impl, editor, {sign_in: ("shipped", False)}) == 0
assert services.set_statuses(impl, editor, {sign_in: ("shipped", True)}) == 1
row.refresh_from_db()
assert (row.feature_revision, row.is_behind) == (2, False)
def test_outsiders_cannot_set_statuses(impl, features, outsider):
with pytest.raises(PermissionDenied):
services.set_statuses(impl, outsider, {features[0]: ("shipped", False)})
# Views
def test_status_grid_via_form(client, impl, features, editor):
sign_in, sign_out = features
client.force_login(editor)
client.post(
iurl("statuses", impl),
{f"status-{sign_in.pk}": "shipped", f"status-{sign_out.pk}": "in_progress"},
)
statuses = dict(impl.feature_statuses.values_list("feature__number", "status"))
assert statuses == {1: "shipped", 2: "in_progress"}
def test_bad_status_value_is_refused(client, impl, features, editor):
client.force_login(editor)
client.post(iurl("statuses", impl), {f"status-{features[0].pk}": "done"})
assert not impl.feature_statuses.exists()
def test_outsider_sees_grid_read_only(client, impl, features, outsider):
client.force_login(outsider)
page = client.get(iurl("detail", impl)).content.decode()
assert "Sign in" in page and 'id="status-' not in page
assert client.post(iurl("statuses", impl), {}).status_code == 403
assert client.get(iurl("create")).status_code == 403
def test_create_edit_archive_delete(client, open_project, owner, editor):
client.force_login(editor)
client.post(iurl("create"), {"name": "CLI", "url": "https://forge.example/acme/cli"})
impl = open_project.implementations.get()
client.post(
iurl("edit", impl),
{"name": "CLI", "url": "https://forge.example/acme/cli", "status": "archived"},
)
impl.refresh_from_db()
assert impl.status == "archived"
assert "Archived" in client.get(iurl("list")).content.decode()
assert client.post(iurl("delete", impl), {"confirm": "CLI"}).status_code == 403
client.force_login(owner)
client.post(iurl("delete", impl), {"confirm": "cli"})
assert open_project.implementations.exists()
client.post(iurl("delete", impl), {"confirm": "CLI"})
assert not open_project.implementations.exists()
def test_feature_page_shows_each_implementation(client, impl, features, owner, editor):
services.set_statuses(impl, editor, {features[0]: ("shipped", False)})
update_feature(features[0], owner, note="Change", title="Sign in v2")
client.force_login(editor)
page = client.get(
reverse("features:detail", kwargs={**P, "feature_key": "INT-1"})
).content.decode()
assert "Web app" in page and "Shipped" in page and "<strong>behind</strong>" in page
other = client.get(
reverse("features:detail", kwargs={**P, "feature_key": "INT-2"})
).content.decode()
assert "Not started" in other