Files
VacatureRadar/tests/integration/test_views.py
T
Jens 5e2453c29a
deploy / deploy (push) Canceled after 0s
feat: professionalize platform release 0.3.13
2026-07-29 16:35:37 +02:00

243 lines
8.4 KiB
Python

import pytest
from django.conf import settings
from django.core.cache import cache
from django.test import override_settings
from django.urls import reverse
from django.utils import timezone
from apps.jobs.models import JobPosting, ScoreRun
@pytest.mark.integration
@pytest.mark.django_db
def test_today_requires_login(client):
response = client.get(reverse("dashboard:today"))
assert response.status_code == 302
assert reverse("login") in response.url
@pytest.mark.integration
@pytest.mark.django_db
def test_browser_favicon_and_product_brand_assets_are_local(client):
response = client.get("/favicon.ico")
assert response.status_code == 301
assert response.url.endswith("/static/favicon.svg")
favicon = (settings.BASE_DIR / "static" / "favicon.svg").read_text(encoding="utf-8")
product_logo = (settings.BASE_DIR / "static" / "img" / "vacatureradar-logo.svg").read_text(
encoding="utf-8"
)
wordmark = settings.BASE_DIR / "static" / "img" / "itworx-wordmark.png"
for signature in ('stroke="#19d3e6"', 'stroke="#7df5ff"', 'fill="#45d69b"'):
assert signature in favicon
assert 'stroke="#19d3e6"' in product_logo
assert 'fill="#36c990"' in product_logo
assert "<rect" not in product_logo
assert 'viewBox="0 0 64 64"' in favicon
assert wordmark.read_bytes().startswith(b"\x89PNG\r\n\x1a\n")
assert wordmark.stat().st_size < 100_000
@pytest.mark.integration
@pytest.mark.django_db
def test_today_loads_for_user(client, user, profile):
client.force_login(user)
response = client.get(reverse("dashboard:today"))
assert response.status_code == 200
assert "Vandaag" in response.content.decode()
body = response.content.decode()
assert 'aria-label="VacatureRadar dashboard"' in body
assert "Intelligence cockpit" in body
assert "Ontwikkeld door" in body
assert 'aria-label="Ontwikkeld door ITWorx.tech"' in body
assert 'class="sidebar-footer"' in body
assert 'class="sidebar-account"' not in body
assert "Dagoverzicht" in body
assert 'content="VacatureRadar"' in body
def test_premium_layout_uses_one_bounded_ultrawide_grid():
css = (settings.BASE_DIR / "static" / "css" / "premium.css").read_text(encoding="utf-8")
assert "--content-frame: 1720px" in css
assert "width: min(100%, var(--content-frame))" in css
assert "margin-inline: auto" in css
assert ".topbar-inner" in css
assert "--content-frame: 2200px" in css
assert "--content-frame: 2480px" in css
assert "@media (min-width: 1800px)" in css
assert "minmax(650px, 880px) minmax(760px, 1fr)" in css
@pytest.mark.integration
@pytest.mark.django_db
def test_demo_banner_is_part_of_main_content_flow(client, user, settings):
settings.DEMO_MODE_ENABLED = True
settings.DEMO_READ_ONLY = True
settings.DEMO_USERNAME = user.username
client.force_login(user)
response = client.get(reverse("dashboard:today"))
body = response.content.decode()
assert response.status_code == 200
assert body.index('<main id="main"') < body.index('class="demo-mode-banner"')
assert 'role="status" aria-label="Publieke demo, alleen-lezen"' in body
def test_css_imports_share_the_release_cache_version():
css = (settings.BASE_DIR / "static" / "css" / "app.css").read_text(encoding="utf-8")
assert css.count("?v=0.3.13") == 7
@pytest.mark.integration
@pytest.mark.django_db
def test_job_list_defaults_to_ranked_it_focus_but_keeps_explicit_archive(
client, user, profile, job
):
non_it = JobPosting.objects.create(
employer=job.employer,
original_title="Spontane sollicitatie - logopedist",
normalized_title="spontane sollicitatie logopedist",
canonical_url="https://jobs.example.org/vacatures/logopedist",
canonical_key="9" * 64,
content_hash="8" * 64,
description_text="Zorgfunctie met toegang tot digitale patiëntendossiers.",
status=JobPosting.Status.ACTIVE,
)
misleading_it_title = JobPosting.objects.create(
employer=job.employer,
original_title="IT Recruitment Specialist",
normalized_title="it recruitment specialist",
canonical_url="https://jobs.example.org/vacatures/it-recruitment",
canonical_key="3" * 64,
content_hash="2" * 64,
status=JobPosting.Status.ACTIVE,
)
ScoreRun.objects.create(
job=job,
profile=profile,
profile_version=profile.version,
score=60,
confidence=0.9,
recommendation=ScoreRun.Recommendation.WEAK,
positives=["IT-focus bevestigd via de functietitel."],
)
ScoreRun.objects.create(
job=non_it,
profile=profile,
profile_version=profile.version,
score=99,
confidence=0.9,
recommendation=ScoreRun.Recommendation.POSSIBLE,
)
hidden_it = JobPosting.objects.create(
employer=job.employer,
original_title="Cloud Engineer buiten de zoekradius",
normalized_title="cloud engineer buiten de zoekradius",
canonical_url="https://jobs.example.org/vacatures/cloud-ver",
canonical_key="5" * 64,
content_hash="4" * 64,
status=JobPosting.Status.ACTIVE,
)
ScoreRun.objects.create(
job=hidden_it,
profile=profile,
profile_version=profile.version,
score=98,
confidence=0.9,
recommendation=ScoreRun.Recommendation.HIDDEN,
hard_exclusions=["Buiten de zoekradius."],
)
client.force_login(user)
focused = client.get(reverse("jobs:list"))
focused_body = focused.content.decode()
assert job.original_title in focused_body
assert non_it.original_title not in focused_body
assert misleading_it_title.original_title not in focused_body
assert hidden_it.original_title not in focused_body
assert focused.context["it_job_count"] == 2
assert focused.context["filtered_non_it_count"] == 2
archive = client.get(reverse("jobs:list"), {"focus": "all", "match": "all"})
archive_body = archive.content.decode()
assert non_it.original_title in archive_body
assert misleading_it_title.original_title in archive_body
assert hidden_it.original_title in archive_body
@pytest.mark.integration
@pytest.mark.django_db
def test_today_uses_latest_user_score_and_excludes_non_it(client, user, profile, job):
non_it = JobPosting.objects.create(
employer=job.employer,
original_title="Technieker Facilitaire Diensten",
normalized_title="technieker facilitaire diensten",
canonical_url="https://jobs.example.org/vacatures/facilitair",
canonical_key="7" * 64,
content_hash="6" * 64,
status=JobPosting.Status.ACTIVE,
first_seen=timezone.now(),
)
ScoreRun.objects.create(
job=job,
profile=profile,
profile_version=profile.version,
score=70,
confidence=0.9,
recommendation=ScoreRun.Recommendation.POSSIBLE,
)
ScoreRun.objects.create(
job=job,
profile=profile,
profile_version=profile.version,
score=66,
confidence=0.9,
recommendation=ScoreRun.Recommendation.POSSIBLE,
)
ScoreRun.objects.create(
job=non_it,
profile=profile,
profile_version=profile.version,
score=95,
confidence=0.9,
recommendation=ScoreRun.Recommendation.STRONG,
)
client.force_login(user)
response = client.get(reverse("dashboard:today"))
cards = list(response.context["score_cards"])
assert [card.job_id for card in cards] == [job.id]
assert cards[0].score == 66
assert response.context["filtered_non_it_jobs"] == 1
@pytest.mark.integration
@pytest.mark.django_db
@override_settings(
AUTH_LOGIN_RATE_LIMIT_MAX_ATTEMPTS=2,
AUTH_LOGIN_RATE_LIMIT_WINDOW_SECONDS=120,
AUTH_LOGIN_RATE_LIMIT_BLOCK_SECONDS=300,
)
def test_login_view_rate_limits_failed_attempts(client, user):
cache.clear()
response = client.post(
reverse("login"),
{"username": user.username, "password": "wrong"},
)
assert response.status_code == 200
response = client.post(
reverse("login"),
{"username": user.username, "password": "wrong"},
)
body = response.content.decode("utf-8")
assert "Te veel inlogpogingen" in body
response = client.post(
reverse("login"),
{"username": user.username, "password": "wrong"},
)
assert "Te veel inlogpogingen" in response.content.decode("utf-8")