import json import pytest from unittest.mock import MagicMock, patch from fastapi.testclient import TestClient from fastapi import FastAPI import sys import os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) from backend.data_sources.api import router, get_redis_client, get_tenant_config from backend.core.auth import AuthUser, get_current_user app = FastAPI() app.include_router(router) def mock_get_current_user(): return AuthUser({ "id": "test_user", "user_metadata": {"tenant_id": "test_tenant", "role": "user"} }) app.dependency_overrides[get_current_user] = mock_get_current_user client = TestClient(app) class TestPatchAndDeleteEndpoints: @patch('backend.data_sources.api.get_redis_client') def test_patch_tenant_source(self, mock_redis): mock_redis_instance = MagicMock() mock_redis.return_value = mock_redis_instance # Existing config existing_config = [ { "source_name": "old_db", "source_type": "ibis", "config": {"uri": "postgres://user:pass@localhost:5432/db1", "other": "val"} } ] mock_redis_instance.get.return_value = json.dumps(existing_config) # Override the dependency for the duration of the request app.dependency_overrides[get_redis_client] = lambda: mock_redis_instance patch_request = { "new_source_name": "new_db", "config_updates": {"uri": "postgres://user:newpass@localhost:5432/db1"} } response = client.patch("/api/v1/data-sources/my-tenant/sources/old_db?validate_connection=false", json=patch_request) # Clean up app.dependency_overrides.pop(get_redis_client, None) assert response.status_code == 200 # Check that set was called with updated config mock_redis_instance.set.assert_called_once() args, _ = mock_redis_instance.set.call_args key, value = args assert key == "tenant:test_tenant:config" updated_sources = json.loads(value) assert len(updated_sources) == 1 assert updated_sources[0]["source_name"] == "new_db" assert updated_sources[0]["config"]["uri"] == "postgres://user:newpass@localhost:5432/db1" assert updated_sources[0]["config"]["other"] == "val" # Ensure config was merged, not replaced @patch('backend.data_sources.api.get_redis_client') def test_delete_tenant_source_cascades(self, mock_redis): mock_redis_instance = MagicMock() mock_redis.return_value = mock_redis_instance existing_config = [{"source_name": "db_to_delete", "config": {}}] mock_redis_instance.get.return_value = json.dumps(existing_config) app.dependency_overrides[get_redis_client] = lambda: mock_redis_instance response = client.delete("/api/v1/data-sources/my-tenant/sources/db_to_delete") app.dependency_overrides.pop(get_redis_client, None) assert response.status_code == 200 # Redis delete should be called for caches delete_calls = mock_redis_instance.delete.call_args_list assert any(call[0][0] == "schema:test_tenant:db_to_delete" for call in delete_calls) assert any(call[0][0] == "instructions:test_tenant:db_to_delete" for call in delete_calls)