Compare commits
4 commits
bb4965ab07
...
2d958ba520
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d958ba520 | |||
| fff7ccde12 | |||
| 1aac45eb76 | |||
| 903b24d17d |
18 changed files with 61 additions and 40 deletions
23
.forgejo/workflows/publish.yaml
Normal file
23
.forgejo/workflows/publish.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
name: ci
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build_and_publish:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: ghcr.io/catthehacker/ubuntu:runner-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/ruff-action@v3
|
||||
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
||||
with:
|
||||
version: "0.11.19"
|
||||
- run: uv python install # Gets Python version from pyproject.toml
|
||||
- run: uv sync
|
||||
- run: uv run pytest test
|
||||
env:
|
||||
ENVIRONMENT: testing
|
||||
|
|
@ -2,6 +2,11 @@
|
|||
add-bounds = "major"
|
||||
exclude-newer = "P2W"
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
".alembic"
|
||||
]
|
||||
|
||||
[project]
|
||||
name = "cloud-api"
|
||||
version = "0.1.0"
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@ def get_super_admin_list():
|
|||
def empty_su_list():
|
||||
return []
|
||||
|
||||
def testing_su_list():
|
||||
return ["admin@test.com"]
|
||||
|
||||
su_list_dependency = Annotated[list[User], Depends(get_super_admin_list)]
|
||||
|
||||
async def user_model_super_admin(user_model: user_model_claims_dependency, super_admin_emails: su_list_dependency):
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ from src.iam.schemas import IAMGetGroupPermissionsResponse, IAMGetGroupUsersResp
|
|||
GroupSchema, IAMPostGroupResponse, IAMPutGroupPermissionRequest, IAMPutGroupPermissionResponse, \
|
||||
IAMPutGroupUserRequest, IAMPutGroupUserResponse, IAMDeleteGroupPermissionRequest, IAMDeleteGroupPermissionResponse, \
|
||||
IAMDeleteGroupUserRequest, IAMDeleteGroupUserResponse, IAMGetPermissionsResponse, IAMPostPermissionRequest, \
|
||||
IAMPostPermissionResponse, PermissionSchema, IAMDeletePermissionRequest, IAMGetPermissionsSearchRequest, IAMGetPermissionsSearchResponse
|
||||
IAMPostPermissionResponse, IAMDeletePermissionRequest, IAMGetPermissionsSearchRequest, IAMGetPermissionsSearchResponse
|
||||
|
||||
router = APIRouter(
|
||||
tags=["IAM"],
|
||||
|
|
@ -195,7 +195,7 @@ async def delete_permission(db: db_dependency, su: super_admin_dependency, perm_
|
|||
|
||||
|
||||
@router.post("/permissions/search", response_model=IAMGetPermissionsSearchResponse)
|
||||
async def get_permissions(db: db_dependency, org_model: org_model_root_claim_body_dependency, request_model: IAMGetPermissionsSearchRequest):
|
||||
async def post_permissions(db: db_dependency, org_model: org_model_root_claim_body_dependency, request_model: IAMGetPermissionsSearchRequest):
|
||||
permission_query = db.query(Perm)
|
||||
|
||||
if request_model.service_id is not None:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from src.database import db_dependency
|
|||
from src.schemas import ResourceName
|
||||
from src.auth.exceptions import UnauthorizedException
|
||||
|
||||
from fastapi import HTTPException, status, Request, Depends
|
||||
from fastapi import Request, Depends
|
||||
|
||||
|
||||
def valid_service_key(db: db_dependency, request: Request, rn: ResourceName) -> bool:
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ from fastapi.params import Query
|
|||
from psycopg.errors import UniqueViolation
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from src.auth.exceptions import UnauthorizedException
|
||||
from src.contact.schemas import ContactModel
|
||||
from src.exceptions import UnprocessableContentException, ConflictException
|
||||
from src.contact.models import Contact
|
||||
|
|
|
|||
|
|
@ -42,5 +42,5 @@ class User(Base):
|
|||
def groups(self):
|
||||
result = defaultdict(list)
|
||||
for group in self.group_rel:
|
||||
result[group.org_rel.name].append(group.name)
|
||||
result[group.org_rel.name].append({"name": group.name, "id": group.id})
|
||||
return dict(result)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class UserResponse(CustomBaseModel):
|
|||
last_name: str
|
||||
email: str
|
||||
organisations: list[Optional[dict[str, str|int]]]
|
||||
groups: Optional[dict[str, list[str]]] = None
|
||||
groups: Optional[dict[str, list[dict[str, str|int]]]] = None
|
||||
|
||||
|
||||
class OrgResponse(CustomBaseModel):
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from src.organisation.models import Organisation as Org
|
|||
from src.contact.models import Contact
|
||||
from src.iam.models import Group, Permission
|
||||
from src.auth.service import get_current_user, get_dev_user
|
||||
from src.auth.dependencies import empty_su_list, get_super_admin_list
|
||||
from src.auth.dependencies import empty_su_list, get_super_admin_list, testing_su_list
|
||||
|
||||
from src.main import app # inited FastAPI app
|
||||
from src.database import engine, Base, get_db
|
||||
|
|
@ -39,6 +39,7 @@ async def default_client(db_session) -> AsyncGenerator[AsyncClient, None]:
|
|||
return db_session
|
||||
app.dependency_overrides[get_db] = get_db_override
|
||||
app.dependency_overrides[get_current_user] = get_dev_user
|
||||
app.dependency_overrides[get_super_admin_list] = testing_su_list
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://localhost:8000/api/v1") as ac:
|
||||
yield ac
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ Delete endpoints are currently skipped because the testing system cannot use bod
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .conftest import default_client
|
||||
|
||||
from src.organisation.models import Organisation as Org, OrgUsers
|
||||
from src.user.models import User
|
||||
from src.iam.models import Group
|
||||
|
|
@ -83,7 +81,7 @@ async def test_get_org_groups_auth_approval(default_client: AsyncClient):
|
|||
|
||||
@pytest.mark.anyio
|
||||
async def test_get_org_contact_auth_approval(default_client: AsyncClient):
|
||||
resp = await default_client.get(f"/org/contact?org_id=1&contact_type=billing")
|
||||
resp = await default_client.get("/org/contact?org_id=1&contact_type=billing")
|
||||
assert resp.status_code != 422
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,8 @@
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .conftest import no_su_client
|
||||
|
||||
from src.organisation.models import Organisation as Org
|
||||
from src.user.models import User
|
||||
from src.iam.models import Group
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ DELETE endpoints are not tested
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .conftest import no_su_client
|
||||
|
||||
from src.organisation.models import Organisation as Org
|
||||
from src.user.models import User
|
||||
from src.iam.models import Group
|
||||
|
|
@ -70,7 +68,7 @@ async def test_get_org_groups_auth_root(no_su_client: AsyncClient):
|
|||
|
||||
@pytest.mark.anyio
|
||||
async def test_get_org_contact_auth_root(no_su_client: AsyncClient):
|
||||
resp = await no_su_client.get(f"/org/contact?org_id=2&contact_type=billing")
|
||||
resp = await no_su_client.get("/org/contact?org_id=2&contact_type=billing")
|
||||
assert resp.status_code != 422
|
||||
assert resp.status_code == 401
|
||||
assert "Must be the org's root user" in resp.json()["detail"]
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ This testing module removes the testing user override to verify that endpoints w
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .conftest import no_user_client
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_get_self_db_auth_user(no_user_client: AsyncClient):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .conftest import default_client
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_healthcheck(default_client: AsyncClient):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from src.user.models import User
|
|||
from src.organisation.models import Organisation as Org
|
||||
from src.iam.models import Group
|
||||
|
||||
from .conftest import default_client, db_session, generate_query_and_status
|
||||
from .conftest import generate_query_and_status
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -25,7 +25,7 @@ async def test_post_act_on_resource_endpoint_success(default_client: AsyncClient
|
|||
data = resp.json()
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert data == True
|
||||
assert data is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -133,7 +133,7 @@ async def test_get_group_permissions_success(default_client: AsyncClient):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "permissions" in data
|
||||
assert type(data["permissions"]) == list
|
||||
assert isinstance(data["permissions"], list)
|
||||
assert data["permissions"][0]["service_name"] == "Test Service"
|
||||
assert data["permissions"][0]["resource"] == "test_resource"
|
||||
assert data["permissions"][0]["action"] == "read"
|
||||
|
|
@ -175,7 +175,7 @@ async def test_get_group_users_success(default_client: AsyncClient):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "users" in data
|
||||
assert type(data["users"]) == list
|
||||
assert isinstance(data["users"], list)
|
||||
assert data["users"][0]["id"] == 1
|
||||
assert data["users"][0]["first_name"] == "Admin"
|
||||
assert data["users"][0]["last_name"] == "Test"
|
||||
|
|
@ -218,7 +218,7 @@ async def test_post_group_success(default_client: AsyncClient):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "group" in data
|
||||
assert type(data["group"]) == dict
|
||||
assert isinstance(data["group"], dict)
|
||||
assert data["group"]["name"] == "New Group"
|
||||
assert data["group"]["id"] == 2
|
||||
|
||||
|
|
@ -255,12 +255,12 @@ async def test_put_group_perm_success(default_client: AsyncClient, db_session):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "group" in data
|
||||
assert type(data["group"]) == dict
|
||||
assert isinstance(data["group"], dict)
|
||||
assert data["group"]["name"] == "Test Group Two"
|
||||
assert data["group"]["id"] == 2
|
||||
|
||||
assert "permissions" in data
|
||||
assert type(data["permissions"]) == list
|
||||
assert isinstance(data["permissions"], list)
|
||||
assert data["permissions"][0]["service_name"] == "Test Service"
|
||||
assert data["permissions"][0]["resource"] == "test_resource"
|
||||
assert data["permissions"][0]["action"] == "read"
|
||||
|
|
@ -316,7 +316,7 @@ async def test_put_group_perm_mismatch(default_client: AsyncClient, db_session,
|
|||
db_session.add(Org(name="Another Test Org", root_user_id=1, billing_contact_id=1, owner_contact_id=2, security_contact_id=3, status="approved"))
|
||||
db_session.add(Group(name="Another Test Group", org_id=2))
|
||||
db_session.flush()
|
||||
resp = await default_client.put(f"/iam/group/permission", json=body)
|
||||
resp = await default_client.put("/iam/group/permission", json=body)
|
||||
|
||||
assert resp.status_code == 401
|
||||
assert resp.json()["detail"] == "Group does not belong to this organization"
|
||||
|
|
@ -332,12 +332,12 @@ async def test_put_group_user_success(default_client: AsyncClient, db_session):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "group" in data
|
||||
assert type(data["group"]) == dict
|
||||
assert isinstance(data["group"], dict)
|
||||
assert data["group"]["name"] == "Test Group"
|
||||
assert data["group"]["id"] == 1
|
||||
|
||||
assert "users" in data
|
||||
assert type(data["users"]) == list
|
||||
assert isinstance(data["users"], list)
|
||||
assert data["users"][1]["id"] == 2
|
||||
assert data["users"][1]["first_name"] == "User"
|
||||
assert data["users"][1]["last_name"] == "Test"
|
||||
|
|
@ -387,7 +387,7 @@ async def test_get_permissions_success(default_client: AsyncClient):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "permissions" in data
|
||||
assert type(data["permissions"]) == list
|
||||
assert isinstance(data["permissions"], list)
|
||||
assert data["permissions"][0]["service_name"] == "Test Service"
|
||||
assert data["permissions"][0]["resource"] == "test_resource"
|
||||
assert data["permissions"][0]["action"] == "read"
|
||||
|
|
@ -470,7 +470,7 @@ async def test_post_perm_search_success(default_client: AsyncClient, db_session,
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "permissions" in data
|
||||
assert type(data["permissions"]) == list
|
||||
assert isinstance(data["permissions"], list)
|
||||
assert data["permissions"][0]["service_name"] == "Test Service"
|
||||
assert data["permissions"][0]["resource"] == "test_resource"
|
||||
assert data["permissions"][0]["action"] == "read"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from httpx import AsyncClient
|
|||
|
||||
from src.organisation.models import Organisation, OrgUsers
|
||||
from src.user.models import User
|
||||
from .conftest import default_client, generate_query_and_status
|
||||
from .conftest import generate_query_and_status
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -151,7 +151,7 @@ async def test_get_org_users_success(default_client: AsyncClient):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "users" in data
|
||||
assert type(data["users"]) == list
|
||||
assert isinstance(data["users"], list)
|
||||
assert len(data["users"]) == 1
|
||||
assert data["users"][0] == "admin@test.com"
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ async def test_post_org_user_success(default_client: AsyncClient, db_session):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "users" in data
|
||||
assert type(data["users"]) == list
|
||||
assert isinstance(data["users"], list)
|
||||
assert "user@test.org" in data["users"]
|
||||
|
||||
|
||||
|
|
@ -264,7 +264,7 @@ async def test_get_org_groups_success(default_client: AsyncClient):
|
|||
|
||||
assert resp.status_code == 200
|
||||
assert "groups" in data
|
||||
assert type(data["groups"]) == list
|
||||
assert isinstance(data["groups"], list)
|
||||
assert "Test Group" in data["groups"]
|
||||
|
||||
|
||||
|
|
@ -379,7 +379,7 @@ async def test_patch_org_contact_success(default_client: AsyncClient, key: str,
|
|||
],
|
||||
)
|
||||
@pytest.mark.anyio
|
||||
async def test_patch_org_status_status_checks(default_client: AsyncClient, body: dict[str, str], expected_status: int):
|
||||
async def test_patch_org_contact_status_checks(default_client: AsyncClient, body: dict[str, str], expected_status: int):
|
||||
resp = await default_client.patch("/org/contact", json=body)
|
||||
|
||||
assert resp.status_code == expected_status
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .conftest import default_client, generate_query_and_status
|
||||
from .conftest import generate_query_and_status
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
|
|
@ -38,7 +38,7 @@ async def test_post_service_success(default_client: AsyncClient):
|
|||
assert "service" in data
|
||||
assert data["service"]["name"] == "New Test Service"
|
||||
assert data["service"]["id"] == 2
|
||||
assert type(data["service"]["api_key"]) == str
|
||||
assert isinstance(data["service"]["api_key"], str)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -64,7 +64,7 @@ async def test_patch_service_success(default_client: AsyncClient):
|
|||
assert "service" in data
|
||||
assert data["service"]["name"] == "Test Service"
|
||||
assert data["service"]["id"] == 1
|
||||
assert type(data["service"]["api_key"]) == str
|
||||
assert isinstance(data["service"]["api_key"], str)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
from .conftest import default_client, generate_query_and_status
|
||||
from .conftest import generate_query_and_status
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_get_self_db_success(default_client: AsyncClient):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue