File size: 5,208 Bytes
a7ffb22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import pytest

from mathly2 import config
from mathly2.config import load_settings

NAMES = ("SUPABASE_URL", "SUPABASE_PUBLISHABLE_KEY", "SUPABASE_SECRET_KEY", "TEACHER_EMAILS", "DEVELOPER_EMAILS", "ADMIN_EMAILS", "ANTHROPIC_API_KEY")


@pytest.fixture(autouse=True)
def clean_environment(monkeypatch):
    """Start with none of the settings in the environment; monkeypatch puts back whatever was there afterwards
    (load_dotenv writes into os.environ, so this also stops one test leaking into the next)."""
    for name in NAMES:
        monkeypatch.setenv(name, "")
        monkeypatch.delenv(name)


def write(tmp_path, text):
    path = tmp_path / ".env"
    path.write_text(text, encoding="utf-8")
    return path


def test_the_env_file_fills_in_what_the_environment_does_not_set(tmp_path):
    settings = load_settings(write(tmp_path, "TEACHER_EMAILS=A@School.test, b@school.test\n"))
    assert settings.teachers == {"a@school.test", "b@school.test"}
    assert settings.supabase_on is False                               # no Supabase values: local development


def test_a_real_environment_variable_wins_over_the_file(tmp_path, monkeypatch):
    monkeypatch.setenv("TEACHER_EMAILS", "real@school.test")
    settings = load_settings(write(tmp_path, "TEACHER_EMAILS=file@school.test\n"))
    assert settings.teachers == {"real@school.test"}


def test_all_three_supabase_values_in_the_file_turn_supabase_on(tmp_path):
    text = "SUPABASE_URL=https://p.supabase.test/\nSUPABASE_PUBLISHABLE_KEY=pub\nSUPABASE_SECRET_KEY=sec\n"
    settings = load_settings(write(tmp_path, text))
    assert settings.supabase_on and settings.supabase_url == "https://p.supabase.test"


def test_a_half_configured_file_is_refused(tmp_path):
    with pytest.raises(ValueError, match="all of SUPABASE_URL"):
        load_settings(write(tmp_path, "SUPABASE_URL=https://p.supabase.test\n"))


def test_a_missing_env_file_is_fine(tmp_path):
    assert load_settings(tmp_path / "nope.env").teachers == frozenset()


def test_the_two_default_files_are_this_apps_own_and_the_repository_root_one():
    assert config.ENV_FILE.parent.name == "mathly2" and config.ENV_FILE.name == ".env"
    assert config.ROOT_ENV_FILE == config.ENV_FILE.parent.parent / ".env" and config.ROOT_ENV_FILE.parent.name == "mathly"


def test_the_repository_env_is_used_when_this_app_has_none(tmp_path, monkeypatch):
    monkeypatch.setattr(config, "ENV_FILE", tmp_path / "missing.env")
    monkeypatch.setattr(config, "ROOT_ENV_FILE", write(tmp_path, "ADMIN_EMAILS=root-admin@x.test\nTEACHER_EMAILS=root-teacher@x.test\n"))
    settings = load_settings()
    assert settings.admins == {"root-admin@x.test"} and settings.teachers == {"root-teacher@x.test"}


def test_order_is_environment_then_this_apps_file_then_the_repository_file(tmp_path, monkeypatch):
    own = tmp_path / "own.env"
    own.write_text("TEACHER_EMAILS=own@x.test\nADMIN_EMAILS=own-admin@x.test\n", encoding="utf-8")
    root = tmp_path / "root.env"
    root.write_text("TEACHER_EMAILS=root@x.test\nADMIN_EMAILS=root-admin@x.test\nDEVELOPER_EMAILS=root-dev@x.test\n", encoding="utf-8")
    monkeypatch.setattr(config, "ENV_FILE", own)
    monkeypatch.setattr(config, "ROOT_ENV_FILE", root)
    monkeypatch.setenv("TEACHER_EMAILS", "env@x.test")
    settings = load_settings()
    assert settings.teachers == {"env@x.test"}             # the environment wins over both files
    assert settings.admins == {"own-admin@x.test"}          # this app's file wins over the repository's
    assert settings.developers == {"root-dev@x.test"}       # the repository's file fills what is left


def test_passing_a_file_reads_only_that_file(tmp_path, monkeypatch):
    monkeypatch.setattr(config, "ROOT_ENV_FILE", write(tmp_path, "ADMIN_EMAILS=root-admin@x.test\n"))
    assert load_settings(tmp_path / "nope.env").admins == frozenset()


def test_the_example_file_lists_every_setting_with_empty_values():
    example = (config.ENV_FILE.parent / ".env.example").read_text(encoding="utf-8")
    for name in ("SUPABASE_URL", "SUPABASE_PUBLISHABLE_KEY", "SUPABASE_SECRET_KEY", "TEACHER_EMAILS"):
        assert f"\n{name}=\n" in example


def test_the_container_image_never_gets_the_env_file():
    ignore = (config.ENV_FILE.parent / "Dockerfile.dockerignore").read_text(encoding="utf-8").split()
    assert "mathly2/.env" in ignore


def test_developers_and_admins_come_from_the_environment_and_the_file(tmp_path, monkeypatch):
    monkeypatch.setenv("DEVELOPER_EMAILS", "Dev@School.test")
    settings = load_settings(write(tmp_path, "ADMIN_EMAILS=head@school.test, deputy@school.test\nDEVELOPER_EMAILS=file-dev@school.test\n"))
    assert settings.developers == {"dev@school.test"}                      # the environment wins over the file
    assert settings.admins == {"head@school.test", "deputy@school.test"}   # from the file
    assert settings.teachers == frozenset()


def test_the_example_file_and_readme_name_the_new_settings():
    base = config.ENV_FILE.parent
    for name in ("DEVELOPER_EMAILS", "ADMIN_EMAILS"):
        assert f"\n{name}=\n" in (base / ".env.example").read_text(encoding="utf-8")
        assert f"`{name}`" in (base / "README.md").read_text(encoding="utf-8")