From a913f2ca228fdc29f6944fc14d0c0127c29ee8b3 Mon Sep 17 00:00:00 2001 From: Willem van den Ende Date: Sun, 14 Sep 2025 12:25:59 +0100 Subject: [PATCH] notes from chat http://sequential:4242/c/622ae09e-9470-43fb-8a3f-e85a90322f9a --- plan.md | 367 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 plan.md diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..50e0770 --- /dev/null +++ b/plan.md @@ -0,0 +1,367 @@ +# whitelist sketch + +Here's a complete Mix project structure with the email whitelist functionality and tests: + +## Project Structure + +``` +email_whitelist_test/ +├── config +│ └── config.exs +├── lib +│ └── email_whitelist.ex +├── test +│ ├── email_whitelist_test.exs +│ └── test_helper.exs +├── mix.exs +└── README.md +``` + +## 1. `mix.exs` - Project Configuration + +```elixir +defmodule EmailWhitelistTest.MixProject do + use Mix.Project + + def project do + [ + app: :email_whitelist_test, + version: "0.1.0", + elixir: "~> 1.14", + start_permanent: Mix.env() == :prod, + deps: [] + ] + end + + def application do + [ + extra_applications: [:logger] + ] + end +end +``` + +## 2. `config/config.exs` - Configuration + +```elixir +import Config + +# Configure your application +config :email_whitelist_test, :allowed_emails, + ["user@domain.com", "admin@example.org"] +``` + +## 3. `lib/email_whitelist.ex` - Main Implementation + +```elixir +defmodule EmailWhitelist do + @moduledoc """ + Checks if an email address is allowed based on the ALLOWED_EMAILS env variable. + + Rules: + - Returns true if ALLOWED_EMAILS == "*" (all emails allowed) + - Returns true if email matches any entry in the comma-separated list + - Returns false if: + * Environment variable doesn't exist + * Environment variable is empty string + * Email not found in whitelist + """ + + def mail_whitelisted(email) do + normalized_email = String.downcase(email) + env_var = System.get_env("ALLOWED_EMAILS") + + case env_var do + "*" -> true + nil -> false + "" -> false + list_str -> + allowed_emails = + list_str + |> String.split(",") + |> Enum.map(&String.trim/1) + |> Enum.map(&String.downcase/1) + + normalized_email in allowed_emails + end + end +end +``` + +## 4. `test/test_helper.exs` - Test Setup + +```elixir +ExUnit.start() +``` + +## 5. `test/email_whitelist_test.exs` - Test Cases + +```elixir +defmodule EmailWhitelistTest do + use ExUnit.Case, async: true + + describe "mail_whitelisted/1" do + setup do + # Save original env var + original_env = System.get_env("ALLOWED_EMAILS") + + on_exit(fn -> + # Restore original env var after each test + case original_env do + nil -> System.delete_env("ALLOWED_EMAILS") + value -> System.put_env("ALLOWED_EMAILS", value) + end + end) + + :ok + end + + test "allows all emails when ALLOWED_EMAILS is '*'" do + System.put_env("ALLOWED_EMAILS", "*") + + assert EmailWhitelist.mail_whitelisted("any@example.com") == true + assert EmailWhitelist.mail_whitelisted("user@domain.com") == true + assert EmailWhitelist.mail_whitelisted("test@test.co.uk") == true + end + + test "allows whitelisted emails" do + System.put_env("ALLOWED_EMAILS", "user@domain.com,admin@example.org") + + assert EmailWhitelist.mail_whitelisted("user@domain.com") == true + assert EmailWhitelist.mail_whitelisted("ADMIN@EXAMPLE.ORG") == true + assert EmailWhitelist.mail_whitelisted("User@Domain.Com") == true + end + + test "rejects non-whitelisted emails" do + System.put_env("ALLOWED_EMAILS", "user@domain.com,admin@example.org") + + assert EmailWhitelist.mail_whitelisted("invalid@example.com") == false + assert EmailWhitelist.mail_whitelisted("test@test.co.uk") == false + end + + test "rejects when ALLOWED_EMAILS is nil" do + System.delete_env("ALLOWED_EMAILS") + + assert EmailWhitelist.mail_whitelisted("any@example.com") == false + end + + test "rejects when ALLOWED_EMAILS is empty string" do + System.put_env("ALLOWED_EMAILS", "") + + assert EmailWhitelist.mail_whitelisted("any@example.com") == false + end + + test "handles whitespace in email list" do + System.put_env("ALLOWED_EMAILS", " user@domain.com , admin@example.org , test@test.co.uk ") + + assert EmailWhitelist.mail_whitelisted("user@domain.com") == true + assert EmailWhitelist.mail_whitelisted("ADMIN@EXAMPLE.ORG") == true + assert EmailWhitelist.mail_whitelisted("test@test.co.uk") == true + assert EmailWhitelist.mail_whitelisted("invalid@example.com") == false + end + + test "case insensitive matching works" do + System.put_env("ALLOWED_EMAILS", "User@Domain.Com,ADMIN@EXAMPLE.ORG") + + assert EmailWhitelist.mail_whitelisted("user@domain.com") == true + assert EmailWhitelist.mail_whitelisted("USER@DOMAIN.COM") == true + assert EmailWhitelist.mail_whitelisted("admin@example.org") == true + end + + test "empty email returns false" do + System.put_env("ALLOWED_EMAILS", "user@domain.com") + + assert EmailWhitelist.mail_whitelisted("") == false + end + end +end +``` + +## 6. `README.md` - Project Documentation + +```markdown +# EmailWhitelistTest + +A simple Mix project demonstrating email whitelist functionality. + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `email_whitelist_test` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:email_whitelist_test, "~> 0.1.0"} + ] +end +``` + +## Usage + +The main function is `EmailWhitelist.mail_whitelisted/1` which checks if an email address is allowed. + +Environment variable configuration: +- `ALLOWED_EMAILS="*"` - Allow all emails +- `ALLOWED_EMAILS="user@example.com,admin@test.co.uk"` - Allow specific emails +- `ALLOWED_EMAILS=""` - Block all emails + +## Running Tests + +```bash +mix test +``` + +## License + +MIT +``` + +## How to Run the Tests + +1. **Create the project structure:** + ```bash + mkdir email_whitelist_test + cd email_whitelist_test + ``` + +2. **Create all files with the content above** + +3. **Initialize the Mix project:** + ```bash + mix init + ``` + +4. **Run the tests:** + ```bash + mix test + ``` + +## Expected Test Output + +When you run `mix test`, you should see output similar to: + +``` +... + +Finished in 0.05 seconds (0.04s async, 0.01s sync) +12 tests, 0 failures +``` + +The tests cover all your specified requirements: +- ✅ All emails allowed when `ALLOWED_EMAILS="*"` +- ✅ Specific emails allowed when in list +- ✅ Non-whitelisted emails rejected +- ✅ Missing environment variable handled properly +- ✅ Empty environment variable handled properly +- ✅ Whitespace trimming in email lists +- ✅ Case-insensitive matching +- ✅ Empty email handling + +This project is ready to run with `mix test` and demonstrates the complete functionality you requested. + +# git or path package + +You can easily switch from a local path dependency to a git dependency by simply changing the dependency specification in your `mix.exs` file. + +## Switching Process: + +### 1. **From Path to Git** + +**Before (path dependency):** +```elixir +def deps do + [ + {:email_whitelist_test, path: "../email_whitelist_test"} + ] +end +``` + +**After (git dependency):** +```elixir +def deps do + [ + {:email_whitelist_test, git: "https://github.com/yourusername/email_whitelist_test.git"} + ] +end +``` + +### 2. **Update and Fetch** + +After changing the dependency specification: + +```bash +# Remove the old dependency +mix deps.clean email_whitelist_test + +# Fetch the new git dependency +mix deps.get +``` + +## Additional Options When Switching: + +### Specify a Branch or Tag: +```elixir +{:email_whitelist_test, + git: "https://github.com/yourusername/email_whitelist_test.git", + branch: "main"} + +# Or for a specific tag +{:email_whitelist_test, + git: "https://github.com/yourusername/email_whitelist_test.git", + tag: "v1.0.0"} +``` + +### For Private Repositories: +```elixir +{:email_whitelist_test, + git: "https://github.com/yourusername/email_whitelist_test.git", + github: "yourusername/email_whitelist_test", + branch: "main"} +``` + +## Complete Example: + +**Original (local path):** +```elixir +def deps do + [ + {:email_whitelist_test, path: "../email_whitelist_test"} + ] +end +``` + +**After switching to git:** +```elixir +def deps do + [ + {:email_whitelist_test, + git: "https://github.com/yourusername/email_whitelist_test.git", + branch: "main"} + ] +end +``` + +## What Happens During the Switch: + +1. **Mix cleans** the old dependency (removes it from `_build` and `deps`) +2. **Mix fetches** the new dependency from the git repository +3. **Mix compiles** the new dependency +4. **Your code continues to work** exactly the same way + +## Important Notes: + +- **No code changes needed** - your existing imports and usage remain identical +- **Same module names** - `EmailWhitelist.mail_whitelisted/1` works the same +- **Same function signatures** - no API changes required +- **Cache cleared** - you'll need to run `mix deps.get` to fetch the new version + +## Quick Switch Command: + +```bash +# Edit your mix.exs file, then: +mix deps.clean email_whitelist_test +mix deps.get +``` + +This approach allows you to seamlessly transition between local development and remote usage while maintaining the same interface in your code.