ivoryos 1.0.5__tar.gz → 1.0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ivoryos might be problematic. Click here for more details.

Files changed (68) hide show
  1. {ivoryos-1.0.5/ivoryos.egg-info → ivoryos-1.0.7}/PKG-INFO +1 -4
  2. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/__init__.py +4 -4
  3. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/config.py +15 -2
  4. ivoryos-1.0.7/ivoryos/version.py +1 -0
  5. {ivoryos-1.0.5 → ivoryos-1.0.7/ivoryos.egg-info}/PKG-INFO +1 -4
  6. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos.egg-info/SOURCES.txt +10 -1
  7. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos.egg-info/top_level.txt +1 -0
  8. ivoryos-1.0.7/tests/__init__.py +0 -0
  9. ivoryos-1.0.7/tests/conftest.py +133 -0
  10. ivoryos-1.0.7/tests/integration/__init__.py +0 -0
  11. ivoryos-1.0.7/tests/integration/test_route_auth.py +80 -0
  12. ivoryos-1.0.7/tests/integration/test_route_control.py +94 -0
  13. ivoryos-1.0.7/tests/integration/test_route_database.py +61 -0
  14. ivoryos-1.0.7/tests/integration/test_route_design.py +36 -0
  15. ivoryos-1.0.7/tests/integration/test_route_main.py +35 -0
  16. ivoryos-1.0.7/tests/integration/test_sockets.py +26 -0
  17. ivoryos-1.0.5/ivoryos/version.py +0 -1
  18. {ivoryos-1.0.5 → ivoryos-1.0.7}/LICENSE +0 -0
  19. {ivoryos-1.0.5 → ivoryos-1.0.7}/MANIFEST.in +0 -0
  20. {ivoryos-1.0.5 → ivoryos-1.0.7}/README.md +0 -0
  21. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/__init__.py +0 -0
  22. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/auth/__init__.py +0 -0
  23. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/auth/auth.py +0 -0
  24. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/auth/templates/auth/login.html +0 -0
  25. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/auth/templates/auth/signup.html +0 -0
  26. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/control/__init__.py +0 -0
  27. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/control/control.py +0 -0
  28. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/control/templates/control/controllers.html +0 -0
  29. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/control/templates/control/controllers_home.html +0 -0
  30. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/control/templates/control/controllers_new.html +0 -0
  31. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/database/__init__.py +0 -0
  32. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/database/database.py +0 -0
  33. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/database/templates/database/scripts_database.html +0 -0
  34. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/database/templates/database/step_card.html +0 -0
  35. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/database/templates/database/workflow_database.html +0 -0
  36. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/database/templates/database/workflow_view.html +0 -0
  37. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/design/__init__.py +0 -0
  38. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/design/design.py +0 -0
  39. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/design/templates/design/experiment_builder.html +0 -0
  40. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/design/templates/design/experiment_run.html +0 -0
  41. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/main/__init__.py +0 -0
  42. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/main/main.py +0 -0
  43. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/main/templates/main/help.html +0 -0
  44. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/routes/main/templates/main/home.html +0 -0
  45. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/favicon.ico +0 -0
  46. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/gui_annotation/Slide1.png +0 -0
  47. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
  48. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/js/overlay.js +0 -0
  49. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/js/socket_handler.js +0 -0
  50. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/js/sortable_card.js +0 -0
  51. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/js/sortable_design.js +0 -0
  52. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/logo.webp +0 -0
  53. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/static/style.css +0 -0
  54. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/templates/base.html +0 -0
  55. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/__init__.py +0 -0
  56. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/bo_campaign.py +0 -0
  57. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/client_proxy.py +0 -0
  58. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/db_models.py +0 -0
  59. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/form.py +0 -0
  60. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/global_config.py +0 -0
  61. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/llm_agent.py +0 -0
  62. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/script_runner.py +0 -0
  63. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/task_runner.py +0 -0
  64. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos/utils/utils.py +0 -0
  65. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos.egg-info/dependency_links.txt +0 -0
  66. {ivoryos-1.0.5 → ivoryos-1.0.7}/ivoryos.egg-info/requires.txt +0 -0
  67. {ivoryos-1.0.5 → ivoryos-1.0.7}/setup.cfg +0 -0
  68. {ivoryos-1.0.5 → ivoryos-1.0.7}/setup.py +0 -0
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
5
5
  Home-page: https://gitlab.com/heingroup/ivoryos
6
6
  Author: Ivory Zhang
7
7
  Author-email: ivoryzhang@chem.ubc.ca
8
8
  License: MIT
9
- Platform: UNKNOWN
10
9
  Description-Content-Type: text/markdown
11
10
  License-File: LICENSE
12
11
 
@@ -208,5 +207,3 @@ For an additional perspective related to the development of the tool, please see
208
207
  Ivory Zhang, Lucy Hao
209
208
 
210
209
  Authors acknowledge all former and current Hein Lab members for their valuable suggestions.
211
-
212
-
@@ -55,9 +55,9 @@ def create_app(config_class=None):
55
55
  create app, init database
56
56
  """
57
57
  app.config.from_object(config_class or 'config.get_config()')
58
-
58
+ os.makedirs(app.config["OUTPUT_FOLDER"], exist_ok=True)
59
59
  # Initialize extensions
60
- socketio.init_app(app)
60
+ socketio.init_app(app, cors_allowed_origins="*", cookie=None, logger=True, engineio_logger=True)
61
61
  login_manager.init_app(app)
62
62
  login_manager.login_view = "auth.login"
63
63
  db.init_app(app)
@@ -114,7 +114,6 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
114
114
  :param exclude_names: list[str] module names to exclude from parsing
115
115
  """
116
116
  app = create_app(config_class=config or get_config()) # Create app instance using factory function
117
- os.makedirs(app.config["OUTPUT_FOLDER"], exist_ok=True)
118
117
 
119
118
  # plugins = load_installed_plugins(app, socketio)
120
119
  plugins = []
@@ -136,13 +135,14 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
136
135
  app.config["LOGGERS"] = logger
137
136
  app.config["LOGGERS_PATH"] = logger_output_name or app.config["LOGGERS_PATH"] # default.log
138
137
  logger_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["LOGGERS_PATH"])
138
+ dummy_deck_path = os.path.join(app.config["OUTPUT_FOLDER"], app.config["DUMMY_DECK"])
139
139
 
140
140
  if module:
141
141
  app.config["MODULE"] = module
142
142
  app.config["OFF_LINE"] = False
143
143
  global_config.deck = sys.modules[module]
144
144
  global_config.deck_snapshot = utils.create_deck_snapshot(global_config.deck,
145
- output_path=app.config["DUMMY_DECK"],
145
+ output_path=dummy_deck_path,
146
146
  save=True,
147
147
  exclude_names=exclude_names
148
148
  )
@@ -36,12 +36,25 @@ class ProductionConfig(Config):
36
36
  class TestingConfig(Config):
37
37
  DEBUG = True
38
38
  TESTING = True
39
+ SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' # Use an in-memory SQLite database for tests
40
+ WTF_CSRF_ENABLED = False # Disable CSRF for testing forms
41
+
42
+
39
43
 
40
44
  class DemoConfig(Config):
41
45
  DEBUG = False
42
46
  SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
43
- OUTPUT_FOLDER = "/tmp/ivoryos_data"
44
-
47
+ OUTPUT_FOLDER = os.path.join(os.path.abspath(os.curdir), '/tmp/ivoryos_data')
48
+ CSV_FOLDER = os.path.join(OUTPUT_FOLDER, 'config_csv/')
49
+ SCRIPT_FOLDER = os.path.join(OUTPUT_FOLDER, 'scripts/')
50
+ DATA_FOLDER = os.path.join(OUTPUT_FOLDER, 'results/')
51
+ DUMMY_DECK = os.path.join(OUTPUT_FOLDER, 'pseudo_deck/')
52
+ LLM_OUTPUT = os.path.join(OUTPUT_FOLDER, 'llm_output/')
53
+ DECK_HISTORY = os.path.join(OUTPUT_FOLDER, 'deck_history.txt')
54
+ # session and cookies
55
+ SESSION_COOKIE_SECURE = True
56
+ SESSION_COOKIE_SAMESITE = "None"
57
+ SESSION_COOKIE_HTTPONLY = True
45
58
 
46
59
  def get_config(env='dev'):
47
60
  if env == 'production':
@@ -0,0 +1 @@
1
+ __version__ = "1.0.7"
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
5
5
  Home-page: https://gitlab.com/heingroup/ivoryos
6
6
  Author: Ivory Zhang
7
7
  Author-email: ivoryzhang@chem.ubc.ca
8
8
  License: MIT
9
- Platform: UNKNOWN
10
9
  Description-Content-Type: text/markdown
11
10
  License-File: LICENSE
12
11
 
@@ -208,5 +207,3 @@ For an additional perspective related to the development of the tool, please see
208
207
  Ivory Zhang, Lucy Hao
209
208
 
210
209
  Authors acknowledge all former and current Hein Lab members for their valuable suggestions.
211
-
212
-
@@ -53,4 +53,13 @@ ivoryos/utils/global_config.py
53
53
  ivoryos/utils/llm_agent.py
54
54
  ivoryos/utils/script_runner.py
55
55
  ivoryos/utils/task_runner.py
56
- ivoryos/utils/utils.py
56
+ ivoryos/utils/utils.py
57
+ tests/__init__.py
58
+ tests/conftest.py
59
+ tests/integration/__init__.py
60
+ tests/integration/test_route_auth.py
61
+ tests/integration/test_route_control.py
62
+ tests/integration/test_route_database.py
63
+ tests/integration/test_route_design.py
64
+ tests/integration/test_route_main.py
65
+ tests/integration/test_sockets.py
@@ -1 +1,2 @@
1
1
  ivoryos
2
+ tests
File without changes
@@ -0,0 +1,133 @@
1
+ from enum import Enum
2
+
3
+ import bcrypt
4
+ import pytest
5
+ from ivoryos.config import get_config
6
+
7
+ from ivoryos import create_app, socketio, db as _db, utils, global_config
8
+ from ivoryos.utils.db_models import User
9
+
10
+
11
+ @pytest.fixture(scope='session')
12
+ def app():
13
+ """Create a new app instance for the test session."""
14
+ _app = create_app(get_config('testing'))
15
+ return _app
16
+
17
+ @pytest.fixture
18
+ def client(app):
19
+ """A test client for the app."""
20
+ with app.test_client() as client:
21
+ with app.app_context():
22
+ _db.create_all()
23
+ yield client
24
+ with app.app_context():
25
+ _db.drop_all()
26
+
27
+ # @pytest.fixture(scope='session')
28
+ # def db(app):
29
+ # """Session-wide test database."""
30
+ # with app.app_context():
31
+ # _db.create_all()
32
+ # yield _db
33
+ # _db.drop_all()
34
+
35
+ @pytest.fixture(scope='module')
36
+ def init_database(app):
37
+ """
38
+ Creates the database tables and seeds it with a default test user.
39
+ This runs once per test module.
40
+ """
41
+ with app.app_context():
42
+ # Drop everything first to ensure a clean slate
43
+ _db.drop_all()
44
+ # Create the database tables
45
+ _db.create_all()
46
+
47
+ # Insert a default user for authentication tests
48
+ # Note: In a real app with password hashing, you'd call a hash function here.
49
+ password = 'password'
50
+ bcrypt_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
51
+ default_user = User(username='testuser', password=bcrypt_password)
52
+ _db.session.add(default_user)
53
+ _db.session.commit()
54
+
55
+ yield _db # this is where the testing happens!
56
+
57
+ # Teardown: drop all tables after the tests in the module are done
58
+ _db.drop_all()
59
+
60
+
61
+ # ---------------------
62
+ # Authentication Fixture
63
+ # ---------------------
64
+
65
+ @pytest.fixture(scope='function')
66
+ def auth(client, init_database):
67
+ """
68
+ Logs in the default user for a single test function.
69
+ Depends on `init_database` to ensure the user exists.
70
+ Handles logout as part of teardown.
71
+ """
72
+ # Log in the default user
73
+ client.post('/ivoryos/auth/login', data={
74
+ 'username': 'testuser',
75
+ 'password': 'password'
76
+ }, follow_redirects=True)
77
+
78
+ yield client # this is where the testing happens!
79
+
80
+ # Log out the user after the test is done
81
+ client.get('/ivoryos/auth/logout', follow_redirects=True)
82
+
83
+
84
+ @pytest.fixture
85
+ def socketio_client(app):
86
+ """A test client for Socket.IO."""
87
+ return socketio.test_client(app)
88
+
89
+
90
+ class TestEnum(Enum):
91
+ """An example Enum for testing type conversion."""
92
+ OPTION_A = 'A'
93
+ OPTION_B = 'B'
94
+
95
+ class DummyModule:
96
+ """A more comprehensive dummy instrument for testing."""
97
+ def int_method(self, arg: int = 1):
98
+ return arg
99
+
100
+ def float_method(self, arg: float = 1.0):
101
+ return arg
102
+
103
+ def bool_method(self, arg: bool = False):
104
+ return arg
105
+
106
+ def list_method(self, arg: list = None):
107
+ return arg or []
108
+
109
+ def enum_method(self, arg: TestEnum = TestEnum.OPTION_A):
110
+ return arg
111
+
112
+ def str_method(self) -> dict:
113
+ return {'status': 'OK'}
114
+
115
+
116
+ @pytest.fixture
117
+ def test_deck(app):
118
+ """
119
+ A fixture that creates and loads a predictable 'deck' of dummy instruments
120
+ for testing the dynamic control routes.
121
+ """
122
+ dummy_module = DummyModule()
123
+ snapshot = utils.create_deck_snapshot(dummy_module)
124
+
125
+ with app.app_context():
126
+ global_config.deck_snapshot = snapshot
127
+ global_config.deck = dummy_module # instantiate the class
128
+
129
+ yield DummyModule
130
+
131
+ with app.app_context():
132
+ global_config.deck_snapshot = {}
133
+ global_config.deck = {}
File without changes
@@ -0,0 +1,80 @@
1
+ from ivoryos.utils.db_models import User, db
2
+
3
+
4
+ def test_get_signup(client):
5
+ """
6
+ GIVEN a client
7
+ WHEN a GET request is made to /ivoryos/auth/signup
8
+ THEN check that signup page loads with 200 status and contains "Signup" text
9
+ """
10
+ response = client.get("/ivoryos/auth/signup", follow_redirects=True)
11
+ assert response.status_code == 200
12
+ assert b"Signup" in response.data
13
+
14
+
15
+ def test_route_auth_signup(client):
16
+ """
17
+ GIVEN a client
18
+ WHEN a POST request is made to /ivoryos/auth/signup with valid credentials
19
+ THEN check that signup succeeds with 200 status and the user is created in database
20
+ """
21
+ response = client.post("/ivoryos/auth/signup",
22
+ data={
23
+ "username": "second_testuser",
24
+ "password": "password"
25
+ },
26
+ follow_redirects=True
27
+ )
28
+ assert response.status_code == 200
29
+ assert b"Login" in response.data
30
+
31
+ # Verify user was created
32
+ with client.application.app_context():
33
+ user = db.session.query(User).filter(User.username == 'second_testuser').first()
34
+ assert user is not None
35
+
36
+
37
+ def test_duplicate_user_signup(client, init_database):
38
+ """
39
+ GIVEN a client and init_database fixture
40
+ WHEN a POST request is made to signup with an existing username
41
+ THEN check that signup fails with 409 status and appropriate error message
42
+ """
43
+ client.post('/ivoryos/auth/signup', data={
44
+ 'username': 'existinguser',
45
+ 'password': 'anotherpass'
46
+ })
47
+ # Try to create duplicate
48
+ response = client.post('/ivoryos/auth/signup', data={
49
+ 'username': 'existinguser',
50
+ 'password': 'anotherpass'
51
+ })
52
+ assert response.status_code == 409
53
+ assert b"Signup" in response.data
54
+ assert b"User already exists" in response.data
55
+
56
+ # Verify user was created
57
+ users = db.session.query(User).filter(User.username == 'existinguser').all()
58
+ assert len(users) == 1
59
+
60
+
61
+ def test_failed_login(client):
62
+ """
63
+ GIVEN a client and invalid login credentials
64
+ WHEN a POST request is made to /ivoryos/auth/login
65
+ THEN check that login fails with 401 status and the appropriate error message
66
+ """
67
+ response = client.post('/ivoryos/auth/login', data={
68
+ 'username': 'nonexistent',
69
+ 'password': 'wrongpass'
70
+ })
71
+ assert response.status_code == 401
72
+
73
+ def test_logout(auth):
74
+ """
75
+ GIVEN an authenticated client
76
+ WHEN a GET request is made to /ivoryos/auth/logout
77
+ THEN check that logout succeeds with 302 status and redirects to login
78
+ """
79
+ response = auth.get('/ivoryos/auth/logout')
80
+ assert response.status_code == 302 # Redirect to login
@@ -0,0 +1,94 @@
1
+ from unittest.mock import patch, Mock
2
+
3
+ from ivoryos.utils.db_models import Script
4
+ from ivoryos import db
5
+
6
+ def test_control_panel_redirects_anonymous(client):
7
+ """
8
+ GIVEN an anonymous user
9
+ WHEN the control panel is accessed
10
+ THEN they should be redirected to the login page
11
+ """
12
+ response = client.get('/ivoryos/control/home/deck', follow_redirects=True)
13
+ assert response.status_code == 200
14
+ assert b'Login' in response.data
15
+
16
+ def test_deck_control_for_auth_user(auth):
17
+ """
18
+ GIVEN an authenticated user
19
+ WHEN the control panel is accessed
20
+ THEN the page should load successfully
21
+ """
22
+ response = auth.get('/ivoryos/control/home/deck', follow_redirects=True)
23
+ assert response.status_code == 200
24
+ assert b'<title>IvoryOS | Devices</title>' in response.data # Assuming this text exists on the page
25
+
26
+ def test_temp_control_for_auth_user(auth):
27
+ """
28
+ GIVEN an authenticated user
29
+ WHEN the control panel is accessed
30
+ THEN the page should load successfully
31
+ """
32
+ response = auth.get('/ivoryos/control/home/temp', follow_redirects=True)
33
+ assert response.status_code == 200
34
+ # assert b'<title>IvoryOS | Devices</title>' in response.data # Assuming this text exists on the page
35
+
36
+ def test_new_controller_page(auth):
37
+ """Test new controller page loads"""
38
+ response = auth.get('/ivoryos/control/new/')
39
+ assert response.status_code == 200
40
+
41
+ def test_download_proxy(self, auth_headers):
42
+ """Test proxy download functionality"""
43
+ with patch('ivoryos.routes.control.control.global_config') as mock_config:
44
+ mock_config.deck_snapshot = {'test_instrument': {'test_method': {'signature': 'test()'}}}
45
+ response = auth_headers.get('/ivoryos/control/download')
46
+ assert response.status_code == 200
47
+ assert response.headers['Content-Disposition'].startswith('attachment')
48
+
49
+ def test_backend_control_get(self, auth_headers):
50
+ """Test backend control GET endpoint"""
51
+ with patch('ivoryos.routes.control.control.global_config') as mock_config:
52
+ mock_config.deck_snapshot = {'test_instrument': {'test_method': {'signature': 'test()'}}}
53
+ response = auth_headers.get('/ivoryos/api/control/')
54
+ assert response.status_code == 200
55
+ assert response.is_json
56
+
57
+ @patch('ivoryos.routes.control.control.runner')
58
+ @patch('ivoryos.routes.control.control.find_instrument_by_name')
59
+ @patch('ivoryos.routes.control.control.create_form_from_module')
60
+ def test_backend_control_post(self, mock_form, mock_find, mock_runner, auth_headers):
61
+ """Test backend control POST endpoint"""
62
+ # Setup mocks
63
+ mock_instrument = Mock()
64
+ mock_find.return_value = mock_instrument
65
+ mock_field = Mock()
66
+ mock_field.name = 'test_param'
67
+ mock_field.data = 'test_value'
68
+ mock_form_instance = Mock()
69
+ mock_form_instance.__iter__ = Mock(return_value=iter([mock_field]))
70
+ mock_form.return_value = {'test_method': mock_form_instance}
71
+ mock_runner.run_single_step.return_value = 'success'
72
+ response = auth_headers.post('/ivoryos/api/control/test_instrument', data={
73
+ 'hidden_name': 'test_method',
74
+ 'hidden_wait': 'true'
75
+ })
76
+ assert response.status_code == 200
77
+
78
+ # def test_control(auth, app):
79
+ # """
80
+ # GIVEN an authenticated user and an existing script
81
+ # WHEN a POST request is made to run the script
82
+ # THEN the user should be redirected and a success message shown
83
+ # """
84
+ # # We need to create a script in the database first
85
+ # with app.app_context():
86
+ # script = Script(name='My Test Script', author='testuser', content='print("hello")')
87
+ # db.session.add(script)
88
+ # db.session.commit()
89
+ # script_id = script.id
90
+ #
91
+ # # Simulate running the script
92
+ # response = auth.post(f'/ivoryos/control/run/{script_id}', follow_redirects=True)
93
+ # assert response.status_code == 200
94
+ # assert b'has been initiated' in response.data # Check for a flash message
@@ -0,0 +1,61 @@
1
+ from datetime import datetime
2
+
3
+ from ivoryos.utils.db_models import Script, WorkflowRun, WorkflowStep
4
+ from ivoryos import db
5
+
6
+ def test_database_scripts_page(auth):
7
+ """
8
+ GIVEN an authenticated user
9
+ WHEN they access the script database page
10
+ THEN the page should load and show their scripts
11
+ """
12
+ # First, create a script so the page has something to render
13
+ with auth.application.app_context():
14
+ script = Script(name='test_script', author='testuser')
15
+ db.session.add(script)
16
+ db.session.commit()
17
+
18
+ response = auth.get('/ivoryos/database/scripts/', follow_redirects=True)
19
+ assert response.status_code == 200
20
+ # assert b'Scripts Database' in response.data
21
+ assert b'<title>IvoryOS | Design Database</title>' in response.data
22
+
23
+ def test_database_workflows_page(auth):
24
+ """
25
+ GIVEN an authenticated user
26
+ WHEN they access the workflow database page
27
+ THEN the page should load and show past workflow runs
28
+ """
29
+ # Create a workflow run to display
30
+ with auth.application.app_context():
31
+ run = WorkflowRun(name="untitled", platform="deck",start_time=datetime.now())
32
+ db.session.add(run)
33
+ db.session.commit()
34
+ run_id = run.id
35
+
36
+ response = auth.get('/ivoryos/database/workflows/', follow_redirects=True)
37
+ assert response.status_code == 200
38
+ assert b'Workflow ID' in response.data
39
+ # assert b'run_id' in response.data
40
+
41
+ def test_view_specific_workflow(auth):
42
+ """
43
+ GIVEN an authenticated user and an existing workflow run
44
+ WHEN they access the specific URL for that workflow
45
+ THEN the detailed view for that run should be displayed
46
+ """
47
+ with auth.application.app_context():
48
+ run = WorkflowRun(name='test_workflow', platform='test_platform', start_time=datetime.now())
49
+ db.session.add(run)
50
+ db.session.commit()
51
+ run_id = run.id
52
+
53
+ step = WorkflowStep(method_name='test_step', workflow_id=run_id, phase="main", run_error=False, start_time=datetime.now())
54
+ db.session.add(step)
55
+ db.session.commit()
56
+ # run_id = run.id
57
+
58
+ response = auth.get(f'/ivoryos/database/workflows/{run_id}', follow_redirects=True)
59
+ assert response.status_code == 200
60
+ # assert b'test_step' in response.data # Check for a title on the view page
61
+ assert b'test_workflow' in response.data
@@ -0,0 +1,36 @@
1
+ def test_design_page_loads_for_auth_user(auth):
2
+ """
3
+ GIVEN an authenticated user
4
+ WHEN the design page is accessed
5
+ THEN the page should load successfully
6
+ """
7
+ response = auth.get('/ivoryos/design/script/', follow_redirects=True)
8
+ assert response.status_code == 200
9
+ assert b'<title>IvoryOS | Design</title>' in response.data # Assuming this text exists
10
+
11
+
12
+ def test_clear_canvas(auth):
13
+ """
14
+ Tests clearing the design canvas.
15
+ """
16
+ response = auth.get('/ivoryos/design/clear', follow_redirects=True)
17
+ assert response.status_code == 200
18
+ # assert b'Operations' in response.data
19
+
20
+ # def test_add_action(auth, test_deck):
21
+ # """
22
+ # Tests adding an action to the design canvas.
23
+ # """
24
+ # response = auth.post('/ivoryos/design/script/deck.dummy/', data={
25
+ # 'hidden_name': 'int_method',
26
+ # 'arg': '10'
27
+ # }, follow_redirects=True)
28
+ # assert response.status_code == 200
29
+
30
+ def test_experiment_run_page(auth):
31
+ """
32
+ Tests the experiment run page.
33
+ """
34
+ response = auth.get('/ivoryos/design/campaign')
35
+ assert response.status_code == 200
36
+ assert b'Run Panel' in response.data
@@ -0,0 +1,35 @@
1
+ from flask_login import current_user
2
+
3
+
4
+ def test_home_page_authenticated(auth, app):
5
+ """
6
+ GIVEN an authenticated user (using the 'auth' fixture)
7
+ WHEN the home page is accessed
8
+ THEN check that they see the main application page
9
+ """
10
+ with auth.application.test_request_context('/ivoryos/'):
11
+ # Manually trigger the before_request functions that Flask-Login uses
12
+ app.preprocess_request()
13
+
14
+ # Assert that the `current_user` proxy is now populated and authenticated
15
+ assert current_user.is_authenticated
16
+ assert current_user.username == 'testuser'
17
+
18
+ def test_help_page(client):
19
+ """
20
+ GIVEN an unauthenticated user
21
+ WHEN they access the help page
22
+ THEN check that the page loads successfully and contains documentation content
23
+ """
24
+ response = client.get('/ivoryos/help')
25
+ assert response.status_code == 200
26
+ assert b'Documentations' in response.data
27
+
28
+ def test_prefix_redirect(auth):
29
+ """
30
+ GIVEN an authenticated user (using the 'auth' fixture)
31
+ WHEN the home page is accessed without prefix
32
+ THEN check that they see the main application page
33
+ """
34
+ response = auth.get('/', follow_redirects=True)
35
+ assert response.status_code == 200
@@ -0,0 +1,26 @@
1
+ def test_socket_connection(socketio_client):
2
+ """
3
+ Test that a client can successfully connect to the Socket.IO server.
4
+ """
5
+ assert socketio_client.is_connected()
6
+ socketio_client.disconnect()
7
+ assert not socketio_client.is_connected()
8
+
9
+
10
+ # def test_logger_socket_event(socketio_client):
11
+ # """
12
+ # Test the custom logging event handler.
13
+ # (This assumes you have a handler like `@socketio.on('start_log')`)
14
+ # """
15
+ # # Connect the client
16
+ # socketio_client.connect()
17
+ #
18
+ # # Emit an event from the client to the server
19
+ # socketio_client.emit('start_log', {'logger_name': 'my_test_logger'})
20
+ #
21
+ # # Check what the server sent back to the client
22
+ # received = socketio_client.get_received()
23
+ #
24
+ # assert len(received) > 0
25
+ # assert received[0]['name'] == 'log_message' # Check for the event name
26
+ # assert 'Logger my_test_logger started' in received[0]['args'][0]['data']
@@ -1 +0,0 @@
1
- __version__ = "1.0.5"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes