11from __future__ import annotations
22
3+ import base64
34import shutil
45from decimal import Decimal
56from typing import TYPE_CHECKING
67from unittest import mock
78
89import pytest
10+ from cryptography .hazmat .primitives import serialization
11+ from cryptography .hazmat .primitives .asymmetric import rsa
912from meltano .core .project import Project
1013from meltano .core .state_store import MeltanoState , state_store_manager_from_project_settings
1114from meltano .core .state_store .base import (
@@ -31,6 +34,17 @@ def project(tmp_path: Path) -> Project:
3134 return Project .find (path .resolve ()) # type: ignore[no-any-return]
3235
3336
37+ @pytest .fixture
38+ def pkey_base64 () -> str :
39+ dummy_key = rsa .generate_private_key (public_exponent = 65537 , key_size = 2048 )
40+ dummy_private_key = dummy_key .private_bytes (
41+ encoding = serialization .Encoding .DER ,
42+ format = serialization .PrivateFormat .PKCS8 ,
43+ encryption_algorithm = serialization .NoEncryption (),
44+ )
45+ return base64 .b64encode (dummy_private_key ).decode ("utf-8" )
46+
47+
3448def test_get_manager (project : Project ) -> None :
3549 with mock .patch (
3650 "meltano_state_backend_snowflake.backend.SnowflakeStateStoreManager._ensure_tables" ,
@@ -379,64 +393,78 @@ def test_acquire_lock_retry(
379393 assert mock_cursor .execute .call_count >= 2
380394
381395
396+ # class TestURIQueryParams:
397+ # """Tests for URI query parameter parsing."""
398+
399+
400+ @pytest .fixture
401+ def base_uri () -> str :
402+ return "snowflake://myuser:mypass@myaccount/mydb/myschema?warehouse=mywh"
403+
404+
382405@pytest .mark .usefixtures ("mock_connection" )
383- class TestURIQueryParams :
384- """Tests for URI query parameter parsing."""
406+ def test_full_sqlalchemy_uri (base_uri : str ) -> None :
407+ """Test full SQLAlchemy-style URI with all params."""
408+ manager = SnowflakeStateStoreManager (uri = f"{ base_uri } &role=myrole" )
409+ assert manager .account == "myaccount"
410+ assert manager .user == "myuser"
411+ assert manager .password == "mypass" # noqa: S105
412+ assert manager .database == "mydb"
413+ assert manager .schema == "myschema"
414+ assert manager .warehouse == "mywh"
415+ assert manager .role == "myrole"
385416
386- def test_full_sqlalchemy_uri (self ) -> None :
387- """Test full SQLAlchemy-style URI with all params."""
388- manager = SnowflakeStateStoreManager (
389- uri = "snowflake://myuser:mypass@myaccount/mydb/myschema?warehouse=mywh&role=myrole" ,
390- )
391- assert manager .account == "myaccount"
392- assert manager .user == "myuser"
393- assert manager .password == "mypass" # noqa: S105
394- assert manager .database == "mydb"
395- assert manager .schema == "myschema"
396- assert manager .warehouse == "mywh"
397- assert manager .role == "myrole"
398-
399- def test_warehouse_from_query_param (self ) -> None :
400- """Test warehouse extracted from query parameter."""
401- manager = SnowflakeStateStoreManager (
402- uri = "snowflake://myuser:mypass@myaccount/mydb?warehouse=mywh" ,
403- )
404- assert manager .warehouse == "mywh"
405- assert manager .schema == "PUBLIC"
406417
407- def test_role_from_query_param ( self ) -> None :
408- """Test role extracted from query parameter."""
409- manager = SnowflakeStateStoreManager (
410- uri = "snowflake://myuser:mypass@myaccount/mydb?warehouse=mywh&role=analyst" ,
411- )
412- assert manager .role == "analyst "
418+ @ pytest . mark . usefixtures ( "mock_connection" )
419+ def test_warehouse_from_query_param ( base_uri : str ) -> None :
420+ """Test warehouse extracted from query parameter."""
421+ manager = SnowflakeStateStoreManager ( uri = base_uri )
422+ assert manager . warehouse == "mywh"
423+ assert manager .schema == "myschema "
413424
414- def test_explicit_settings_override_query_params (self ) -> None :
415- """Test that explicit settings take precedence over query params."""
416- manager = SnowflakeStateStoreManager (
417- uri = "snowflake://uri_user:uri_pass@uri_account/uri_db/uri_schema?warehouse=uri_wh&role=uri_role" ,
418- account = "explicit_account" ,
419- user = "explicit_user" ,
420- password = "explicit_pass" , # noqa: S106
421- warehouse = "explicit_wh" ,
422- database = "explicit_db" ,
423- schema = "explicit_schema" ,
424- role = "explicit_role" ,
425- )
426- assert manager .account == "explicit_account"
427- assert manager .user == "explicit_user"
428- assert manager .password == "explicit_pass" # noqa: S105
429- assert manager .warehouse == "explicit_wh"
430- assert manager .database == "explicit_db"
431- assert manager .schema == "explicit_schema"
432- assert manager .role == "explicit_role"
433-
434- def test_role_defaults_to_none (self ) -> None :
435- """Test role defaults to None when not provided."""
436- manager = SnowflakeStateStoreManager (
437- uri = "snowflake://myuser:mypass@myaccount/mydb?warehouse=mywh" ,
438- )
439- assert manager .role is None
425+
426+ @pytest .mark .usefixtures ("mock_connection" )
427+ def test_role_from_query_param (base_uri : str ) -> None :
428+ """Test role extracted from query parameter."""
429+ manager = SnowflakeStateStoreManager (uri = f"{ base_uri } &role=analyst" )
430+ assert manager .role == "analyst"
431+
432+
433+ @pytest .mark .usefixtures ("mock_connection" )
434+ def test_explicit_settings_override_query_params (base_uri : str ) -> None :
435+ """Test that explicit settings take precedence over query params."""
436+ manager = SnowflakeStateStoreManager (
437+ uri = f"{ base_uri } &role=uri_role" ,
438+ account = "explicit_account" ,
439+ user = "explicit_user" ,
440+ password = "explicit_pass" , # noqa: S106
441+ warehouse = "explicit_wh" ,
442+ database = "explicit_db" ,
443+ schema = "explicit_schema" ,
444+ role = "explicit_role" ,
445+ )
446+ assert manager .account == "explicit_account"
447+ assert manager .user == "explicit_user"
448+ assert manager .password == "explicit_pass" # noqa: S105
449+ assert manager .warehouse == "explicit_wh"
450+ assert manager .database == "explicit_db"
451+ assert manager .schema == "explicit_schema"
452+ assert manager .role == "explicit_role"
453+
454+
455+ @pytest .mark .usefixtures ("mock_connection" )
456+ def test_role_defaults_to_none (base_uri : str ) -> None :
457+ """Test role defaults to None when not provided."""
458+ manager = SnowflakeStateStoreManager (uri = base_uri )
459+ assert manager .role is None
460+
461+
462+ @pytest .mark .usefixtures ("mock_connection" )
463+ def test_private_key_from_query_param (base_uri : str , pkey_base64 : str ) -> None :
464+ """Test private key extracted from query parameter."""
465+ manager = SnowflakeStateStoreManager (uri = f"{ base_uri } &private_key_base64={ pkey_base64 } " )
466+ assert manager .private_key is not None
467+ assert manager .private_key == base64 .b64decode (pkey_base64 )
440468
441469
442470def test_missing_account_validation () -> None :
@@ -470,10 +498,10 @@ def test_missing_user_validation() -> None:
470498
471499
472500def test_missing_password_validation () -> None :
473- """Test missing password validation."""
501+ """Test missing password validation when no private key is provided ."""
474502 with pytest .raises (
475503 MissingStateBackendSettingsError ,
476- match = "Snowflake password is required" ,
504+ match = "Snowflake password or private key is required" ,
477505 ):
478506 SnowflakeStateStoreManager (
479507 uri = "snowflake://user@account/db" , # No password in URI
@@ -484,6 +512,17 @@ def test_missing_password_validation() -> None:
484512 )
485513
486514
515+ @pytest .mark .usefixtures ("mock_connection" )
516+ def test_private_key_without_password (pkey_base64 : str ) -> None :
517+ """Test that private key auth works without a password."""
518+ manager = SnowflakeStateStoreManager (
519+ uri = "snowflake://myuser@myaccount/mydb?warehouse=mywh" ,
520+ private_key_base64 = pkey_base64 ,
521+ )
522+ assert manager .private_key == base64 .b64decode (pkey_base64 )
523+ assert manager .password is None
524+
525+
487526def test_missing_warehouse_validation () -> None :
488527 """Test missing warehouse validation."""
489528 with pytest .raises (
0 commit comments