Skip to content

Commit df61ab7

Browse files
authored
Merge pull request ArgLab#301 from ArgLab/berickson/20260423-test-fix
Resolves all LO test failures
2 parents 43c7564 + 816efc9 commit df61ab7

13 files changed

Lines changed: 90 additions & 67 deletions

File tree

‎VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0+2026.04.23T14.17.45.604Z.c8e75908.berickson.20260423.documentation
1+
0.1.0+2026.04.23T20.03.53.409Z.887fd5d9.berickson.20260423.test.fix

‎learning_observer/VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0+2026.04.23T13.51.16.444Z.f82c7ba3.berickson.20260423.documentation
1+
0.1.0+2026.04.23T20.03.53.409Z.887fd5d9.berickson.20260423.test.fix

‎learning_observer/learning_observer/adapters/helpers.py‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,13 @@ def rename_json_keys(source, replacements):
1818
>>> source = {
1919
... "event-type": "blog",
2020
... "writing-log": "foobar",
21-
}
21+
... }
2222
>>> replacements = {
2323
... "event-type": "event_type",
2424
... "writing-log": "writing_log",
2525
... }
2626
>>> rename_json_keys(source, replacements)
27-
{
28-
"event_type": "blog",
29-
"writing_log": "foobar",
30-
}
27+
{'event_type': 'blog', 'writing_log': 'foobar'}
3128
'''
3229
if isinstance(source, dict):
3330
for key, value in list(source.items()):

‎learning_observer/learning_observer/auth/events.py‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
AUTH_METHODS = {}
4141

4242

43+
class TestRequest:
44+
"""Simple request stub for doctests."""
45+
pass
46+
47+
4348
def register_event_auth(name):
4449
'''
4550
Decorator to register a method to authenticate events
@@ -135,7 +140,9 @@ async def guest_auth(request, event, source):
135140
We assign a cookie on first visit, but we have no guarantee
136141
the browser will keep cookies around.
137142
138-
>>> a = asyncio.run(guest_auth(TestRequest(), [], {}, 'org.mitros.test'))
143+
>>> from unittest.mock import AsyncMock, patch
144+
>>> with patch('aiohttp_session.get_session', new=AsyncMock(return_value={})):
145+
... a = asyncio.run(guest_auth(TestRequest(), {}, 'org.mitros.test'))
139146
>>> a['user_id'] = len(a['user_id']) # Different user_id each time, and we want doctest to match exact string.
140147
>>> a
141148
{'sec': 'none', 'user_id': 32, 'providence': 'guest'}
@@ -163,12 +170,16 @@ async def local_storage_auth(request, event, source):
163170
unauthenticated (if we don't), or allow for both, with a tag for
164171
guest versus non-guest accounts.
165172
173+
>>> from unittest.mock import patch
166174
>>> auth_event = {'event': 'local_storage', 'user_tag': 'bob'}
167-
>>> a = asyncio.run(local_storage_auth(TestRequest(), [], auth_event, 'org.mitros.test'))
175+
>>> with patch('learning_observer.auth.events.token_authorize_user', return_value='authenticated'):
176+
... a = asyncio.run(local_storage_auth(TestRequest(), auth_event, 'org.mitros.test'))
168177
>>> a
169178
{'sec': 'authenticated', 'user_id': 'ls-bob', 'providence': 'ls'}
170179
>>> auth_event['user_tag'] = 'jim'
171-
>>> a = asyncio.run(local_storage_auth(TestRequest(), [auth_event], {}, 'org.mitros.test'))
180+
>>> with patch('learning_observer.auth.events.token_authorize_user', return_value='unauthenticated'):
181+
... a = asyncio.run(local_storage_auth(TestRequest(), auth_event, 'org.mitros.test'))
182+
172183
>>> a
173184
{'sec': 'unauthenticated', 'user_id': 'ls-jim', 'providence': 'ls'}
174185
'''
@@ -344,9 +355,6 @@ def check_event_auth_config():
344355
import doctest
345356
print("Running tests")
346357

347-
class TestRequest:
348-
pass
349-
350358
session = {}
351359

352360
async def get_session(request):

‎learning_observer/learning_observer/communication_protocol/executor.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import learning_observer.stream_analytics.fields
2020
import learning_observer.stream_analytics.helpers
2121
from learning_observer.log_event import debug_log
22-
from learning_observer.util import get_nested_dict_value, clean_json, ensure_async_generator, async_zip
22+
from learning_observer.util import get_nested_dict_value, clean_json, ensure_async_generator, async_zip, async_generator_to_list
2323
from learning_observer.communication_protocol.exception import DAGExecutionException
2424

2525

@@ -1133,7 +1133,5 @@ async def visit(node_name):
11331133

11341134
if __name__ == "__main__":
11351135
import doctest
1136-
# This function is used by doctests
1137-
from learning_observer.util import async_generator_to_list
11381136

11391137
doctest.testmod(optionflags=doctest.ELLIPSIS)

‎learning_observer/learning_observer/doc_processor.py‎

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import learning_observer.auth.utils
1919
import learning_observer.constants
20-
import learning_observer.google
20+
import learning_observer.integrations.google as google_integration
2121
import learning_observer.kvs
2222
import learning_observer.offline
2323
import learning_observer.run
@@ -26,13 +26,25 @@
2626
import learning_observer.stream_analytics.helpers as sa_helpers
2727
import learning_observer.util
2828

29-
import writing_observer
30-
import writing_observer.awe_nlp
31-
import writing_observer.languagetool
32-
import writing_observer.writing_analysis
29+
try:
30+
import writing_observer
31+
import writing_observer.awe_nlp
32+
import writing_observer.languagetool
33+
import writing_observer.writing_analysis
34+
except ModuleNotFoundError:
35+
writing_observer = None
3336

3437
from learning_observer.log_event import debug_log
3538

39+
40+
def _require_writing_observer():
41+
if writing_observer is None:
42+
raise RuntimeError(
43+
"writing_observer is required for document processing, "
44+
"but is not installed in this environment."
45+
)
46+
47+
3648
pmss.register_field(
3749
name='document_processing_delay_seconds',
3850
type=pmss.pmsstypes.TYPES.integer,
@@ -93,6 +105,7 @@ async def check_recent_mod_and_not_recent_process(doc_id):
93105
processing and check whether it is past a specified cutoff
94106
time (5 minutes).
95107
'''
108+
_require_writing_observer()
96109
cutoff = learning_observer.settings.pmss_settings.document_processing_delay_seconds(types=['modules', 'writing_observer'])
97110
student_id = await _determine_student(doc_id)
98111

@@ -161,8 +174,9 @@ def fetch_mock_runtime(creds):
161174
async def start():
162175
learning_observer.offline.init('creds.yaml')
163176
global app, KVS
177+
_require_writing_observer()
164178
app = StubApp(asyncio.get_event_loop())
165-
learning_observer.google.initialize_and_register_routes(app)
179+
google_integration.initialize_and_register_routes(app)
166180
KVS = learning_observer.kvs.KVS()
167181

168182
# overwrite aiohttp_session.get_session so the Google API

‎learning_observer/learning_observer/integrations/google.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,11 @@ def _force_text_length(text, length):
161161
'''
162162
Force text to a given length, either concatenating or padding
163163
164-
>>> force_text_length("Hello", 3)
165-
>>> 'Hel'
164+
>>> _force_text_length("Hello", 3)
165+
'Hel'
166166
167-
>>> force_text_length("Hello", 13)
168-
>>> 'Hello '
167+
>>> _force_text_length("Hello", 13)
168+
'Hello '
169169
'''
170170
return text[:length] + " " * (length - len(text))
171171

‎learning_observer/learning_observer/integrations/util.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def extract_parameters_from_format_string(format_string):
6464
'''
6565
Extracts parameters from a format string. E.g.
6666
67-
>>> ("hello {hi} my {bye}")]
67+
>>> extract_parameters_from_format_string("hello {hi} my {bye}")
6868
['hi', 'bye']
6969
'''
7070
# The parse returns a lot of context, which we discard. In particular, the

‎learning_observer/learning_observer/merkle_store.py‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,13 @@
6868
import hashlib
6969
import json
7070
import datetime
71-
from modulefinder import STORE_GLOBAL
7271
import os
73-
from pickle import STOP
7472

75-
# These should be abstracted out into a visualization library.
76-
import matplotlib
77-
import networkx
78-
from learning_observer.incoming_student_event import COUNT
79-
import pydot
80-
81-
from confluent_kafka import Producer, Consumer
73+
try:
74+
from confluent_kafka import Producer, Consumer
75+
except:
76+
Producer = None
77+
Consumer = None
8278

8379

8480
def json_dump(obj):
@@ -410,6 +406,7 @@ def to_networkx(self):
410406
This is used for testing, experimentation, and demonstration. It
411407
would never scale with real data.
412408
'''
409+
import networkx
413410
G = networkx.DiGraph()
414411
for item in self._walk():
415412
print(item)
@@ -426,6 +423,7 @@ def to_graphviz(self):
426423
This is used for testing, experimentation, and demonstration. It
427424
would never scale with real data.
428425
'''
426+
import pydot
429427
G = pydot.Dot(graph_type='digraph')
430428
for item in self._walk():
431429
node = pydot.Node(item['hash'], label=self._make_label(item))

‎learning_observer/learning_observer/pubsub/__init__.py‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@
1818
TODO this module is no longer being used by the LO system.
1919
This should be removed.
2020
'''
21-
import sys
22-
2321
import learning_observer.settings as settings
2422
from learning_observer.log_event import debug_log
2523

2624
try:
2725
PUBSUB = settings.settings['pubsub']['type']
28-
except KeyError:
29-
print("Pub-sub configuration missing from configuration file.")
30-
sys.exit(-1)
26+
except (TypeError, KeyError):
27+
debug_log("Pub-sub configuration missing from configuration file; defaulting to stub.")
28+
PUBSUB = 'stub'
3129

3230
if PUBSUB == 'xmpp':
3331
import learning_observer.pubsub.receivexmpp

0 commit comments

Comments
 (0)