67a1bc740d
create_app is synchronous (returns Quart) and runs its own internal asyncio.run for settings/migrations. _make_app wrapped it in asyncio.run, which nested event loops and raised "a coroutine is required" — the other integration tests already call create_app() directly. This test was added in an unpushed commit, so CI never exercised it until now. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
181 lines
6.1 KiB
Python
181 lines
6.1 KiB
Python
"""Integration tests: ansible_targets / ansible_groups tables + fetch_scope_targets.
|
|
|
|
Requires a live Postgres DB (STEWARD_DATABASE_URL).
|
|
Run with:
|
|
pytest tests/integration/test_ansible_inventory.py -m integration -v
|
|
"""
|
|
from __future__ import annotations
|
|
import os
|
|
import uuid
|
|
|
|
import pytest
|
|
from sqlalchemy import select, text
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
_NEEDS_DB = pytest.mark.skipif(
|
|
not os.environ.get("STEWARD_DATABASE_URL"),
|
|
reason="integration test needs a live Postgres (STEWARD_DATABASE_URL)",
|
|
)
|
|
|
|
|
|
def _make_app():
|
|
# create_app is synchronous and runs its own internal asyncio.run for
|
|
# settings/migrations — call it directly (matching the other integration
|
|
# tests); wrapping it in asyncio.run nests event loops and fails.
|
|
from steward.app import create_app
|
|
return create_app(testing=False)
|
|
|
|
|
|
@_NEEDS_DB
|
|
def test_ansible_targets_table_exists():
|
|
"""Migration 0015 created the ansible_targets table."""
|
|
app = _make_app()
|
|
|
|
async def _check():
|
|
async with app.db_sessionmaker() as db:
|
|
result = await db.execute(
|
|
text(
|
|
"SELECT column_name FROM information_schema.columns "
|
|
"WHERE table_name = 'ansible_targets'"
|
|
)
|
|
)
|
|
return {row[0] for row in result.fetchall()}
|
|
|
|
import asyncio
|
|
columns = asyncio.run(_check())
|
|
assert "id" in columns
|
|
assert "name" in columns
|
|
assert "address" in columns
|
|
assert "ansible_vars" in columns
|
|
assert "host_id" in columns
|
|
|
|
|
|
@_NEEDS_DB
|
|
def test_ansible_groups_table_exists():
|
|
"""Migration 0016 created ansible_groups and ansible_target_groups."""
|
|
app = _make_app()
|
|
|
|
async def _check():
|
|
async with app.db_sessionmaker() as db:
|
|
groups_result = await db.execute(
|
|
text(
|
|
"SELECT column_name FROM information_schema.columns "
|
|
"WHERE table_name = 'ansible_groups'"
|
|
)
|
|
)
|
|
groups_cols = {row[0] for row in groups_result.fetchall()}
|
|
|
|
join_result = await db.execute(
|
|
text(
|
|
"SELECT column_name FROM information_schema.columns "
|
|
"WHERE table_name = 'ansible_target_groups'"
|
|
)
|
|
)
|
|
join_cols = {row[0] for row in join_result.fetchall()}
|
|
return groups_cols, join_cols
|
|
|
|
import asyncio
|
|
groups_cols, join_cols = asyncio.run(_check())
|
|
assert "id" in groups_cols
|
|
assert "name" in groups_cols
|
|
assert "ansible_vars" in groups_cols
|
|
assert "target_id" in join_cols
|
|
assert "group_id" in join_cols
|
|
|
|
|
|
@_NEEDS_DB
|
|
def test_ansible_run_scope_column_exists():
|
|
"""Migration 0017 added inventory_scope to ansible_runs."""
|
|
app = _make_app()
|
|
|
|
async def _check():
|
|
async with app.db_sessionmaker() as db:
|
|
result = await db.execute(
|
|
text(
|
|
"SELECT column_name FROM information_schema.columns "
|
|
"WHERE table_name = 'ansible_runs'"
|
|
)
|
|
)
|
|
return {row[0] for row in result.fetchall()}
|
|
|
|
import asyncio
|
|
columns = asyncio.run(_check())
|
|
assert "inventory_scope" in columns
|
|
assert "inventory_path" in columns
|
|
|
|
|
|
@_NEEDS_DB
|
|
def test_create_target_and_group_and_fetch():
|
|
"""Can create a target+group, assign membership, and fetch via fetch_scope_targets."""
|
|
from steward.models.ansible_inventory import AnsibleTarget, AnsibleGroup
|
|
from steward.ansible.inventory_gen import fetch_scope_targets, generate_inventory
|
|
|
|
app = _make_app()
|
|
target_id = str(uuid.uuid4())
|
|
group_id = str(uuid.uuid4())
|
|
target_name = f"test-target-{target_id[:8]}"
|
|
group_name = f"test-group-{group_id[:8]}"
|
|
|
|
async def _run():
|
|
# Create
|
|
async with app.db_sessionmaker() as db:
|
|
async with db.begin():
|
|
grp = AnsibleGroup(
|
|
id=group_id,
|
|
name=group_name,
|
|
ansible_vars={"env": "test"},
|
|
)
|
|
tgt = AnsibleTarget(
|
|
id=target_id,
|
|
name=target_name,
|
|
address="10.0.99.1",
|
|
ansible_vars={"ansible_user": "ubuntu"},
|
|
)
|
|
tgt.groups.append(grp)
|
|
db.add(grp)
|
|
db.add(tgt)
|
|
|
|
# Fetch all — target must be in the list
|
|
async with app.db_sessionmaker() as db:
|
|
all_targets = await fetch_scope_targets(db, "steward:all")
|
|
assert any(t.id == target_id for t in all_targets)
|
|
|
|
# Fetch by group
|
|
async with app.db_sessionmaker() as db:
|
|
group_targets = await fetch_scope_targets(db, f"steward:group:{group_id}")
|
|
assert any(t.id == target_id for t in group_targets)
|
|
|
|
# Fetch single target
|
|
async with app.db_sessionmaker() as db:
|
|
single = await fetch_scope_targets(db, f"steward:target:{target_id}")
|
|
assert len(single) == 1
|
|
assert single[0].id == target_id
|
|
|
|
# Generate inventory and verify var merging
|
|
async with app.db_sessionmaker() as db:
|
|
targets = await fetch_scope_targets(db, f"steward:group:{group_id}")
|
|
inv = generate_inventory(targets)
|
|
assert target_name in inv["all"]["hosts"]
|
|
assert target_name in inv[group_name]["hosts"]
|
|
assert inv["_meta"]["hostvars"][target_name]["ansible_host"] == "10.0.99.1"
|
|
assert inv["_meta"]["hostvars"][target_name]["ansible_user"] == "ubuntu"
|
|
assert inv["_meta"]["hostvars"][target_name]["env"] == "test"
|
|
|
|
# Cleanup
|
|
async with app.db_sessionmaker() as db:
|
|
async with db.begin():
|
|
t = (await db.execute(
|
|
select(AnsibleTarget).where(AnsibleTarget.id == target_id)
|
|
)).scalar_one_or_none()
|
|
if t:
|
|
await db.delete(t)
|
|
g = (await db.execute(
|
|
select(AnsibleGroup).where(AnsibleGroup.id == group_id)
|
|
)).scalar_one_or_none()
|
|
if g:
|
|
await db.delete(g)
|
|
|
|
import asyncio
|
|
asyncio.run(_run())
|