"""Tests for the Keycloak export module.""" import json import uuid import pytest from local_identity.export import ( _deterministic_id, bulk_export_body, split_fullname, user_to_keycloak, validate_keycloak_user, ) from local_identity.user import ProductionIdentity, UserRecord, make_test_user PRIMARY = UserRecord( username="tegwick", fullname="Bernd Worsch", email="bernd.worsch@gmail.com", ) # ------------------------------------------------------------------ # # split_fullname # # ------------------------------------------------------------------ # class TestSplitFullname: def test_two_words(self): assert split_fullname("Bernd Worsch") == ("Bernd", "Worsch") def test_three_words(self): assert split_fullname("Jean Claude Damme") == ("Jean Claude", "Damme") def test_single_word(self): assert split_fullname("Madonna") == ("Madonna", "") def test_extra_whitespace_stripped(self): assert split_fullname(" Bernd Worsch ") == ("Bernd", "Worsch") def test_non_ascii(self): first, last = split_fullname("Ärger Müller") assert first == "Ärger" assert last == "Müller" # ------------------------------------------------------------------ # # _deterministic_id # # ------------------------------------------------------------------ # class TestDeterministicId: def test_stable_across_calls(self): assert _deterministic_id("net-kingdom", "tegwick") == _deterministic_id("net-kingdom", "tegwick") def test_different_realm_gives_different_id(self): assert _deterministic_id("realm-a", "u") != _deterministic_id("realm-b", "u") def test_different_username_gives_different_id(self): assert _deterministic_id("r", "alice") != _deterministic_id("r", "bob") def test_is_valid_uuid(self): uid = _deterministic_id("net-kingdom", "tegwick") uuid.UUID(uid) # must not raise # ------------------------------------------------------------------ # # user_to_keycloak # # ------------------------------------------------------------------ # class TestUserToKeycloak: def test_username(self): assert user_to_keycloak(PRIMARY)["username"] == "tegwick" def test_name_split(self): kc = user_to_keycloak(PRIMARY) assert kc["firstName"] == "Bernd" assert kc["lastName"] == "Worsch" def test_email(self): assert user_to_keycloak(PRIMARY)["email"] == "bernd.worsch@gmail.com" def test_enabled_and_email_verified(self): kc = user_to_keycloak(PRIMARY) assert kc["enabled"] is True assert kc["emailVerified"] is False def test_environment_attribute(self): kc = user_to_keycloak(PRIMARY) assert kc["attributes"]["local_identity_environment"] == ["local"] def test_primary_user_has_no_generated_attribute(self): kc = user_to_keycloak(PRIMARY) assert "local_identity_generated" not in kc["attributes"] def test_test_user_has_generated_attribute(self): t = make_test_user(PRIMARY, 1) kc = user_to_keycloak(t) assert kc["attributes"]["local_identity_generated"] == ["true"] def test_structural_fields_present(self): kc = user_to_keycloak(PRIMARY) for field in ("credentials", "requiredActions", "groups", "realmRoles", "clientRoles"): assert field in kc, f"missing field '{field}'" def test_id_is_deterministic(self): assert user_to_keycloak(PRIMARY)["id"] == user_to_keycloak(PRIMARY)["id"] def test_id_is_valid_uuid(self): uuid.UUID(user_to_keycloak(PRIMARY)["id"]) def test_json_serializable(self): json.dumps(user_to_keycloak(PRIMARY)) # must not raise def test_production_identity_overrides_username(self): u = UserRecord( username="tegwick", fullname="Bernd Worsch", email="bernd.worsch@gmail.com", production_identity=ProductionIdentity(username="bworsch", realm="prod"), ) assert user_to_keycloak(u)["username"] == "bworsch" def test_production_identity_realm_used_in_id(self): u = UserRecord( username="tegwick", fullname="Bernd Worsch", email="bernd.worsch@gmail.com", production_identity=ProductionIdentity(username="bworsch", realm="prod"), ) expected_id = _deterministic_id("prod", "bworsch") assert user_to_keycloak(u)["id"] == expected_id def test_default_realm_used_when_no_production_identity(self): expected_id = _deterministic_id("my-realm", "tegwick") assert user_to_keycloak(PRIMARY, realm="my-realm")["id"] == expected_id # ------------------------------------------------------------------ # # validate_keycloak_user # # ------------------------------------------------------------------ # class TestValidateKeycloakUser: def test_valid_passes(self): validate_keycloak_user(user_to_keycloak(PRIMARY)) # must not raise def test_missing_field_raises(self): d = user_to_keycloak(PRIMARY) del d["username"] with pytest.raises(ValueError, match="username"): validate_keycloak_user(d) def test_wrong_type_raises(self): d = user_to_keycloak(PRIMARY) d["enabled"] = "yes" with pytest.raises(ValueError, match="enabled"): validate_keycloak_user(d) def test_multiple_errors_all_reported(self): d = user_to_keycloak(PRIMARY) del d["username"] del d["email"] with pytest.raises(ValueError) as exc_info: validate_keycloak_user(d) msg = str(exc_info.value) assert "username" in msg assert "email" in msg # ------------------------------------------------------------------ # # bulk_export_body # # ------------------------------------------------------------------ # class TestBulkExportBody: def test_required_keys_present(self): body = bulk_export_body([PRIMARY]) assert "ifResourceExists" in body assert "realm" in body assert "users" in body def test_default_if_resource_exists(self): assert bulk_export_body([PRIMARY])["ifResourceExists"] == "SKIP" def test_custom_realm(self): assert bulk_export_body([PRIMARY], realm="my-realm")["realm"] == "my-realm" def test_user_count(self): users = [PRIMARY, make_test_user(PRIMARY, 1), make_test_user(PRIMARY, 2)] assert len(bulk_export_body(users)["users"]) == 3 def test_empty_user_list(self): assert bulk_export_body([])["users"] == [] def test_overwrite_strategy(self): body = bulk_export_body([PRIMARY], if_resource_exists="OVERWRITE") assert body["ifResourceExists"] == "OVERWRITE" def test_json_serializable(self): json.dumps(bulk_export_body([PRIMARY, make_test_user(PRIMARY, 1)]))