From c6e3a490d2f32c7edad598c82b98272f0c9771f3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 16 Sep 2025 09:34:03 +0000 Subject: [PATCH] add forgotten files --- CLAUDE.md | 33 +++++++++++++++ lib/fun_core/basic_signup_allowlist.ex | 19 +++++++++ test/fun_core/basic_signup_allowlist_test.exs | 40 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 CLAUDE.md create mode 100644 lib/fun_core/basic_signup_allowlist.ex create mode 100644 test/fun_core/basic_signup_allowlist_test.exs diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e05257f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,33 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +- `mix compile` - Compile source files +- `mix test` - Run all tests +- `mix test test/path/to/test.exs` - Run a specific test file +- `mix test test/path/to/test.exs:LINE` - Run a specific test at a line number +- `mix deps.get` - Install dependencies +- `mix clean` - Clean build artifacts + +## Architecture + +This is an Elixir library that provides email allowlist functionality for restricting account creation. The library consists of two main modules: + +1. **BasicSignupAllowlist** (lib/basic_signup_allowlist.ex) - Public API module that reads the `SIGNUP_ALLOWLIST_EMAILS` environment variable and delegates to the functional core +2. **FunCore.BasicSignupAllowlist** (lib/fun_core/basic_signup_allowlist.ex) - Pure functional core that handles the allowlist logic without side effects + +The architecture follows a functional core/imperative shell pattern where: +- The functional core contains pure functions for email normalization and allowlist checking +- The outer module handles environment variable reading and provides the public interface + +## Environment Variables + +- `SIGNUP_ALLOWLIST_EMAILS` - Comma-separated list of allowed email addresses, or "*" to allow all emails + +## Project Configuration + +- Elixir version: ~> 1.18 (managed via mise) +- No external dependencies currently +- Test framework: ExUnit with async: false for integration tests that modify environment variables \ No newline at end of file diff --git a/lib/fun_core/basic_signup_allowlist.ex b/lib/fun_core/basic_signup_allowlist.ex new file mode 100644 index 0000000..33dc034 --- /dev/null +++ b/lib/fun_core/basic_signup_allowlist.ex @@ -0,0 +1,19 @@ +defmodule FunCore.BasicSignupAllowlist do + def normalize(email) do + email |> String.trim() |> String.downcase() + end + + def addresses_as_list(addresses_str) do + addresses_str + |> String.split(",") + |> Enum.map(&normalize/1) + end + + def mail_allowlisted_fun(signups_allowed, email_received) do + case signups_allowed do + nil -> false + "*" -> true + list_str -> normalize(email_received) in addresses_as_list(list_str) + end + end +end diff --git a/test/fun_core/basic_signup_allowlist_test.exs b/test/fun_core/basic_signup_allowlist_test.exs new file mode 100644 index 0000000..9f364b5 --- /dev/null +++ b/test/fun_core/basic_signup_allowlist_test.exs @@ -0,0 +1,40 @@ +defmodule FunCore.BasicSignupAllowlistTest do + use ExUnit.Case + import FunCore.BasicSignupAllowlist + + test "addresses_as_list" do + assert addresses_as_list("joe@example.com, jane@example.com") == [ + "joe@example.com", + "jane@example.com" + ] + end + + test "jane in list" do + lst = addresses_as_list("joe@example.com, jane@example.com") + assert "jane@example.com" in lst + end + + describe "Not allowlisted when allowed list is" do + test "not set" do + refute(mail_allowlisted_fun(nil, "joe@example.com")) + end + + test "empty" do + refute(mail_allowlisted_fun("j", "joe@example.com")) + end + end + + describe "Allowlisted when" do + test "*" do + assert(mail_allowlisted_fun("*", "jane@example.com")) + end + + test "Multiple set and one match" do + assert(mail_allowlisted_fun("joe@example.com, jane@example.com", "jane@example.com")) + end + + test "Matches with different casings" do + assert(mail_allowlisted_fun("joe@Example.com, jane@example.com", "Joe@example.com")) + end + end +end