Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for the Config class #248

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -159,5 +159,8 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

# Visual Studio Code project settings
.vscode/

notes.md
data/
2 changes: 1 addition & 1 deletion devika.py
Expand Up @@ -183,7 +183,7 @@ def set_settings():
data = request.json
print("Data: ", data)
config.config.update(data)
config.save_config()
config.dump_config()
return jsonify({"message": "Settings updated"})


Expand Down
52 changes: 27 additions & 25 deletions src/config.py
Expand Up @@ -4,6 +4,8 @@

class Config:
_instance = None
_CONFIG_FILE = "config.toml"
_SAMPLE_CONFIG_FILE = "sample.config.toml"

def __new__(cls):
if cls._instance is None:
Expand All @@ -13,11 +15,11 @@ def __new__(cls):

def _load_config(self):
# If the config file doesn't exist, copy from the sample
if not os.path.exists("config.toml"):
with open("sample.config.toml", "r") as f_in, open("config.toml", "w") as f_out:
if not os.path.exists(self._CONFIG_FILE):
with open(self._SAMPLE_CONFIG_FILE, "r") as f_in, open(self._CONFIG_FILE, "w") as f_out:
f_out.write(f_in.read())

self.config = toml.load("config.toml")
self.config = toml.load(self._CONFIG_FILE)

def get_config(self):
return self.config
Expand Down Expand Up @@ -84,84 +86,84 @@ def get_logging_prompts(self):

def set_bing_api_key(self, key):
self.config["API_KEYS"]["BING"] = key
self.save_config()
self.dump_config()

def set_bing_api_endpoint(self, endpoint):
self.config["API_ENDPOINTS"]["BING"] = endpoint
self.save_config()
self.dump_config()

def set_google_search_api_key(self, key):
self.config["API_KEYS"]["GOOGLE_SEARCH"] = key
self.save_config()
self.dump_config()

def set_google_search_engine_id(self, key):
self.config["API_KEYS"]["GOOGLE_SEARCH_ENGINE_ID"] = key
self.save_config()
self.dump_config()

def set_google_search_api_endpoint(self, endpoint):
self.config["API_ENDPOINTS"]["GOOGLE_SEARCH"] = endpoint
self.save_config()
self.dump_config()

def set_ollama_api_endpoint(self, endpoint):
self.config["API_ENDPOINTS"]["OLLAMA"] = endpoint
self.save_config()
self.dump_config()

def set_claude_api_key(self, key):
self.config["API_KEYS"]["CLAUDE"] = key
self.save_config()
self.dump_config()

def set_openai_api_key(self, key):
self.config["API_KEYS"]["OPENAI"] = key
self.save_config()
self.dump_config()

def set_gemini_api_key(self, key):
self.config["API_KEYS"]["GEMINI"] = key
self.save_config()
self.dump_config()

def set_mistral_api_key(self, key):
self.config["API_KEYS"]["MISTRAL"] = key
self.save_config()
self.dump_config()

def set_groq_api_key(self, key):
self.config["API_KEYS"]["GROQ"] = key
self.save_config()
self.dump_config()

def set_netlify_api_key(self, key):
self.config["API_KEYS"]["NETLIFY"] = key
self.save_config()
self.dump_config()

def set_sqlite_db(self, db):
self.config["STORAGE"]["SQLITE_DB"] = db
self.save_config()
self.dump_config()

def set_screenshots_dir(self, dir):
self.config["STORAGE"]["SCREENSHOTS_DIR"] = dir
self.save_config()
self.dump_config()

def set_pdfs_dir(self, dir):
self.config["STORAGE"]["PDFS_DIR"] = dir
self.save_config()
self.dump_config()

def set_projects_dir(self, dir):
self.config["STORAGE"]["PROJECTS_DIR"] = dir
self.save_config()
self.dump_config()

def set_logs_dir(self, dir):
self.config["STORAGE"]["LOGS_DIR"] = dir
self.save_config()
self.dump_config()

def set_repos_dir(self, dir):
self.config["STORAGE"]["REPOS_DIR"] = dir
self.save_config()
self.dump_config()

def set_logging_rest_api(self, value):
self.config["LOGGING"]["LOG_REST_API"] = "true" if value else "false"
self.save_config()
self.dump_config()

def set_logging_prompts(self, value):
self.config["LOGGING"]["LOG_PROMPTS"] = "true" if value else "false"
self.save_config()
self.dump_config()

def save_config(self):
with open("config.toml", "w") as f:
def dump_config(self):
with open(self._CONFIG_FILE, "w") as f:
toml.dump(self.config, f)
62 changes: 62 additions & 0 deletions tests/test_config.py
@@ -0,0 +1,62 @@
from src.config import Config
import os
import shutil
from pytest import fixture


class TestConfig:

_TEST_CONFIG_FILE = "sample.config.toml"

@fixture
def setup(self):
# Delete Config singleton
Config._instance = None

@fixture
def patch_config(self, mocker, tmpdir):
"""
Patch the class Config
Config._CONFIG_FILE is changed to a path towards a temporary file
The temporary file is a copy of _TEST_CONFIG_FILE
"""
assert os.path.isfile(self._TEST_CONFIG_FILE)
p = tmpdir
p = shutil.copy(self._TEST_CONFIG_FILE, p)
return mocker.patch("src.config.Config._CONFIG_FILE", p)

@fixture
def patch_load_config(self, mocker):
"""
Patch load_config in class Config
The return value from load_config is changed to `{}`
"""
return mocker.patch("src.config.Config._load_config", return_value={})

def test_creation(self, setup, patch_config):
assert Config() is not None

def test_singleton(self, setup, patch_config):
assert Config() == Config()

def test_load_config_called_once_during_multiple_access(self, setup, patch_config, patch_load_config):
Config(), Config(), Config(), Config(), Config()
patch_load_config.assert_called_once()

def test_dump_config_correctly_saves_config(self, setup, patch_config):
# Read config from file and modify values
Config().set_bing_api_key("10random_bing_key01")
Config().set_logs_dir("10random_logs_dir01")
# (as of March 28th, setters save to file)
# Get the config that was written to file
config_saved = Config().get_config()
# Force loading of the config from the file by deleting singleton
Config._instance = None
# Get the config loaded from file
config_loaded = Config().get_config()

assert config_saved == config_loaded




Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT:
Delete extra lines