From 12d1e79b3f099bccec5d1f847d5de4378c140e83 Mon Sep 17 00:00:00 2001 From: Michael Witbrock Date: Sat, 5 Oct 2024 13:41:27 +1300 Subject: [PATCH 1/5] Got DB Tests to pass on my machine. Improved them a little using GPT suggestions --- paper_recommender/requirements.txt | 13 --- src/tell_von/requirements.txt | 5 ++ src/vonlib/database_driver.py | 3 +- src/web_server/app.py | 83 ++++++++++++++++++ src/web_server/templates/dashboard.html | 12 +++ src/web_server/templates/home.html | 17 ++++ src/web_server/templates/login.html | 17 ++++ ...e_driver_test.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 8611 bytes tests/test_vonlib/database_driver_test.py | 12 ++- 9 files changed, 144 insertions(+), 18 deletions(-) delete mode 100644 paper_recommender/requirements.txt create mode 100644 src/web_server/app.py create mode 100644 src/web_server/templates/dashboard.html create mode 100644 src/web_server/templates/home.html create mode 100644 src/web_server/templates/login.html create mode 100644 tests/test_vonlib/__pycache__/database_driver_test.cpython-312-pytest-8.2.2.pyc diff --git a/paper_recommender/requirements.txt b/paper_recommender/requirements.txt deleted file mode 100644 index eb8a166..0000000 --- a/paper_recommender/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ --e "git+https://github.com/Strong-AI-Lab/Von.git@a46877f9f34afcbcb986f06241129041a7df7e90#egg=paper_recommender&subdirectory=paper_recommender" -bs4==0.0.2 -bson==0.5.10 -configparser==7.0.0 -dataclasses==0.6 -DateTime==5.5 -feedparser==6.0.11 -openai==1.35.9 -pathlib==1.0.1 -pymongo==4.8.0 -requests==2.32.3 -slack_bolt==1.19.1 -typing==3.7.4.3 \ No newline at end of file diff --git a/src/tell_von/requirements.txt b/src/tell_von/requirements.txt index b01c21c..b2fd4d2 100644 --- a/src/tell_von/requirements.txt +++ b/src/tell_von/requirements.txt @@ -5,6 +5,10 @@ openai>=1.28.1 # Flask framework for creating web applications in Python Flask>=3.0.3 +# Testing frameworks +pytest>=7.4.0 +pytest-flask>=1.2.0 + # # Package 1 # package1==1.0.0 @@ -129,6 +133,7 @@ pyasn1==0.6.0 pyasn1_modules==0.4.0 pydantic==2.7.1 pydantic_core==2.18.2 +pymongo>=4.10.1 # AND MAKE SURE SEPARATE bson IS NOT INSTALLED pyparsing==3.1.2 pysimplegui>=5.0.5 requests==2.31.0 diff --git a/src/vonlib/database_driver.py b/src/vonlib/database_driver.py index 9ac0ba3..82b430e 100644 --- a/src/vonlib/database_driver.py +++ b/src/vonlib/database_driver.py @@ -16,8 +16,7 @@ def get_local_client(): client = MongoClient('mongodb://localhost:27017/') return client except errors.ConnectionError as e: - print(f"Error connecting to MongoDB: {e}") - return None + raise RuntimeError(f"Error connecting to MongoDB: {e}") class DatabaseDriver: diff --git a/src/web_server/app.py b/src/web_server/app.py new file mode 100644 index 0000000..494c375 --- /dev/null +++ b/src/web_server/app.py @@ -0,0 +1,83 @@ +from flask import Flask, render_template, redirect, url_for, request +from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user +from flask_pymongo import PyMongo +from bson.objectid import ObjectId +import argparse + +app = Flask(__name__) +app.secret_key = 'your_secret_key' # Replace with a secure secret key in production + +# Configure MongoDB +app.config["MONGO_URI"] = "mongodb://localhost:27017/yourdbname" # Change this for your MongoDB URI +mongo = PyMongo(app) + +# Configure Flask-Login +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = 'login' + +# User loader callback +@login_manager.user_loader +def load_user(user_id): + user = mongo.db.users.find_one({"_id": ObjectId(user_id)}) + if user: + return User(user['username']) + return None + +# User class +class User(UserMixin): + def __init__(self, username): + self.username = username + self.id = str(mongo.db.users.find_one({"username": username})['_id']) + +# Routes +@app.route('/') +def home(): + return render_template('home.html') + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + + # Verify username and password + user = mongo.db.users.find_one({"username": username}) + if user and user['password'] == password: # In production, use hashed passwords + login_user(User(username)) + return redirect(url_for('dashboard')) + else: + return 'Invalid credentials', 401 + + return render_template('login.html') + +@app.route('/dashboard') +@login_required +def dashboard(): + return render_template('dashboard.html', user=current_user.id) + +@app.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('home')) + +# Main function +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Run the Flask web server.') + parser.add_argument('--host', default='127.0.0.1', help='Host to listen on.') + parser.add_argument('--port', default=5000, type=int, help='Port to listen on.') + parser.add_argument('--background', action='store_true', help='Run server in the background.') + args = parser.parse_args() + + if args.background: + from multiprocessing import Process + + def run_app(): + app.run(host=args.host, port=args.port) + + p = Process(target=run_app) + p.start() + print(f"Server running in background on {args.host}:{args.port} (PID: {p.pid})") + else: + app.run(host=args.host, port=args.port) diff --git a/src/web_server/templates/dashboard.html b/src/web_server/templates/dashboard.html new file mode 100644 index 0000000..e41978d --- /dev/null +++ b/src/web_server/templates/dashboard.html @@ -0,0 +1,12 @@ + + + + + Dashboard + + +

Dashboard

+

Welcome, {{ user }}!

+

Logout

+ + diff --git a/src/web_server/templates/home.html b/src/web_server/templates/home.html new file mode 100644 index 0000000..6ed7b09 --- /dev/null +++ b/src/web_server/templates/home.html @@ -0,0 +1,17 @@ + + + + + Von + + +

Welcome to Von

+ {% if current_user.is_authenticated %} +

Hello, {{ current_user.id }}!

+

Go to Dashboard

+

Logout

+ {% else %} +

Login

+ {% endif %} + + diff --git a/src/web_server/templates/login.html b/src/web_server/templates/login.html new file mode 100644 index 0000000..71ea3e0 --- /dev/null +++ b/src/web_server/templates/login.html @@ -0,0 +1,17 @@ + + + + + Login + + +

Login

+
+ +

+ +

+ +
+ + diff --git a/tests/test_vonlib/__pycache__/database_driver_test.cpython-312-pytest-8.2.2.pyc b/tests/test_vonlib/__pycache__/database_driver_test.cpython-312-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6d241a097f20796455469a01e9b7b1cfa7c9be1 GIT binary patch literal 8611 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 literal 0 HcmV?d00001 diff --git a/tests/test_vonlib/database_driver_test.py b/tests/test_vonlib/database_driver_test.py index fda676a..c411095 100644 --- a/tests/test_vonlib/database_driver_test.py +++ b/tests/test_vonlib/database_driver_test.py @@ -20,15 +20,21 @@ def setUpClass(cls): cls.table_name = "TestTable" if cls.driver is None: - #print("setUpClass: cls.driver is None") + print("setUpClass: Failed to initialize DatabaseDriver") + + cls.skip_all_tests = True # Make sure we know the driver isn't good, and skip tests return cls.db = cls.driver.create_database(cls.db_name) cls.table = cls.driver.create_table(cls.db, cls.table_name) + cls.skip_all_tests = False def setUp(self): #print("Entering self.setup") + if getattr(self, 'skip_all_tests', False): # If the class setup failed, skip the tests; False is the default value, not the thing to test. This tests whether 'skip_all_tests' has been set to True. + self.skipTest("DatabaseDriver setup failed in setUpClass") + return if self.driver is None: self.skipTest("no valid database system") return @@ -45,8 +51,8 @@ def tearDownClass(cls): This method is called once after all tests. """ - if cls.driver is None: - #print("tearDownClass: cls.driver is None") + if not getattr(cls, 'skip_all_tests', False): + cls.driver.delete_database(cls.db_name) return cls.driver.delete_table(cls.db, cls.table_name) From 60c69f7c6264427b5633d57a33309208cad0eb99 Mon Sep 17 00:00:00 2001 From: Michael Witbrock Date: Sat, 5 Oct 2024 22:11:39 +1300 Subject: [PATCH 2/5] Much of login setup for web UI, including tests --- src/tell_von/requirements.txt | 1 + src/vonlib/database_driver.py | 2 +- src/web_interface/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 182 bytes .../__pycache__/user_data.cpython-312.pyc | Bin 0 -> 4463 bytes src/{web_server => web_interface}/app.py | 23 ++-- .../templates/dashboard.html | 0 .../templates/home.html | 0 .../templates/login.html | 0 src/web_interface/user_data.py | 99 ++++++++++++++++++ .../app_test.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 455 bytes ...ser_data_test.cpython-312-pytest-8.2.2.pyc | Bin 0 -> 5689 bytes tests/test_web_interface/app_test.py | 52 +++++++++ tests/test_web_interface/user_data_test.py | 33 ++++++ 14 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 src/web_interface/__init__.py create mode 100644 src/web_interface/__pycache__/__init__.cpython-312.pyc create mode 100644 src/web_interface/__pycache__/user_data.cpython-312.pyc rename src/{web_server => web_interface}/app.py (70%) rename src/{web_server => web_interface}/templates/dashboard.html (100%) rename src/{web_server => web_interface}/templates/home.html (100%) rename src/{web_server => web_interface}/templates/login.html (100%) create mode 100644 src/web_interface/user_data.py create mode 100644 tests/test_web_interface/__pycache__/app_test.cpython-312-pytest-8.2.2.pyc create mode 100644 tests/test_web_interface/__pycache__/user_data_test.cpython-312-pytest-8.2.2.pyc create mode 100644 tests/test_web_interface/app_test.py create mode 100644 tests/test_web_interface/user_data_test.py diff --git a/src/tell_von/requirements.txt b/src/tell_von/requirements.txt index b2fd4d2..0110c81 100644 --- a/src/tell_von/requirements.txt +++ b/src/tell_von/requirements.txt @@ -101,6 +101,7 @@ debugpy==1.8.1 distro==1.9.0 exceptiongroup==1.2.1 Flask>=3.0.3 +flask_testing>=0.8.1 gcloud==0.18.3 google-api-core==2.19.0 google-api-python-client==2.128.0 diff --git a/src/vonlib/database_driver.py b/src/vonlib/database_driver.py index 82b430e..1e3878c 100644 --- a/src/vonlib/database_driver.py +++ b/src/vonlib/database_driver.py @@ -197,7 +197,7 @@ def create_database(self, database_name): dblist = self.client.list_database_names() if database_name not in dblist: - db = self.client[database_name] + db = self.client[database_name] # This will create a new database only if data is stored in it. return db else: return None diff --git a/src/web_interface/__init__.py b/src/web_interface/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/web_interface/__pycache__/__init__.cpython-312.pyc b/src/web_interface/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cabbb2f3a468a1ddc91aa5ea1a756dbe798295e GIT binary patch literal 182 zcmX@j%ge<81jP>+(n0iN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!3U#)M2`x@7Dvl}7 zEJ-Sg@y|`2%H*!R zyOLspE{MVh8b}ZqJw!rk*hd*YR6W(l^wdKyOE3nBg#rj@d+1HE4h3@R%r19HeT)L} z0-QH*-psst^XAPA|IyawC-5a5%xo=4$UkvneXa(wT>|7A!V{j!kvx-R7{nbpN8Xuq zF+?CqH}9lw-gN=^csF1k#E^0r#-s-+2Ih5;QNnv45T1SRfF)dIDLa3_#9JZtP_M4Be@veZZY zGY;PIfU#Etl;xcdh&>O&+|@E~;!TSJ4ct5}c_n1UL_kLy` zYa(k|iWYd_hx6?h3|d_z^GMzG&9_Q4){4w?kihT0v*;|HdX1#pYoueFvEoa3ymz8okhYjw@o3(IDSmlJb%`E8PZyqNc?x9pJ`xN$?U?I8jnLL-drrk)Wa-G{1mukhrW& z(_oOG?$w~ELN3Sc)WfA`RB#*m&yYlaHPkh?NQ%II{89ixEWNst1y=q3tf;uMY@K^lQ^bn3MJnjWq5LMlUwkF)VGn-TNfXSOO>_4zm zz$CUIv1wM=m^QU4m=q1i|Mc1L;PfqER;HH)H6u??igX04(DVphqB&_HPepb5@)r<~ zVmdcHb!luootXwVO)GMCdYNWYU>%ib(pfrf?>Vq=PxnLgq>wT)RERX(I?$wHo>Be{ zphULfYHXzZNj)6X z!hIVDk3CJSU0b`TeU#R?O!Z(^YtL>*->F4EtVBQ5&i=X@y;cs_15qsyug4O#*!fEA zymn!-8oOSetcQHakyB;o_iS{7ZF>}4nR=33O+HDjrnFf9+HcmUYG+0(XGUvh#w%yW zwGTegLK839$!$0&$u_DL;skM{>O}#0UDcZ+&zOk>BXArGQOnhP*FHdTtOg~r8IIJ#y_ImU#`el_ApIQMkh#n9|zECo8Q{M_4=E% zkB}W&HPe=_3l9q#d)Tz3(Ne)0)D!p_UJ~Kh;3Wp?oUwpFwK?basqt37GH+K&TM$~n zA|o`r2S?FS3afUF->(Ny?b=>+m<{3c#%+Q{*ufaz4lXw~i!EHX9Xq&;ma>IwzbfwC zzq5gg&B6+Lao5*a*{AY1R`#j#-;&3yWd~GrlJHH)sG#7+?7$TQM&h247DawQ4#R@( zObNW1Hhp&5)EO?}H{@izJNa#7WM>4C$0R3XXQDfy{FoXX4Fxi~3X-x*p}84DT6Zih zDtPmdq|9JvQ;=x{HI*u~TOn>yJ_bJJDS#5GcXMUemu(w?NG))@5;$HByi*IDt^`i6 zpLj8+y+2wFTrE55fwuDCMxgryJtcrrN<>kq;o2Vm;m=cPuJDI zF8^hDU3hW#yUxEK`P-52!dmG1OZL;f0gfST1b76H-2r}&7*7mdKKeW1kl%wr_la~l zWz;MMr7iyi7Z;!`QFEoxY5Ay?ItJ_thB^4D>uA~amH&<3K1K-GhlaJjexpiaX2J^9 zG{$=WNF4`}Z8HPZ!w0^;|M0%X#@^VT0njR+-DQmj_Z9d;$a0D~pF}o%*WeDYVEN&4 zIrPQ`U>mW=0K*+t^Rd$%7WD?ookI`@@Uzpg{EPke_u+w!kSTPu`oJLF0*OW{MmMyi z;zXwouMRIOmD2sGR9@m2bBG61sk@8mTw}$(B#C@R4_V!t(LTsB<`}a!VENJmMx$>w z_PP&5Fg!r4qk%S?6S)^9F>}c8B6uIc83b50K#+XgIZ+@)dFM8dDPWfVyqkc|KR zkofS~>mE3LPRPhEn8?EhB`b+D!mNA+DWeFCL~8itDo!yzjN|^qUNCRuV;LV4)+6O} z09#ImVSXU-AIQl~U!dfvcOETyO7|+HqaJ&!@kp7$=7;(H{xItvLr_*cypc2(W5L-+M_;8=L_ literal 0 HcmV?d00001 diff --git a/src/web_server/app.py b/src/web_interface/app.py similarity index 70% rename from src/web_server/app.py rename to src/web_interface/app.py index 494c375..62fa2ce 100644 --- a/src/web_server/app.py +++ b/src/web_interface/app.py @@ -1,25 +1,26 @@ from flask import Flask, render_template, redirect, url_for, request from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user from flask_pymongo import PyMongo -from bson.objectid import ObjectId +from web_interface.user_data import VonUser # work our why it doesn't like this import when it's in the same directory import argparse +#For details, see https://naoinstitute.atlassian.net/browse/JVNAUTOSCI-111 +# write a minimal local web page server, that includes login and identity tracking, and +# is easy to redeploy on a remote server (e.g. one running on aws or google cloud or Azure). +# I want to be able to easily call python backend code from the server. +# Make sure the start code for the local server has an option to run in foreground or background. app = Flask(__name__) app.secret_key = 'your_secret_key' # Replace with a secure secret key in production -# Configure MongoDB -app.config["MONGO_URI"] = "mongodb://localhost:27017/yourdbname" # Change this for your MongoDB URI -mongo = PyMongo(app) - # Configure Flask-Login login_manager = LoginManager() login_manager.init_app(app) -login_manager.login_view = 'login' +login_manager.login_view = 'login' # User loader callback @login_manager.user_loader def load_user(user_id): - user = mongo.db.users.find_one({"_id": ObjectId(user_id)}) + user = VonUser.find() if user: return User(user['username']) return None @@ -27,8 +28,10 @@ def load_user(user_id): # User class class User(UserMixin): def __init__(self, username): + self.vonuser = VonUser(username) self.username = username - self.id = str(mongo.db.users.find_one({"username": username})['_id']) + self.id = VonUser.get_id(username) + #str(mongo.db.users.find_one({"username": username})['_id']) # Routes @app.route('/') @@ -42,8 +45,8 @@ def login(): password = request.form['password'] # Verify username and password - user = mongo.db.users.find_one({"username": username}) - if user and user['password'] == password: # In production, use hashed passwords + user = VonUser(username) + if user and user.get_password() == password: # In production, use hashed passwords login_user(User(username)) return redirect(url_for('dashboard')) else: diff --git a/src/web_server/templates/dashboard.html b/src/web_interface/templates/dashboard.html similarity index 100% rename from src/web_server/templates/dashboard.html rename to src/web_interface/templates/dashboard.html diff --git a/src/web_server/templates/home.html b/src/web_interface/templates/home.html similarity index 100% rename from src/web_server/templates/home.html rename to src/web_interface/templates/home.html diff --git a/src/web_server/templates/login.html b/src/web_interface/templates/login.html similarity index 100% rename from src/web_server/templates/login.html rename to src/web_interface/templates/login.html diff --git a/src/web_interface/user_data.py b/src/web_interface/user_data.py new file mode 100644 index 0000000..1121f90 --- /dev/null +++ b/src/web_interface/user_data.py @@ -0,0 +1,99 @@ +from bson.objectid import ObjectId +from vonlib import database_driver as vondb + +config = {} +config["USER_DB"] = "vonUsers" +config["USER_COLLECTION"] = "users" + +vonuserdb = config["USER_DB"] +vonusercollection = config["USER_COLLECTION"] + +class VonUser: + vomongo = vondb.DatabaseDriver() + userdb=None + usercollection=None + + @classmethod + def set_userDB(cls, userdb_name=vonuserdb, usercollection_name=vonusercollection): + """ + Sets the user database collection for the class. + + This method assigns the provided user database name to the class attribute `userdb`. + It then checks if the database exists in the `vomongo` instance. If the database does not exist, + it attempts to create it. If the database still does not exist after the creation attempt, + an exception is raised. + + Args: + userdb (str): The name of the user database to set. + + Raises: + Exception: If the database does not exist and could not be created. + """ + + user_database = cls.vomongo.get_database(userdb_name) + if user_database is None: + user_database = cls.vomongo.create_database(userdb_name) + if user_database is None: + raise Exception(f"Database {userdb_name} does not exist and could not be created.") + + user_collection = user_database.get_collection(usercollection_name) + if user_collection is None: + user_collection=user_database.create_collection(usercollection_name) + if user_collection is None: + raise Exception(f"Collection {usercollection_name} does not exist and could not be created.") + + # put self in 湛 Zhan von Neumarkt + user_collection.insert_one({'username': 'zhan', 'email': 'vonneumarkt@gmail.com','password': 'password'}) + + cls.userdb = user_database + cls.usercollection = user_collection + print(f"VonUser: DB={cls.userdb.name}, Coll={cls.usercollection.name}") + + @classmethod + def get_userCollection(cls): + return cls.usercollection + + @classmethod + def get_userDB(cls): + return cls.userdb + + + + def __init__(self, username): + self.username = username + vu=VonUser.get_userCollection().find_one({"username": username}) + if vu is None: + raise Exception(f"User {username} not found.") + + self.email = str(vu['email']) + self.id = str(vu['_id']) + self.password = str(vu['password']) + print(f"VonUser: {self.username} {self.email} {self.id} {self.password}") #@TODO remove this debug print and the insecure password handling + + def update_email(self, new_email): + self.email = new_email + + def get_username(self): + return self.username + + def get_id(self): + return self.id + + def get_password(self): + return self.password + + def get_email(self): + return self.email + + # def get_user_info(self): + # return { + # 'username': self.username, + # 'email': self.email + # } + + +VonUser.set_userDB(vonuserdb, vonusercollection) + + + + diff --git a/tests/test_web_interface/__pycache__/app_test.cpython-312-pytest-8.2.2.pyc b/tests/test_web_interface/__pycache__/app_test.cpython-312-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d722cdf0f639974dc109319af6f2e6b671984bee GIT binary patch literal 455 zcmX|5O-sW-5S?wCHccBuM8s=vz4Ql&iqa}5q6a??VId^#wk&QoVRzG-v;GHvgTKY2 zw?Osa$(ztyPfk+Xft`Kx-psstcU=cCtPhwRH2}VgVjY!#u((9<3L_X10Ujy;N>m+I z2t+l{A}git)lsR4Mr3<-$yG%&ay+NRHPMRNUYkHhy4Fvz__|AS>s$o-5rKWjwCM+$ zA$CMO;nGhvQ(ZT=!;}ldrOw+YN#<{=eLu+z4)p?!jfTgvr`Sw!!}8{a${TCW!QrCa z4^(calAEH)t#RS6s(4QBme?WqAkpctd**ANfL!BnN!5zjXI~o z8_7;p({pKiQD9pIG?HBAMG$Kc9Y*85j|y(DPa%f9Mbjyrq7AHw!x-L|cg zTdvnu#0_a?MiZCcJ2Ku_n$n!?XJIEllgHk1J_Vg{YEpA1nh&V!O7F0`{T#gUFx17X%E^U+l8NXELX>^ z!ok&G%Ra#!``L4iCA5TYP=AkJGHhF?R)d!6)rLVWKL@Y%VzX?!<(lKGb{#kz|A6jX ztv79xvFZzUy=d4v6PM8)W8Oju{?K`LMzHNAO6%0$r_a}E)o^v|M#DC023p-8!p%fy z&{Tf_H3sH679X6552oXTlffX6{#LN%U~p{G!APBF*GBQ;+l4Ej&M90kyG2^~sAkR5 z@}gCkwH7VA-l$qNw{Yo(Ytfov7d|=v{)a}f0N7GM`#6m1*R7&nt^v!uQL+lSdEGQz zLr0d0#*)8_*)p;=Jq-FWvO7NkxhXtUa&2W~O&R$neeclm2g-?#a z>D<34XV&3BLNew`%4WiIU)n9NStFV5#g$p3Jb6c*Ob61)0H>gubTa z5x`QmCWfUXh7bt|%=H154#(u@xTdI$P>#kq6Yx3W1neX+sY$$DyY%7f#>qZ>GDkX7 z39(Z#RWs!&A$F?n9)6~nq$E^Vxg_3(*on9)!9&15LghIwEovbTO1Dygb1CLK$&;MB zFz6R^2ZNyvi0A41@d zCg!L8y)nqVG4#wEv9C7i7t+TYkZtU`?33lIMvY=I0`UI9s=lI}yL7*i;w_!PfpI)Vgq zb2^ITc_6;Rw(Lt~Q_Ij-ar#9hFCjUIWDH3j$;&|E$--1TS(tXvenENwlpaE6mKXxa zmxGLn9)n3_bN&PZ2tKg)tLZP_y*b+<7uj}^JsgvtP-I7N4~fAx3uTrV z^yZBcgWi1I!~g(402jglf9Hy2UMNR?ODL4Pep5X)DLrFJ!R9|lp3HBV9rIf^!RhO_ zE+_ac)5pjEIll!(ZyIocl|CGbUhfgEH&?O{tbNHWQutF#psyjp*zYR{KaNkZb_=Ua zia!rO%Om>i1&Xk_?V!GwFN}(EEk&nL1dHx(VvmLRN$lm3OapNcUBeh2MDP(-gkfD) zG#pEJT-q$TO={88z==5Nd=CUdIT>h^{cB|Z19ISDYN(wWT}zGLPrY#SLWktrJ9IzH z>~3ccuVoJ3&m2L}7;Ep)eUFT>sI2AuG_TMZZu+BoP}5HYWi-^;6V$p+%dX{Tx>J5C zoMR61)nJJ~vZ=yle{Zk~m%%lwW*HzdQ?3YX1I*|f>9f_k*|e>9=y@YSt5?D=M*u|*8c_T#o*8Y literal 0 HcmV?d00001 diff --git a/tests/test_web_interface/app_test.py b/tests/test_web_interface/app_test.py new file mode 100644 index 0000000..2fac08e --- /dev/null +++ b/tests/test_web_interface/app_test.py @@ -0,0 +1,52 @@ +import unittest +from flask import Flask +from flask_testing import TestCase +from flask_login import login_user + +# class AppTestCase(TestCase): +# def create_app(self): +# app.config['TESTING'] = True +# app.config['SECRET_KEY'] = 'your_secret_key' +# return app + +# def setUp(self): +# self.client = app.test_client() + +# def test_home_redirects_to_login(self): +# response = self.client.get('/', follow_redirects=False) +# self.assertEqual(response.status_code, 302) +# self.assertIn('/login', response.location) + +# def test_login_page_loads(self): +# response = self.client.get('/login') +# self.assert200(response) +# self.assertIn(b'Login', response.data) + +# def test_valid_login(self): +# response = self.client.post('/login', data=dict( +# username='testuser', +# password='testpass' +# ), follow_redirects=True) +# self.assert200(response) +# self.assertIn(b'Welcome, testuser', response.data) + +# def test_invalid_login(self): +# response = self.client.post('/login', data=dict( +# username='wronguser', +# password='wrongpass' +# ), follow_redirects=True) +# self.assert200(response) +# self.assertIn(b'Invalid credentials', response.data) + +# def test_logout(self): +# with self.client: +# self.client.post('/login', data=dict( +# username='testuser', +# password='testpass' +# ), follow_redirects=True) +# response = self.client.get('/logout', follow_redirects=True) +# self.assert200(response) +# self.assertIn(b'You have been logged out', response.data) + +# if __name__ == '__main__': +# unittest.main() \ No newline at end of file diff --git a/tests/test_web_interface/user_data_test.py b/tests/test_web_interface/user_data_test.py new file mode 100644 index 0000000..60ad7d5 --- /dev/null +++ b/tests/test_web_interface/user_data_test.py @@ -0,0 +1,33 @@ +import pytest +from web_interface.user_data import VonUser + +def test_set_userDB(): + VonUser.set_userDB('testDB', 'testCollection') + assert VonUser.userdb.name == 'testDB' + assert VonUser.usercollection.name == 'testCollection' + +def test_init_user(): + user = VonUser('zhan') + assert user.get_username() == 'zhan' + assert user.email == 'vonneumarkt@gmail.com' + assert user.id is not None + +def test_database_structure(): + VonUser.set_userDB() # use default values + db=VonUser.get_userDB() + assert db.name == 'vonUsers' + coll=VonUser.get_userCollection() + assert coll.name == 'users' + +# def test_update_email(mock_user_collection): +# VonUser.usercollection = mock_user_collection +# user = VonUser('testuser', 'test@example.com') +# user.update_email('new@example.com') +# assert user.email == 'new@example.com' + +# def test_get_user_info(mock_user_collection): +# VonUser.usercollection = mock_user_collection +# user = VonUser('testuser', 'test@example.com') +# user_info = user.get_user_info() +# assert user_info['username'] == 'testuser' +# assert user_info['email'] == 'test@example.com' \ No newline at end of file From 4e25c02e3f5b05a6c44a523ab50535e5362c0351 Mon Sep 17 00:00:00 2001 From: Michael Witbrock Date: Sat, 5 Oct 2024 22:52:39 +1300 Subject: [PATCH 3/5] Login and page turning is working --- .../__pycache__/user_data.cpython-312.pyc | Bin 4463 -> 4820 bytes src/web_interface/app.py | 8 ++++---- src/web_interface/templates/home.html | 2 +- src/web_interface/user_data.py | 7 +++++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/web_interface/__pycache__/user_data.cpython-312.pyc b/src/web_interface/__pycache__/user_data.cpython-312.pyc index 466ced1c4e4c25c18bac52c7dc2d8774c36bcbfc..a2ef007eb7941f5aa0c1c6a4ca68f5c34ec6838e 100644 GIT binary patch delta 723 zcmZ9J-%C?r7{}k|oNb=%c+T06vQn^3YjJE^Or%Y1QXyo7M53e!g1(z9bc1(OvQ`Qi zcxk>DbUl;p=z;S&-{&Xq^S(bicF&ui4a3LK z5*o~ZGTxagz-t+V#Y9@;?sZnC9NLu0J?m`B;!Q|D*N_43MFzPK8R9zf#0uEWl{e6< zMLpP~;&=1_@q(4ueL(S_mE}IPt#T;38;KH>|JG%X)nlckticK^HXTZqg6WVtXgY`a7he~y;P=J-rLmlkvqL2KHoXrq*p<&LtvRKUb43x~rZOi^FGVRi)v zJq%9dbK`}y==m2L#G1kYVvT*(&6*zB(IZv8v!=&(^w`_<`+FZ}E7RNE*Q@%C4P{R^ zHv0EMr}nLuP4Tkp`ID*@-PWR_1N-L`M8hhM_Z7HB7y9y%ey&ekL?uE9M=cbEu*i)O z3Iv1&s;v2$CvEU91ZYHy3W^L^z&c delta 320 zcmcbj`d*3eG%qg~0}wY)ND@eBGT&k; zPR&Ux;sZ;Sl}=XX)nv4r?92OtQGc=-pBq!>jeFB52$G6V^mf(Q!` zVGSZ|L4^HeFF`rRy2+V>Nlc%(CO;JPQE~=~-(pM7&r8cpFLDDZat9Hh5Ghgw5gwEM bg(QHk$`JD8@n&R{`p5txKV(eaD

Welcome to Von

{% if current_user.is_authenticated %} -

Hello, {{ current_user.id }}!

+

Hello, {{ current_user.username }}!

Go to Dashboard

Logout

{% else %} diff --git a/src/web_interface/user_data.py b/src/web_interface/user_data.py index 1121f90..c300953 100644 --- a/src/web_interface/user_data.py +++ b/src/web_interface/user_data.py @@ -57,6 +57,13 @@ def get_userCollection(cls): def get_userDB(cls): return cls.userdb + @classmethod + def find_by_id(cls, id): + vu=cls.get_userCollection().find_one({"_id": ObjectId(id)}) + if vu is None: + return None + return VonUser(vu['username']) #return VonUser object + def __init__(self, username): From 7649bfb40e156eee821d8c3972bf69fdc8e0092a Mon Sep 17 00:00:00 2001 From: Michael Witbrock Date: Sun, 6 Oct 2024 10:41:08 +1300 Subject: [PATCH 4/5] Get signup working and login working cleanly. --- .../__pycache__/user_data.cpython-312.pyc | Bin 4820 -> 5973 bytes src/web_interface/app.py | 69 ++++++++++++++++-- src/web_interface/templates/login.html | 18 ++++- src/web_interface/templates/signup.html | 37 ++++++++++ src/web_interface/user_data.py | 37 +++++++++- ...mconnect_test.cpython-312-pytest-8.2.2.pyc | Bin 3686 -> 3686 bytes tests/test_vonlib/llmconnect_test.py | 2 +- 7 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 src/web_interface/templates/signup.html diff --git a/src/web_interface/__pycache__/user_data.cpython-312.pyc b/src/web_interface/__pycache__/user_data.cpython-312.pyc index a2ef007eb7941f5aa0c1c6a4ca68f5c34ec6838e..6debd73485a7fcad3685729c92ed04c595fdc867 100644 GIT binary patch delta 2434 zcmaJ?U2GIp6ux(6e`a>NyWQ=73+*gL%0O8VP)Y>_i@`=Is8o`Tv2J&!?Udb_I^iAH_!fd}IvCPtq;XJ&WVN^p|-=G=4c zIrrRqzH{f+Be#>0Z^PjbfoI1jbn?B}l}P)-Wf)x}Dp6^czabHAAz~d2MVaZQHY^rH)Od?J1`zGo@wD zZd6|R@-+RL#p(RBkWk=Fa-*_jDvAl#jT; z^wQwXtNdGBN3r2$(-^fiYfwJ+^xzfSHJ*4n&D&Zz?qd0 z>;tj4?FCfeA-JxWiWiH^LI*fH;`;-59*WOh#t|lTLme~Wiorhej*WLSayVUexN`*? z4ek-KPeZ+Ql-&v)>*@hrrgFi7Sl5ZKu|LIwT#2%yp{9k0wA_eW5`}kDuy%uc{lZ?o z9tG|FrD5PSx~Y!Ox#dB!7u%~P`mf~`blyh}xZ9T^Yf@rGN~}t~Yf}G;)PH^Y^X<#K zhE}Cxi`=>tUL0I+YTHnvn;p_6x`4-2^pD~dP?TiD%*zvI!BCSPm6Bj}`BW5ZtahlZ zguL|e=wR#JnPY>OY;}!mxWE9>u|-@-N93JjvzV#Y(iZvMyhr3#s!=v0YDlMZua8rVq_zS zu`jLqXzWh3Pdbmf_#^2^Cv1=kji`6hq1v{xHHh?tC>Lh1OAe+?ge)ZEW(HqcEaPfX(YY7 zQdiwxbz-xcb+)J#CX=?7DOh@b&hlYO*mO?doM~kBaSMOc)^n}~el-*Nq&0$?lL#(H z-GexV!`i0)5f0_d9<`9wp0q~6g4D2I1t{~B(jUo|pUCj9Y(Bof*QB)bE7%Q6rR~#uh;*He-g({o3%reZYge{MWktQltk*ab*p`=6#J|>$!rc6Q{ z^MRzIgq8s~3o2SX$uE(Y3G?3|OuHk23%#1Dl@lF#FPf_9@5ZDFCD#K+@DKDG)YZ<< zmd&}^6kGe}b$VM|r0)lqdV>^VP+2^r`;_a{2#Y>Rm%gRq&rn7jiX_EtDJJUDj~A74 zWN({}%zz;CGwloc4((VdY06D98ewq|_dy*LzY?5-%PSF2n z8Qm%IogAg1dq2w`NT*z-akXeQQj4}_ne}Rszgs)KP%V}!`8m7lYS)U6vt)B-$gWm* zOl}pcrt32?fFpfzUQN@GxT?Mx3YIJgsLj}xDZW?RDzU$iR~n0Lvx z#F6mez;TR=0<4hVV)3n5b|aQ;X<42V#qfCg32;EoemM9+jOTo>kyFeewMD1-$D7IgsVmmdlyxkM< zU8384)Hk%PQr~`reouM?Ql?WBOHCU=H!|zkR^Imhf=Z0zt8`WPjw_{Eu7JYNdX_oc znlCNzb7+}D@c4LFdIi;8qJL{clU1A5E9MJ)8a(*DIPU

Login

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} +
- +


- + +


+
+

Don't have an account? Sign up here.

diff --git a/src/web_interface/templates/signup.html b/src/web_interface/templates/signup.html new file mode 100644 index 0000000..9f85d7e --- /dev/null +++ b/src/web_interface/templates/signup.html @@ -0,0 +1,37 @@ + + + + + Signup + + +

Sign Up

+ + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + +
+
+

+ +
+

+ +
+

+ +
+

+ + +
+

Already have an account? Log in here.

+ + diff --git a/src/web_interface/user_data.py b/src/web_interface/user_data.py index c300953..0e10c0e 100644 --- a/src/web_interface/user_data.py +++ b/src/web_interface/user_data.py @@ -1,4 +1,6 @@ from bson.objectid import ObjectId +from werkzeug.security import generate_password_hash, check_password_hash + from vonlib import database_driver as vondb config = {} @@ -42,13 +44,36 @@ def set_userDB(cls, userdb_name=vonuserdb, usercollection_name=vonusercollection if user_collection is None: raise Exception(f"Collection {usercollection_name} does not exist and could not be created.") - # put self in 湛 Zhan von Neumarkt - user_collection.insert_one({'username': 'zhan', 'email': 'vonneumarkt@gmail.com','password': 'password'}) - cls.userdb = user_database cls.usercollection = user_collection print(f"VonUser: DB={cls.userdb.name}, Coll={cls.usercollection.name}") + @classmethod + def create_user(cls, username, email, password): + """ + Creates a new user in the user database collection. + + This method creates a new user document in the user database collection. The user document + contains the provided username, email, and password. + + Args: + username (str): The username of the new user. + email (str): The email of the new user. + password (str): The password of the new user. + + Returns: + VonUser: A VonUser object representing the new user. + """ + + user_dict = { + 'username': username, + 'email': email, + 'password': password #should be hashed by now + } + + cls.get_userCollection().insert_one(user_dict) + return VonUser(username) + @classmethod def get_userCollection(cls): return cls.usercollection @@ -64,6 +89,12 @@ def find_by_id(cls, id): return None return VonUser(vu['username']) #return VonUser object + @classmethod + def find_by_username(cls, username): + vu=cls.get_userCollection().find_one({"username": username}) + if vu is None: + return None + return VonUser(username) #return VonUser object def __init__(self, username): diff --git a/tests/test_vonlib/__pycache__/llmconnect_test.cpython-312-pytest-8.2.2.pyc b/tests/test_vonlib/__pycache__/llmconnect_test.cpython-312-pytest-8.2.2.pyc index 72c722286eef54c258ee0a236a8b718d821a18ca..0de702e404ce6a4fa4acf1a06ba71fd90e4c94ed 100644 GIT binary patch delta 22 ccmaDR^Gt^KG%qg~0}#kHq)$ diff --git a/tests/test_vonlib/llmconnect_test.py b/tests/test_vonlib/llmconnect_test.py index 0d17260..557726f 100644 --- a/tests/test_vonlib/llmconnect_test.py +++ b/tests/test_vonlib/llmconnect_test.py @@ -18,7 +18,7 @@ def test_ask_llm_1(self): #, mock_openai): assert response is not None expected_response = "os" self.assertIn(expected_response, response) - expected_response = "listdir" + expected_response = "listdir" #this doesn't always happen self.assertIn(expected_response, response) From 3dcf9e4a3164bb7850c578cb01ef7e0581018837 Mon Sep 17 00:00:00 2001 From: Michael Witbrock Date: Sun, 6 Oct 2024 13:59:21 +1300 Subject: [PATCH 5/5] Refactor secret key and database configuration to use environment variables; enhance user creation tests. --- .../__pycache__/user_data.cpython-312.pyc | Bin 5973 -> 6083 bytes src/web_interface/app.py | 2 +- src/web_interface/user_data.py | 15 +++++----- ...ser_data_test.cpython-312-pytest-8.2.2.pyc | Bin 5689 -> 7360 bytes tests/test_web_interface/user_data_test.py | 26 ++++++++++++++---- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/web_interface/__pycache__/user_data.cpython-312.pyc b/src/web_interface/__pycache__/user_data.cpython-312.pyc index 6debd73485a7fcad3685729c92ed04c595fdc867..500d483ad96e4956e9183d48d093840d95983125 100644 GIT binary patch delta 769 zcmZ{hO-K}B7{{MycK4mxZ{68lR7^|EGC>QX4l87u$%f66=@J7X)H}+?e#mSXlHdk= zDhapupq-59Ab9Ao(4kw@A^dxOCSxJJT#9>k2{& zbhiWs+z#(8JaFz>L!t}x7;esb$^SblZ;$RwDm#-yz}-dQp3;N1HqIJ%d$zAhBvc5q z*y;UcN3HZa(d|zrw26U)b~!#YajC!NK8UK_8M{6>Lha$pstv3!XA#L6KK5Yt`u-w$$65%fClC9UT^ z(ykEqlc_vF8s*vAkhwrb1t0+CQ>wfsWt zxCx`88onX3Te<9HYD!#*jIe;c?X7qgX%C!QjalvI-z6)fhIMsPypOD~PJd0x9;}~P9 zkK>HxJ|5sB+XhjYCWyu~Ng_;55@l*xL}yE?f!SDBzO*}d&Tk11!kF-fmCqs`rGFwX z@f>{-y^r($j2=hK`}45_h*$hu@g)I?|IIjv=+J>=3thUAyo<|p%xvLp`pj&M++i=P zN11rgi6>!=7E|040zxe-<>s{lYEjX1*v2dg8)aVmyLDKmVujp@U ze`qS0y+|igkHuV8r|(mo8fYvP7dxZU1fGhM8uCU{t3>0 zg~@F-;z=Dd<4Inl4Mt~VpC<+Ko;IYnqlvX+?dYhdcFdt*_)y!N3c&akauA1p0N0%* U5m5O6i8ip-ATzxMM|#is3p}}^YXATM diff --git a/src/web_interface/app.py b/src/web_interface/app.py index 5985d5d..bf9eb53 100644 --- a/src/web_interface/app.py +++ b/src/web_interface/app.py @@ -18,7 +18,7 @@ # I want to be able to easily call python backend code from the server. # Make sure the start code for the local server has an option to run in foreground or background. app = Flask(__name__) -app.secret_key = 'your_secret_key' # Replace with a secure secret key in production +app.secret_key = os.getenv("VON_APP_SECRET_KEY") # Replace with a secure secret key in production # Configure Flask-Login login_manager = LoginManager() diff --git a/src/web_interface/user_data.py b/src/web_interface/user_data.py index 0e10c0e..c86e476 100644 --- a/src/web_interface/user_data.py +++ b/src/web_interface/user_data.py @@ -1,14 +1,15 @@ +import os from bson.objectid import ObjectId from werkzeug.security import generate_password_hash, check_password_hash from vonlib import database_driver as vondb +# fyi in mongosh > show databases ; > use xxx ; > show collections ; Kill collection yyy > db.yyy.drop() ; kill db xxx > db.dropDatabase() + +config = {} #not entirely clear that this config +config["CONFIG_DB"] = os.getenv('VON_CONFIG_DB') +config["USER_COLLECTION"] = os.getenv('VON_USER_COLLECTION') -config = {} -config["USER_DB"] = "vonUsers" -config["USER_COLLECTION"] = "users" -vonuserdb = config["USER_DB"] -vonusercollection = config["USER_COLLECTION"] class VonUser: vomongo = vondb.DatabaseDriver() @@ -16,7 +17,7 @@ class VonUser: usercollection=None @classmethod - def set_userDB(cls, userdb_name=vonuserdb, usercollection_name=vonusercollection): + def set_userDB(cls, userdb_name=config["CONFIG_DB"], usercollection_name=config["USER_COLLECTION"]): """ Sets the user database collection for the class. @@ -130,7 +131,7 @@ def get_email(self): # } -VonUser.set_userDB(vonuserdb, vonusercollection) +VonUser.set_userDB(config["CONFIG_DB"], config["USER_COLLECTION"]) diff --git a/tests/test_web_interface/__pycache__/user_data_test.cpython-312-pytest-8.2.2.pyc b/tests/test_web_interface/__pycache__/user_data_test.cpython-312-pytest-8.2.2.pyc index 286f7d13ade8244b7d2ca9c581d89b4141b078c9..d6f0be52e12d7fa351daec120e84556f30649409 100644 GIT binary patch literal 7360 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 literal 5689 zcmeHL&2QXP5ckKk_amG1i!==%&C*g4QwV9Brj$0&k5mGI3KA-ZXeGWqAkpctd**ANfL!BnN!5zjXI~o z8_7;p({pKiQD9pIG?HBAMG$Kc9Y*85j|y(DPa%f9Mbjyrq7AHw!x-L|cg zTdvnu#0_a?MiZCcJ2Ku_n$n!?XJIEllgHk1J_Vg{YEpA1nh&V!O7F0`{T#gUFx17X%E^U+l8NXELX>^ z!ok&G%Ra#!``L4iCA5TYP=AkJGHhF?R)d!6)rLVWKL@Y%VzX?!<(lKGb{#kz|A6jX ztv79xvFZzUy=d4v6PM8)W8Oju{?K`LMzHNAO6%0$r_a}E)o^v|M#DC023p-8!p%fy z&{Tf_H3sH679X6552oXTlffX6{#LN%U~p{G!APBF*GBQ;+l4Ej&M90kyG2^~sAkR5 z@}gCkwH7VA-l$qNw{Yo(Ytfov7d|=v{)a}f0N7GM`#6m1*R7&nt^v!uQL+lSdEGQz zLr0d0#*)8_*)p;=Jq-FWvO7NkxhXtUa&2W~O&R$neeclm2g-?#a z>D<34XV&3BLNew`%4WiIU)n9NStFV5#g$p3Jb6c*Ob61)0H>gubTa z5x`QmCWfUXh7bt|%=H154#(u@xTdI$P>#kq6Yx3W1neX+sY$$DyY%7f#>qZ>GDkX7 z39(Z#RWs!&A$F?n9)6~nq$E^Vxg_3(*on9)!9&15LghIwEovbTO1Dygb1CLK$&;MB zFz6R^2ZNyvi0A41@d zCg!L8y)nqVG4#wEv9C7i7t+TYkZtU`?33lIMvY=I0`UI9s=lI}yL7*i;w_!PfpI)Vgq zb2^ITc_6;Rw(Lt~Q_Ij-ar#9hFCjUIWDH3j$;&|E$--1TS(tXvenENwlpaE6mKXxa zmxGLn9)n3_bN&PZ2tKg)tLZP_y*b+<7uj}^JsgvtP-I7N4~fAx3uTrV z^yZBcgWi1I!~g(402jglf9Hy2UMNR?ODL4Pep5X)DLrFJ!R9|lp3HBV9rIf^!RhO_ zE+_ac)5pjEIll!(ZyIocl|CGbUhfgEH&?O{tbNHWQutF#psyjp*zYR{KaNkZb_=Ua zia!rO%Om>i1&Xk_?V!GwFN}(EEk&nL1dHx(VvmLRN$lm3OapNcUBeh2MDP(-gkfD) zG#pEJT-q$TO={88z==5Nd=CUdIT>h^{cB|Z19ISDYN(wWT}zGLPrY#SLWktrJ9IzH z>~3ccuVoJ3&m2L}7;Ep)eUFT>sI2AuG_TMZZu+BoP}5HYWi-^;6V$p+%dX{Tx>J5C zoMR61)nJJ~vZ=yle{Zk~m%%lwW*HzdQ?3YX1I*|f>9f_k*|e>9=y@YSt5?D=M*u|*8c_T#o*8Y diff --git a/tests/test_web_interface/user_data_test.py b/tests/test_web_interface/user_data_test.py index 60ad7d5..147c670 100644 --- a/tests/test_web_interface/user_data_test.py +++ b/tests/test_web_interface/user_data_test.py @@ -1,23 +1,37 @@ import pytest from web_interface.user_data import VonUser +import os + +test_user={'username':'testuser', 'email':'test@testing.org', 'password':'testpassword'} def test_set_userDB(): VonUser.set_userDB('testDB', 'testCollection') assert VonUser.userdb.name == 'testDB' assert VonUser.usercollection.name == 'testCollection' +def test_create_user(): + VonUser.set_userDB('testDB', 'testCollection') + user = VonUser.create_user(test_user['username'], test_user['email'], test_user['password']) + def test_init_user(): - user = VonUser('zhan') - assert user.get_username() == 'zhan' - assert user.email == 'vonneumarkt@gmail.com' - assert user.id is not None + VonUser.set_userDB('testDB', 'testCollection') + user = VonUser.create_user(test_user['username'], test_user['email'], test_user['password']) + + user = VonUser(test_user['username']) + assert user.get_username() == test_user['username'] + assert user.email == test_user['email'] + assert user.password == test_user['password'] + assert user.id is not None def test_database_structure(): + dbname=os.getenv('VON_CONFIG_DB') + collname=os.getenv('VON_USER_COLLECTION') + VonUser.set_userDB() # use default values db=VonUser.get_userDB() - assert db.name == 'vonUsers' + assert db.name == dbname coll=VonUser.get_userCollection() - assert coll.name == 'users' + assert coll.name == collname # def test_update_email(mock_user_collection): # VonUser.usercollection = mock_user_collection