mirror of
https://github.com/wizarrrr/wizarr.git
synced 2026-04-19 05:28:33 -04:00
644 lines
20 KiB
Python
644 lines
20 KiB
Python
"""Tests for wizard admin form handling with category field."""
|
|
|
|
import pytest
|
|
from flask import url_for
|
|
|
|
from app.extensions import db
|
|
from app.models import AdminAccount, Library, MediaServer, User, WizardStep
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def session(app):
|
|
"""Return a clean database session inside an app context."""
|
|
with app.app_context():
|
|
# Clean up any existing data before each test - delete in correct order for FKs
|
|
db.session.execute(db.text("DELETE FROM invitation_server"))
|
|
db.session.execute(db.text("DELETE FROM invitation_user"))
|
|
db.session.query(User).delete()
|
|
db.session.query(WizardStep).delete()
|
|
db.session.query(Library).delete()
|
|
db.session.query(MediaServer).delete()
|
|
db.session.query(AdminAccount).delete()
|
|
db.session.commit()
|
|
|
|
yield db.session
|
|
|
|
# Clean up after the test
|
|
db.session.execute(db.text("DELETE FROM invitation_server"))
|
|
db.session.execute(db.text("DELETE FROM invitation_user"))
|
|
db.session.query(User).delete()
|
|
db.session.query(WizardStep).delete()
|
|
db.session.query(Library).delete()
|
|
db.session.query(MediaServer).delete()
|
|
db.session.query(AdminAccount).delete()
|
|
db.session.commit()
|
|
db.session.rollback()
|
|
|
|
|
|
@pytest.fixture
|
|
def admin_user(session):
|
|
"""Create an admin user for authentication."""
|
|
admin = AdminAccount(username="testadmin")
|
|
admin.set_password("testpass123")
|
|
session.add(admin)
|
|
session.commit()
|
|
return admin
|
|
|
|
|
|
@pytest.fixture
|
|
def authenticated_client(client, admin_user):
|
|
"""Return a client with an authenticated admin session."""
|
|
with client.session_transaction() as sess:
|
|
sess["_user_id"] = str(admin_user.id)
|
|
sess["_fresh"] = True
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def plex_server(session):
|
|
"""Create a Plex media server for testing."""
|
|
server = MediaServer(
|
|
name="Test Plex",
|
|
server_type="plex",
|
|
url="http://plex.test",
|
|
api_key="test_key",
|
|
)
|
|
session.add(server)
|
|
session.commit()
|
|
return server
|
|
|
|
|
|
# ─── Test Category Field Validation ───────────────────────────────────
|
|
|
|
|
|
def test_create_step_with_pre_invite_category(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test creating a wizard step with pre_invite category."""
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "pre_invite",
|
|
"title": "Pre-Invite Welcome",
|
|
"markdown": "# Welcome\nThis is shown before invite acceptance",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
# Should redirect after successful creation
|
|
assert response.status_code == 302
|
|
|
|
# Verify step was created with correct category
|
|
step = WizardStep.query.filter_by(
|
|
server_type="plex", category="pre_invite", title="Pre-Invite Welcome"
|
|
).first()
|
|
assert step is not None
|
|
assert step.title == "Pre-Invite Welcome"
|
|
assert step.category == "pre_invite"
|
|
# Position should be >= 0 (exact value depends on seeded steps)
|
|
assert step.position >= 0
|
|
|
|
|
|
def test_create_step_with_post_invite_category(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test creating a wizard step with post_invite category."""
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "post_invite",
|
|
"title": "Post-Invite Setup",
|
|
"markdown": "# Setup\nThis is shown after invite acceptance",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
step = WizardStep.query.filter_by(
|
|
server_type="plex", category="post_invite", title="Post-Invite Setup"
|
|
).first()
|
|
assert step is not None
|
|
assert step.title == "Post-Invite Setup"
|
|
assert step.category == "post_invite"
|
|
# Position should be >= 0 (exact value depends on seeded steps)
|
|
assert step.position >= 0
|
|
|
|
|
|
def test_create_step_default_category(authenticated_client, session, plex_server):
|
|
"""Test that category defaults to post_invite when not specified."""
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"title": "Default Category Step",
|
|
"markdown": "# Default\nShould default to post_invite",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
step = WizardStep.query.filter_by(server_type="plex").first()
|
|
assert step is not None
|
|
assert step.category == "post_invite"
|
|
|
|
|
|
# ─── Test Editing Step Category ───────────────────────────────────────
|
|
|
|
|
|
def test_edit_step_category_from_post_to_pre(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test editing a step's category from post_invite to pre_invite."""
|
|
# Create a post_invite step
|
|
step = WizardStep(
|
|
server_type="plex",
|
|
category="post_invite",
|
|
position=0,
|
|
title="Original Post Step",
|
|
markdown="# Original",
|
|
)
|
|
session.add(step)
|
|
session.commit()
|
|
step_id = step.id
|
|
|
|
# Edit to change category
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.edit_step", step_id=step_id),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "pre_invite",
|
|
"title": "Changed to Pre Step",
|
|
"markdown": "# Changed",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
# Verify category was changed
|
|
session.expire_all()
|
|
updated_step = db.session.get(WizardStep, step_id)
|
|
assert updated_step is not None
|
|
assert updated_step.category == "pre_invite"
|
|
assert updated_step.title == "Changed to Pre Step"
|
|
|
|
|
|
def test_edit_step_category_from_pre_to_post(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test editing a step's category from pre_invite to post_invite."""
|
|
# Create a pre_invite step
|
|
step = WizardStep(
|
|
server_type="plex",
|
|
category="pre_invite",
|
|
position=0,
|
|
title="Original Pre Step",
|
|
markdown="# Original",
|
|
)
|
|
session.add(step)
|
|
session.commit()
|
|
step_id = step.id
|
|
|
|
# Edit to change category
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.edit_step", step_id=step_id),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "post_invite",
|
|
"title": "Changed to Post Step",
|
|
"markdown": "# Changed",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
# Verify category was changed
|
|
session.expire_all()
|
|
updated_step = db.session.get(WizardStep, step_id)
|
|
assert updated_step is not None
|
|
assert updated_step.category == "post_invite"
|
|
assert updated_step.title == "Changed to Post Step"
|
|
|
|
|
|
def test_edit_step_preserves_category_when_not_changed(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test that editing a step without changing category preserves the original category."""
|
|
# Create a pre_invite step
|
|
step = WizardStep(
|
|
server_type="plex",
|
|
category="pre_invite",
|
|
position=0,
|
|
title="Original Title",
|
|
markdown="# Original",
|
|
)
|
|
session.add(step)
|
|
session.commit()
|
|
step_id = step.id
|
|
|
|
# Edit only the title, keeping category the same
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.edit_step", step_id=step_id),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "pre_invite",
|
|
"title": "Updated Title",
|
|
"markdown": "# Original",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
# Verify category was preserved
|
|
session.expire_all()
|
|
updated_step = db.session.get(WizardStep, step_id)
|
|
assert updated_step is not None
|
|
assert updated_step.category == "pre_invite"
|
|
assert updated_step.title == "Updated Title"
|
|
|
|
|
|
# ─── Test Preset Creation with Category ───────────────────────────────
|
|
|
|
|
|
def test_create_preset_with_pre_invite_category(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test creating a preset step with pre_invite category."""
|
|
from app.models import WizardStep
|
|
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_preset"),
|
|
data={
|
|
"preset_id": "discord_community",
|
|
"server_type": "plex",
|
|
"category": "pre_invite",
|
|
"discord_id": "123456789",
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
# Should redirect on success
|
|
assert response.status_code == 302
|
|
|
|
# Verify the step was created with correct category
|
|
step = WizardStep.query.filter_by(
|
|
server_type="plex", category="pre_invite", title="Discord community"
|
|
).first()
|
|
assert step is not None
|
|
assert step.category == "pre_invite"
|
|
assert "discord" in step.markdown.lower()
|
|
|
|
|
|
def test_create_preset_with_post_invite_category(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test creating a preset step with post_invite category."""
|
|
from app.models import WizardStep
|
|
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_preset"),
|
|
data={
|
|
"preset_id": "overseerr_requests",
|
|
"server_type": "plex",
|
|
"category": "post_invite",
|
|
"overseerr_url": "https://overseerr.example.com",
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
# Should redirect on success
|
|
assert response.status_code == 302
|
|
|
|
# Verify the step was created with correct category
|
|
step = WizardStep.query.filter_by(
|
|
server_type="plex", category="post_invite", title="Automatic requests"
|
|
).first()
|
|
assert step is not None
|
|
assert step.category == "post_invite"
|
|
assert "overseerr" in step.markdown.lower()
|
|
|
|
|
|
# ─── Test Position Calculation with Category ──────────────────────────
|
|
|
|
|
|
def test_position_calculation_respects_category(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test that position calculation is scoped to server_type AND category."""
|
|
# Get existing counts
|
|
existing_pre_count = WizardStep.query.filter_by(
|
|
server_type="plex", category="pre_invite"
|
|
).count()
|
|
WizardStep.query.filter_by(server_type="plex", category="post_invite").count()
|
|
|
|
# Create pre_invite step
|
|
pre_step = WizardStep(
|
|
server_type="plex",
|
|
category="pre_invite",
|
|
position=existing_pre_count,
|
|
markdown="# Pre Step 1",
|
|
)
|
|
session.add(pre_step)
|
|
session.commit()
|
|
|
|
# Create post_invite step via API
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "post_invite",
|
|
"markdown": "# Post Step 1",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
# Verify both categories have independent position sequences
|
|
pre_steps = (
|
|
WizardStep.query.filter_by(server_type="plex", category="pre_invite")
|
|
.order_by(WizardStep.position)
|
|
.all()
|
|
)
|
|
post_steps = (
|
|
WizardStep.query.filter_by(server_type="plex", category="post_invite")
|
|
.order_by(WizardStep.position)
|
|
.all()
|
|
)
|
|
|
|
# Should have at least one step in each category
|
|
assert len(pre_steps) >= 1
|
|
assert len(post_steps) >= 1
|
|
|
|
# The manually created pre_step should be at the expected position
|
|
assert pre_step.position == existing_pre_count
|
|
|
|
|
|
# ─── Test Simple Form (Bundle Steps) with Category ────────────────────
|
|
|
|
|
|
def test_create_simple_step_with_pre_invite_category(authenticated_client, session):
|
|
"""Test creating a simple (bundle) step with pre_invite category."""
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step", simple=1),
|
|
data={
|
|
"category": "pre_invite",
|
|
"title": "Bundle Pre Step",
|
|
"markdown": "# Bundle Pre\nFor bundles",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
# Simple steps use 'custom' server_type
|
|
step = WizardStep.query.filter_by(
|
|
server_type="custom", category="pre_invite"
|
|
).first()
|
|
assert step is not None
|
|
assert step.title == "Bundle Pre Step"
|
|
assert step.category == "pre_invite"
|
|
|
|
|
|
def test_create_simple_step_with_post_invite_category(authenticated_client, session):
|
|
"""Test creating a simple (bundle) step with post_invite category."""
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step", simple=1),
|
|
data={
|
|
"category": "post_invite",
|
|
"title": "Bundle Post Step",
|
|
"markdown": "# Bundle Post\nFor bundles",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
step = WizardStep.query.filter_by(
|
|
server_type="custom", category="post_invite"
|
|
).first()
|
|
assert step is not None
|
|
assert step.title == "Bundle Post Step"
|
|
assert step.category == "post_invite"
|
|
|
|
|
|
def test_edit_simple_step_category(authenticated_client, session):
|
|
"""Test editing a simple step's category."""
|
|
# Create a simple step with post_invite category
|
|
step = WizardStep(
|
|
server_type="custom",
|
|
category="post_invite",
|
|
position=0,
|
|
title="Simple Step",
|
|
markdown="# Simple",
|
|
)
|
|
session.add(step)
|
|
session.commit()
|
|
step_id = step.id
|
|
|
|
# Edit to change category to pre_invite
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.edit_step", step_id=step_id),
|
|
data={
|
|
"category": "pre_invite",
|
|
"title": "Simple Step Updated",
|
|
"markdown": "# Simple Updated",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
|
|
# Verify category was changed
|
|
session.expire_all()
|
|
updated_step = db.session.get(WizardStep, step_id)
|
|
assert updated_step is not None
|
|
assert updated_step.category == "pre_invite"
|
|
assert updated_step.server_type == "custom"
|
|
|
|
|
|
# ─── Test Multiple Steps with Same Position in Different Categories ───
|
|
|
|
|
|
def test_multiple_steps_same_position_different_categories(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test that multiple steps can have the same position if in different categories."""
|
|
# Create pre_invite step
|
|
response1 = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "pre_invite",
|
|
"title": "Pre Step 1",
|
|
"markdown": "# Pre 1",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
assert response1.status_code == 302
|
|
|
|
# Create post_invite step
|
|
response2 = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "post_invite",
|
|
"title": "Post Step 1",
|
|
"markdown": "# Post 1",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
assert response2.status_code == 302
|
|
|
|
# Both should exist with their titles
|
|
pre_step = WizardStep.query.filter_by(
|
|
server_type="plex", category="pre_invite", title="Pre Step 1"
|
|
).first()
|
|
post_step = WizardStep.query.filter_by(
|
|
server_type="plex", category="post_invite", title="Post Step 1"
|
|
).first()
|
|
|
|
assert pre_step is not None
|
|
assert post_step is not None
|
|
assert pre_step.id != post_step.id
|
|
# They can have the same position because they're in different categories
|
|
# (though they might not if there are seeded steps)
|
|
|
|
|
|
# ─── Test Form Validation ──────────────────────────────────────────────
|
|
|
|
|
|
def test_create_step_requires_category(authenticated_client, session, plex_server):
|
|
"""Test that category field is required (has default value)."""
|
|
# Even without explicit category, it should default to post_invite
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"title": "No Category Specified",
|
|
"markdown": "# Test",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
# Should succeed with default category
|
|
assert response.status_code == 302
|
|
|
|
step = WizardStep.query.filter_by(server_type="plex").first()
|
|
assert step is not None
|
|
assert step.category == "post_invite"
|
|
|
|
|
|
def test_create_step_invalid_category_rejected(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test that invalid category values are rejected by form validation."""
|
|
response = authenticated_client.post(
|
|
url_for("wizard_admin.create_step"),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "invalid_category",
|
|
"title": "Invalid Category",
|
|
"markdown": "# Test",
|
|
"require_interaction": False,
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
# Should fail validation and return form with errors
|
|
assert response.status_code == 200 # Returns form with errors
|
|
|
|
# No step should be created
|
|
step = WizardStep.query.filter_by(title="Invalid Category").first()
|
|
assert step is None
|
|
|
|
|
|
# ─── Test Category Persistence Across Operations ──────────────────────
|
|
|
|
|
|
def test_category_persists_after_multiple_edits(
|
|
authenticated_client, session, plex_server
|
|
):
|
|
"""Test that category persists correctly through multiple edit operations."""
|
|
# Create step with pre_invite category
|
|
step = WizardStep(
|
|
server_type="plex",
|
|
category="pre_invite",
|
|
position=0,
|
|
title="Original",
|
|
markdown="# Original",
|
|
)
|
|
session.add(step)
|
|
session.commit()
|
|
step_id = step.id
|
|
|
|
# Edit 1: Change title only
|
|
authenticated_client.post(
|
|
url_for("wizard_admin.edit_step", step_id=step_id),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "pre_invite",
|
|
"title": "Edit 1",
|
|
"markdown": "# Original",
|
|
"require_interaction": False,
|
|
},
|
|
)
|
|
|
|
session.expire_all()
|
|
step = db.session.get(WizardStep, step_id)
|
|
assert step is not None
|
|
assert step.category == "pre_invite"
|
|
|
|
# Edit 2: Change markdown only
|
|
authenticated_client.post(
|
|
url_for("wizard_admin.edit_step", step_id=step_id),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "pre_invite",
|
|
"title": "Edit 1",
|
|
"markdown": "# Updated",
|
|
"require_interaction": False,
|
|
},
|
|
)
|
|
|
|
session.expire_all()
|
|
step = db.session.get(WizardStep, step_id)
|
|
assert step is not None
|
|
assert step.category == "pre_invite"
|
|
|
|
# Edit 3: Change category to post_invite
|
|
authenticated_client.post(
|
|
url_for("wizard_admin.edit_step", step_id=step_id),
|
|
data={
|
|
"server_type": "plex",
|
|
"category": "post_invite",
|
|
"title": "Edit 1",
|
|
"markdown": "# Updated",
|
|
"require_interaction": False,
|
|
},
|
|
)
|
|
|
|
session.expire_all()
|
|
step = db.session.get(WizardStep, step_id)
|
|
assert step is not None
|
|
assert step.category == "post_invite"
|