Phase 4b: Unit Testing

This commit is contained in:
Jay Lee
2026-07-04 07:52:53 -04:00
parent d72dec3caf
commit 69d886af4e
6 changed files with 412 additions and 0 deletions

181
tests/test_args.py Normal file
View File

@@ -0,0 +1,181 @@
"""Unit tests for gam.util.args — argument parsing and formatting functions."""
import pytest
import arrow
class TestCourseScopes:
"""Test course ID and alias scope manipulation."""
def test_add_course_id_scope_plain(self):
from gam.util.args import addCourseIdScope
assert addCourseIdScope('12345') == '12345' # numeric, no prefix
def test_add_course_id_scope_name(self):
from gam.util.args import addCourseIdScope
assert addCourseIdScope('my-course') == 'd:my-course'
def test_add_course_id_scope_already_prefixed(self):
from gam.util.args import addCourseIdScope
assert addCourseIdScope('d:my-course') == 'd:my-course'
assert addCourseIdScope('p:my-course') == 'p:my-course'
def test_remove_course_id_scope(self):
from gam.util.args import removeCourseIdScope
assert removeCourseIdScope('d:my-course') == 'my-course'
def test_remove_course_id_scope_no_prefix(self):
from gam.util.args import removeCourseIdScope
assert removeCourseIdScope('12345') == '12345'
def test_add_course_alias_scope(self):
from gam.util.args import addCourseAliasScope
assert addCourseAliasScope('my-alias') == 'd:my-alias'
def test_add_course_alias_scope_already_prefixed(self):
from gam.util.args import addCourseAliasScope
assert addCourseAliasScope('d:my-alias') == 'd:my-alias'
assert addCourseAliasScope('p:my-alias') == 'p:my-alias'
def test_remove_course_alias_scope(self):
from gam.util.args import removeCourseAliasScope
assert removeCourseAliasScope('d:my-alias') == 'my-alias'
def test_remove_course_alias_scope_no_prefix(self):
from gam.util.args import removeCourseAliasScope
assert removeCourseAliasScope('my-alias') == 'my-alias'
class TestEmailAddressParsing:
"""Test email address parsing and normalization."""
def test_get_email_domain(self):
from gam.util.args import getEmailAddressDomain
assert getEmailAddressDomain('user@domain.com') == 'domain.com'
def test_get_email_domain_no_at(self):
from gam.util.args import getEmailAddressDomain
# Falls back to GC.Values[GC.DOMAIN] which is 'example.com' in conftest
assert getEmailAddressDomain('justuser') == 'example.com'
def test_get_email_domain_case(self):
from gam.util.args import getEmailAddressDomain
assert getEmailAddressDomain('user@DOMAIN.COM') == 'domain.com'
def test_get_email_username(self):
from gam.util.args import getEmailAddressUsername
assert getEmailAddressUsername('user@domain.com') == 'user'
def test_get_email_username_no_at(self):
from gam.util.args import getEmailAddressUsername
assert getEmailAddressUsername('justuser') == 'justuser'
def test_split_email(self):
from gam.util.args import splitEmailAddress
assert splitEmailAddress('User@Domain.COM') == ('user', 'domain.com')
def test_split_email_no_at(self):
from gam.util.args import splitEmailAddress
assert splitEmailAddress('JustUser') == ('justuser', 'example.com')
def test_normalize_uid(self):
from gam.util.args import normalizeEmailAddressOrUID
# uid: prefix should be stripped
assert normalizeEmailAddressOrUID('uid:12345') == '12345'
def test_normalize_email_lowercased(self):
from gam.util.args import normalizeEmailAddressOrUID
result = normalizeEmailAddressOrUID('User@Domain.COM')
assert result == 'user@domain.com'
def test_normalize_email_no_lower(self):
from gam.util.args import normalizeEmailAddressOrUID
result = normalizeEmailAddressOrUID('User@Domain.COM', noLower=True)
assert result == 'User@Domain.COM'
def test_normalize_bare_username_gets_domain(self):
from gam.util.args import normalizeEmailAddressOrUID
result = normalizeEmailAddressOrUID('admin')
assert result == 'admin@example.com'
class TestFormatting:
"""Test formatting helper functions."""
def test_format_milliseconds(self):
from gam.util.args import formatMilliSeconds
assert formatMilliSeconds(0) == '00:00:00'
assert formatMilliSeconds(1000) == '00:00:01'
assert formatMilliSeconds(61000) == '00:01:01'
assert formatMilliSeconds(3661000) == '01:01:01'
def test_format_http_error(self):
from gam.util.args import formatHTTPError
result = formatHTTPError(404, 'Not Found', 'Resource missing')
assert result == '404: Not Found - Resource missing'
def test_format_max_message_bytes_raw(self):
from gam.util.args import formatMaxMessageBytes
assert formatMaxMessageBytes(500, 1024, 1048576) == 500
def test_format_max_message_bytes_kilobytes(self):
from gam.util.args import formatMaxMessageBytes
assert formatMaxMessageBytes(2048, 1024, 1048576) == '2K'
def test_format_max_message_bytes_megabytes(self):
from gam.util.args import formatMaxMessageBytes
assert formatMaxMessageBytes(5242880, 1024, 1048576) == '5M'
def test_format_file_size_zero(self):
from gam.util.args import formatFileSize
assert formatFileSize(0) == '0 KB'
def test_format_file_size_small(self):
from gam.util.args import formatFileSize
assert formatFileSize(500) == '1 KB'
class TestTimestamps:
"""Test timestamp formatting."""
def test_iso_format_timestamp(self):
from gam.util.output import ISOformatTimeStamp
ts = arrow.get('2024-01-15T10:30:00+00:00')
result = ISOformatTimeStamp(ts)
assert '2024-01-15' in result
assert '10:30:00' in result
def test_current_iso_format_returns_string(self):
from gam.util.output import currentISOformatTimeStamp
result = currentISOformatTimeStamp()
assert isinstance(result, str)
assert 'T' in result # ISO format contains T separator
class TestDeltaParsing:
"""Test time delta parsing functions."""
def test_get_delta_invalid_returns_none(self):
from gam.util.args import getDelta, DELTA_DATE_PATTERN
result = getDelta('not-a-delta', DELTA_DATE_PATTERN)
assert result is None
def test_get_delta_date(self):
"""Requires GM.DATETIME_NOW to be initialized."""
import arrow as _arrow
from gamlib import glglobals as GM
GM.Globals[GM.DATETIME_NOW] = _arrow.now()
from gam.util.args import getDeltaDate
result = getDeltaDate('-30d')
assert result is not None
def test_get_delta_time(self):
"""Requires GM.DATETIME_NOW to be initialized."""
import arrow as _arrow
from gamlib import glglobals as GM
GM.Globals[GM.DATETIME_NOW] = _arrow.now()
from gam.util.args import getDeltaTime
result = getDeltaTime('-1h')
assert result is not None