defmodule SimpleshopTheme.VaultTest do use SimpleshopTheme.DataCase, async: true alias SimpleshopTheme.Vault describe "encrypt/1 and decrypt/1" do test "round-trips a string successfully" do plaintext = "my_secret_api_key_12345" assert {:ok, ciphertext} = Vault.encrypt(plaintext) assert is_binary(ciphertext) assert ciphertext != plaintext assert {:ok, decrypted} = Vault.decrypt(ciphertext) assert decrypted == plaintext end test "produces different ciphertext for same plaintext (random IV)" do plaintext = "same_secret" {:ok, ciphertext1} = Vault.encrypt(plaintext) {:ok, ciphertext2} = Vault.encrypt(plaintext) assert ciphertext1 != ciphertext2 # Both should decrypt to same value assert {:ok, ^plaintext} = Vault.decrypt(ciphertext1) assert {:ok, ^plaintext} = Vault.decrypt(ciphertext2) end test "handles empty string" do assert {:ok, ciphertext} = Vault.encrypt("") assert {:ok, ""} = Vault.decrypt(ciphertext) end test "handles unicode characters" do plaintext = "héllo wörld 你好 🎉" {:ok, ciphertext} = Vault.encrypt(plaintext) {:ok, decrypted} = Vault.decrypt(ciphertext) assert decrypted == plaintext end test "handles long strings" do plaintext = String.duplicate("a", 10_000) {:ok, ciphertext} = Vault.encrypt(plaintext) {:ok, decrypted} = Vault.decrypt(ciphertext) assert decrypted == plaintext end test "encrypt/1 returns {:ok, nil} for nil input" do assert {:ok, nil} = Vault.encrypt(nil) end test "decrypt/1 returns {:ok, nil} for nil input" do assert {:ok, nil} = Vault.decrypt(nil) end test "decrypt/1 returns {:ok, empty} for empty string" do assert {:ok, ""} = Vault.decrypt("") end test "decrypt/1 returns error for invalid ciphertext" do assert {:error, :invalid_ciphertext} = Vault.decrypt("not_valid") assert {:error, :invalid_ciphertext} = Vault.decrypt(<<1, 2, 3>>) end test "decrypt/1 returns error for tampered ciphertext" do {:ok, ciphertext} = Vault.encrypt("secret") # Tamper with the ciphertext (flip a bit in the middle) tampered = :binary.bin_to_list(ciphertext) middle = div(length(tampered), 2) tampered = List.update_at(tampered, middle, &Bitwise.bxor(&1, 0xFF)) tampered = :binary.list_to_bin(tampered) assert {:error, :decryption_failed} = Vault.decrypt(tampered) end end describe "encrypt!/1 and decrypt!/1" do test "round-trips successfully" do plaintext = "test_secret" ciphertext = Vault.encrypt!(plaintext) decrypted = Vault.decrypt!(ciphertext) assert decrypted == plaintext end test "encrypt!/1 raises on invalid input type" do assert_raise FunctionClauseError, fn -> Vault.encrypt!(123) end end test "decrypt!/1 raises on invalid ciphertext" do assert_raise RuntimeError, ~r/Decryption failed/, fn -> Vault.decrypt!("invalid") end end end end